SkateSpots/src/i18n/en.ts
SkateSpots b37d627138 Add a slim, dismissible site-links strip to the map
The desktop footer is hidden on full-bleed map routes, leaving the
Newsletter/License/Privacy/Collaborators links (and the "not sponsored by
OpenSats" note) unreachable from the homepage. MapFooterStrip overlays them
inside the map container without stealing map height:

- Desktop: a slim full-width bottom bar.
- Mobile: a tiny collapsed "Links" chip that expands to a compact, scrollable
  row.
- Dismissible (useMapFooterHidden, device-local, same-tab synced) and
  restorable from Settings -> Appearance.
- The map's OSM attribution moved up (md:bottom-7) so the bar doesn't cover it.
2026-10-05 09:15:51 +07:00

6834 lines
319 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.

/**
* English translations
* Note: Skateboarding trick names and terminology stay in English
*/
import { batch3En } from './batch3';
import { batch4En } from './batch4';
const en = {
// Common
common: {
loading: 'Loading...',
error: 'Error',
save: 'Save',
cancel: 'Cancel',
delete: 'Delete',
edit: 'Edit',
add: 'Add',
remove: 'Remove',
create: 'Create',
submit: 'Submit',
close: 'Close',
back: 'Back',
next: 'Next',
done: 'Done',
search: 'Search',
filter: 'Filter',
sort: 'Sort',
share: 'Share',
copy: 'Copy',
copied: 'Copied!',
viewAll: 'View All',
seeMore: 'See More',
showMore: 'Show More',
showLess: 'Show Less',
noResults: 'No results found',
tryAgain: 'Try Again',
learnMore: 'Learn More',
getStarted: 'Get Started',
yes: 'Yes',
no: 'No',
or: 'or',
and: 'and',
free: 'Free',
new: 'New',
popular: 'Popular',
featured: 'Featured',
verified: 'Verified',
sats: 'sats',
clear: 'Clear',
clearFilters: 'Clear Filters',
clearAll: 'Clear All',
},
// Navigation
nav: {
home: 'Home',
// Kept to one word so the mobile tab bar labels stay on a single line.
map: 'Spots',
lab: 'The Lab',
events: 'Events',
crews: 'Crews',
wallet: 'Wallet',
settings: 'Settings',
more: 'More',
rewards: 'Rewards',
stats: 'Community Stats',
about: 'About',
food: 'Food',
marketplace: 'Marketplace',
mints: 'Mints',
appShop: 'App Shop',
trades: 'Marketplace',
stash: 'Stash',
collections: 'Collections',
contacts: 'Contacts',
messages: 'Messages',
skaters: 'Skaters',
glossary: 'Glossary',
riderCard: 'Rider Card',
supporters: 'Supporters ⚡',
newsletter: 'Newsletter',
viewProfile: 'View Profile',
leaderboards: 'Leaderboards',
},
// Authentication
auth: {
login: 'Log in',
logout: 'Log out',
signUp: 'Sign Up',
signUpTitle: 'New to Nostr?',
signUpDesc: "Create a new account to get started. It's free and open.",
orLogIn: 'Or log in',
loginWithExtension: 'Login with Extension',
loginWithAmber: 'Login with Amber (Android)',
loginWithKey: 'Secret Key (nsec) or Recovery Phrase',
loginWithBunker: 'Login with Nostr Connect',
bunkerUri: 'Bunker Connection String',
openAmber: 'Open Amber',
connectToBunker: 'Connect to Bunker',
noExtension: 'No extension detected.',
extensionHelp: 'Try using the Sign Up button above to create a new account, or use the Amber, Key or Bunker tabs if you have existing credentials.',
amberDesc: 'Amber is a secure Nostr signer app for Android devices. Click the button below to open Amber and authorize access.',
bunkerDesc: 'Access your account from any device using a remote signer (bunker). Your keys stay safe on the server, never exposed to websites.',
keyPlaceholder: 'nsec1... or your 12-word recovery phrase',
keyHint: 'You can paste either your nsec key or 12-word recovery phrase',
uploadKeyFile: 'Upload Your Key File',
loggingIn: 'Logging in...',
connecting: 'Connecting...',
verifying: 'Verifying...',
},
// Backup Reminder
backup: {
title: 'Save your login',
subtitle: 'Takes 10 seconds — do it once',
message: 'Your recovery code is the only way back into your account. Save it and you can log in on any device.',
warning: "Lose it and there's no reset — no email, no password recovery.",
backupNow: 'Save it now',
confirmed: "I've saved it",
remindLater: 'Later',
},
// PWA Install
pwa: {
installTitle: 'Install SkateSpots',
installSubtitle: 'Get the full app experience',
addToHomeScreen: 'Add to Home Screen',
installApp: 'Install App',
maybeLater: 'Maybe later',
gotIt: 'Got it!',
benefits: {
fast: 'Lightning Fast',
offline: 'Works Offline',
fullAccess: 'Full Access',
},
iosStep1: 'Tap the Share button',
iosStep1Hint: 'at the bottom of Safari',
iosStep2: 'Scroll and tap "Add to Home Screen"',
iosStep2Hint: 'in the share menu',
iosStep3: 'Tap "Add" to install',
iosStep3Hint: 'The app will appear on your home screen',
androidStep1: 'Tap the menu button',
androidStep1Hint: '(three dots) in your browser',
androidStep2: 'Select "Add to Home Screen"',
androidStep2Hint: 'or "Install App"',
androidStep3: 'Confirm to install',
androidStep3Hint: 'The app will appear on your home screen',
tagline: 'Free • Works offline • Fast & reliable',
},
// Home/Index Page
home: {
tagline: 'Community-Powered • Relay-Based',
title: 'SKATE SPOTS',
subtitle: 'Discover Spots • Meet Up • Learn Tricks • Support Locals',
description: 'Real skateboarding happens offline - this tool helps you find it.',
betaWarning: 'BETA - Under Construction • Use at Your Own Risk',
pwaHint: 'Progressive Web App - Install on Your Device',
exploreMap: 'Explore Map',
addSpot: 'Add Spot',
globalSpots: 'Global Spots',
openCommunity: 'Open Community',
lightningZaps: 'Lightning Zaps',
featuredSpots: 'Featured Spots',
featuredSpotsDesc: 'Community-verified skate spots from around the world',
upcomingEvents: 'Upcoming Events',
upcomingEventsDesc: 'Contests, meetups, and sessions happening soon',
noSpotsYet: 'No spots found yet',
noSpotsHint: 'Be the first to add a skate spot!',
noEventsYet: 'No upcoming events',
noEventsHint: 'Check back later or create one!',
theLab: 'The Lab',
theLabDesc: 'Learn tricks, find coaches, and level up your skating',
coaches: 'Coaches',
coachesDesc: 'Learn from experienced skaters who can help you progress',
filmers: 'Filmers',
filmersDesc: 'Connect with filmers to capture your best clips',
findCoach: 'Find a Coach',
becomeCoach: 'Become a Coach',
findFilmer: 'Find a Filmer',
becomeFilmer: 'Become a Filmer',
viewAllSpots: 'View All Spots',
viewAllEvents: 'View All Events',
createEvent: 'Create Event',
readyToShare: 'Ready to Share?',
readyToShareDesc: 'Help the skateboarding community grow by adding your favorite spots. Every location you share helps fellow skaters discover new places to ride.',
},
// Map Page
mapPage: {
title: 'Shops & Spots',
spots: 'Spots',
shops: 'Shops',
filters: 'Filters',
listView: 'List',
mapView: 'Map',
nearMe: 'Near Me',
searchPlaceholder: 'Search spots...',
noSpotsInArea: 'No spots in this area',
zoomIn: 'Zoom In',
zoomOut: 'Zoom Out',
myLocation: 'My Location',
locationsFound: '{{count}} locations found',
spotsOnly: 'Spots only',
shopsOnly: 'Shops only',
spotsAndShops: 'Spots & Shops',
noSpotsMatch: 'No spots match your filters.',
noSpotsYet: 'No spots have been added yet.',
addFirstSpot: 'Add First Spot',
},
// Spots
spots: {
addSpot: 'Add Spot',
editSpot: 'Edit Spot',
spotDetails: 'Spot Details',
spotName: 'Spot Name',
spotType: 'Spot Type',
description: 'Description',
location: 'Location',
address: 'Address',
photos: 'Photos',
addPhotos: 'Add Photos',
obstacles: 'Obstacles',
features: 'Features',
bustLevel: 'Bust Level',
bestTime: 'Best Time',
tips: 'Tips',
reviews: 'Reviews',
addReview: 'Add Review',
reportSpot: 'Report Spot',
directions: 'Directions',
shareSpot: 'Share Spot',
createdBy: 'Created by',
lastUpdated: 'Last updated',
// Spot types (keep some English as they're skate terms)
types: {
park: 'Skatepark',
street: 'Street Spot',
diy: 'DIY Spot',
plaza: 'Plaza',
indoor: 'Indoor',
bowl: 'Bowl',
vert: 'Vert',
miniramp: 'Mini Ramp',
},
// Bust levels
bustLevels: {
chill: 'Chill',
moderate: 'Moderate',
sketchy: 'Sketchy',
heavy: 'Heavy',
},
},
// Events
events: {
title: 'Skate Events',
createEvent: 'Create Event',
editEvent: 'Edit Event',
eventDetails: 'Event Details',
eventName: 'Event Name',
eventType: 'Event Type',
date: 'Date',
time: 'Time',
startDate: 'Start Date',
endDate: 'End Date',
venue: 'Venue',
organizer: 'Organizer',
attendees: 'Attendees',
going: 'Going',
interested: 'Interested',
rsvp: 'RSVP',
ticketPrice: 'Ticket Price',
freeEntry: 'Free Entry',
register: 'Register',
moreInfo: 'More Info',
upcomingEvents: 'Upcoming',
pastEvents: 'Past',
noEvents: 'No events found',
findEvents: 'Find contests, demos, sessions, and more',
searchPlaceholder: 'Search events...',
loginToCreate: 'Login to create events',
noEventsMatch: 'No upcoming events match your filters.',
noUpcoming: 'No upcoming events found.',
noPast: 'No past events found.',
// Event types
types: {
contest: 'Contest',
session: 'Session',
meetup: 'Meetup',
premiere: 'Video Premiere',
workshop: 'Workshop',
demo: 'Demo',
jam: 'Jam',
},
},
// Shops
shops: {
title: 'Skate Shops',
addShop: 'Add Shop',
editShop: 'Edit Shop',
shopDetails: 'Shop Details',
shopName: 'Shop Name',
hours: 'Hours',
phone: 'Phone',
website: 'Website',
services: 'Services',
brands: 'Brands',
supportLocal: 'Support Local',
nip05: 'Shop NIP-05',
nip05Hint: 'An optional handle you control for this shop (e.g. shop@yourdomain.com), verified against your key. Add up to 5.',
},
// Wallet
wallet: {
title: 'Wallet',
balance: 'Balance',
send: 'Send',
receive: 'Receive',
transactions: 'Transactions',
history: 'History',
noTransactions: 'No transactions yet',
sendSats: 'Send Sats',
receiveSats: 'Receive Sats',
amount: 'Amount',
recipient: 'Recipient',
lightning: 'Lightning',
onChain: 'On-chain',
invoice: 'Invoice',
address: 'Address',
scanQR: 'Scan QR',
paste: 'Paste',
payInvoice: 'Pay Invoice',
createInvoice: 'Create Invoice',
copied: 'Copied to clipboard!',
insufficientFunds: 'Insufficient funds',
paymentSent: 'Payment sent!',
paymentReceived: 'Payment received!',
cashu: {
title: 'Cashu Wallet',
tokens: 'Tokens',
mint: 'Mint',
mints: 'Mints',
manageMints: 'Manage Mints',
addMint: 'Add Mint',
removeMint: 'Remove Mint',
defaultMint: 'Default Mint',
backup: 'Backup',
restore: 'Restore',
recoveryPhrase: 'Recovery Phrase',
viewRecoveryPhrase: 'View Recovery Phrase',
seedWarning: 'This is like your Nostr nsec - keep it secret and backed up!',
},
},
// Settings
settings: {
title: 'Settings',
profile: 'Profile',
editProfile: 'Edit Profile',
account: 'Account',
security: 'Security',
relays: 'Relays',
appearance: 'Appearance',
language: 'Language',
notifications: 'Notifications',
privacy: 'Privacy',
about: 'About',
help: 'Help',
feedback: 'Feedback',
version: 'Version',
// Profile fields
displayName: 'Display Name',
username: 'Username',
bio: 'Bio',
website: 'Website',
lightningAddress: 'Lightning Address',
profilePicture: 'Profile Picture',
banner: 'Banner',
saveProfile: 'Save Profile',
// Security
backupKeys: 'Backup Keys',
exportKeys: 'Export Keys',
secretKey: 'Secret Key',
publicKey: 'Public Key',
showSecretKey: 'Show Secret Key',
hideSecretKey: 'Hide Secret Key',
copyKey: 'Copy Key',
keysBackedUp: "You've confirmed that your keys are safely backed up.",
keysNotBackedUp: 'Make sure to backup your recovery phrase or secret key!',
// Language
selectLanguage: 'Select Language',
english: 'English',
spanish: 'Spanish',
},
// Lab / Coaches
lab: {
title: 'The Lab',
subtitle: 'Level up your skating',
coaches: 'Coaches',
filmers: 'Filmers',
findCoach: 'Find a Coach',
becomeCoach: 'Become a Coach',
findFilmer: 'Find a Filmer',
becomeFilmer: 'Become a Filmer',
hourlyRate: 'Hourly Rate',
experience: 'Experience',
specialties: 'Specialties',
availability: 'Availability',
bookSession: 'Book a Session',
contactCoach: 'Contact Coach',
noCoaches: 'No coaches found in your area',
noFilmers: 'No filmers found in your area',
},
// Rewards
rewards: {
title: 'Rewards',
yourPoints: 'Your Points',
earnPoints: 'Earn Points',
redeemPoints: 'Redeem Points',
leaderboard: 'Leaderboard',
achievements: 'Achievements',
dailyTasks: 'Daily Tasks',
weeklyTasks: 'Weekly Tasks',
},
// Zaps
zaps: {
zapButton: 'Zap',
sendZap: 'Send Zap',
zapAmount: 'Zap Amount',
customAmount: 'Custom Amount',
addMessage: 'Add a message (optional)',
sending: 'Sending...',
sent: 'Zap sent!',
failed: 'Zap failed',
received: 'You received a zap!',
},
// Errors
errors: {
somethingWentWrong: 'Something went wrong',
tryAgainLater: 'Please try again later',
notFound: 'Not found',
pageNotFound: 'Page not found',
unauthorized: 'Unauthorized',
loginRequired: 'Please log in to continue',
networkError: 'Network error',
checkConnection: 'Please check your internet connection',
invalidInput: 'Invalid input',
requiredField: 'This field is required',
},
// Time/Date
time: {
today: 'Today',
yesterday: 'Yesterday',
tomorrow: 'Tomorrow',
thisWeek: 'This week',
nextWeek: 'Next week',
thisMonth: 'This month',
justNow: 'Just now',
minutesAgo: '{{count}} minutes ago',
hoursAgo: '{{count}} hours ago',
daysAgo: '{{count}} days ago',
},
// Footer
footer: {
vibedWith: 'Vibed with',
poweredBy: 'Powered by',
openSource: 'Open Source',
contribute: 'Contribute',
decentralized: 'Decentralized',
communityOwned: 'Community Owned',
},
// Create Spot Page
createSpot: {
title: 'Add a Skate Spot',
subtitle: 'Share a skateboarding spot with the community',
backToMap: 'Back to Map',
namePlaceholder: 'e.g., Hollywood High School',
descriptionPlaceholder: 'Describe the spot, obstacles, best times to skate, security info, etc.',
addressPlaceholder: 'e.g., 1521 N Highland Ave, Los Angeles, CA',
selectType: 'Select spot type',
selectBustLevel: 'Select bust level',
uploadPhotos: 'Upload',
dragDrop: 'Drag and drop or click to upload',
maxPhotos: 'Max 5 photos',
publishing: 'Publishing...',
success: 'Spot published successfully!',
error: 'Failed to publish spot',
locationRequired: 'Please select a location on the map',
useCurrentLocation: 'Use my current location',
pickOnMap: 'Pick Location on Map',
loginToAdd: 'You need to log in to add a skate spot.',
requiredFields: 'Tell us about this spot. All fields marked with * are required.',
coordinates: 'Coordinates',
latitudePlaceholder: 'Latitude (e.g., 34.1012)',
longitudePlaceholder: 'Longitude (e.g., -118.3389)',
gettingLocation: 'Getting location...',
accessType: 'Access Type',
publicTitle: 'Public - Open to Everyone',
publicDesc: 'Skate parks, public plazas, street spots',
privateTitle: 'Private Property - Permission Needed',
privateDesc: 'Backyard ramps, private land, gated spots',
localsTitle: 'Ask Locals First',
localsDesc: "Respect the spot, don't blow it out",
contactInfo: 'Contact Information',
contactPlaceholder: 'e.g., Phone number, Instagram, email...',
contactHintPrivate: 'How should people contact you for permission?',
contactHintLocals: 'How should people reach out to ask about the spot?',
},
// Create Event Page
createEvent: {
title: 'Create an Event',
subtitle: 'Organize a skateboarding event for the community',
backToEvents: 'Back to Events',
namePlaceholder: 'e.g., Summer Skate Session',
descriptionPlaceholder: 'Describe the event, what to expect, rules, prizes, etc.',
selectType: 'Select event type',
venuePlaceholder: 'e.g., Venice Beach Skatepark, CA',
selectDate: 'Select date',
selectTime: 'Select time',
ticketPricePlaceholder: 'Leave empty for free events',
publishing: 'Publishing...',
success: 'Event published successfully!',
error: 'Failed to publish event',
loginToCreate: 'You need to log in to create an event.',
requiredFields: 'Tell us about this event. All fields marked with * are required.',
startDateTime: 'Start Date & Time',
endDateTime: 'End Date & Time (optional)',
coordinatesOptional: 'Coordinates (optional)',
coverImage: 'Cover Image',
uploadCover: 'Upload cover image',
nip05Recommended: 'NIP-05 Verification Recommended',
nip05Desc: 'Add a NIP-05 identifier to your profile to verify your identity and build trust with attendees.',
addNip05: 'Add NIP-05 in Settings',
},
// Spot Detail Page
spotDetail: {
postedBy: 'Posted by',
getDirections: 'Get Directions',
reviews: 'Reviews',
noReviews: 'No reviews yet',
beFirstReview: 'Be the first to review this spot!',
writeReview: 'Write a Review',
submitReview: 'Submit Review',
rating: 'Rating',
reviewPlaceholder: 'Share your experience at this spot...',
relatedSpots: 'Nearby Spots',
},
// Event Detail Page
eventDetail: {
organizedBy: 'Organized by',
eventInfo: 'Event Info',
location: 'Location',
dateTime: 'Date & Time',
price: 'Price',
attendeeCount: '{{count}} going',
interested: 'Interested',
going: 'Going',
notGoing: 'Not Going',
share: 'Share Event',
},
// Profile Page
profile: {
posts: 'Posts',
spots: 'Spots',
events: 'Events',
following: 'Following',
followers: 'Followers',
editProfile: 'Edit Profile',
follow: 'Follow',
unfollow: 'Unfollow',
noPostsYet: 'No posts yet',
noBio: 'No bio yet',
zapProfile: 'Zap this skater',
},
// About Page
about: {
title: 'About SkateSpots',
subtitle: 'A decentralized skateboarding community',
whatIs: 'What is SkateSpots?',
whatIsDesc: 'SkateSpots is a community-powered platform for discovering skateboarding locations worldwide. Built on Nostr, it gives skaters true ownership of their data.',
features: 'Features',
featuresDesc: 'Find spots, create events, connect with coaches, and support your local skate shops—all in one place.',
nostr: 'Built on Nostr',
nostrDesc: 'Nostr is a simple, open protocol that enables truly censorship-resistant global communication. Your account and data belong to you.',
openSource: 'Open Source',
openSourceDesc: 'SkateSpots is completely open source. Contribute, fork, or run your own instance.',
lightning: 'Lightning Payments',
lightningDesc: 'Zap your favorite skaters, coaches, and content creators with Bitcoin Lightning payments.',
},
// FAQ Page
faq: {
title: 'Frequently Asked Questions',
subtitle: 'Everything you need to know about SkateSpots',
whatIsNostr: 'What is Nostr?',
whatIsNostrAnswer: 'Nostr is an open protocol for decentralized social networking. Unlike traditional platforms, you own your data and identity.',
howToStart: 'How do I get started?',
howToStartAnswer: 'Click "Sign Up" to create a new Nostr account, or "Log In" if you already have one. You can use a browser extension, mobile app, or secret key.',
whatAreZaps: 'What are Zaps?',
whatAreZapsAnswer: 'Zaps are Bitcoin Lightning tips you can send to other users. They\'re a way to show appreciation for great content or support creators.',
howToAddSpot: 'How do I add a spot?',
howToAddSpotAnswer: 'Log in to your account, click "Add Spot" and fill in the details including location, photos, and description.',
isItFree: 'Is SkateSpots free?',
isItFreeAnswer: 'Yes! SkateSpots is completely free to use. You can optionally send Zaps (Bitcoin tips) to other users.',
canIEditSpot: 'Can I edit or delete my spots?',
canIEditSpotAnswer: 'Yes, you can edit spots you\'ve created. Due to the decentralized nature of Nostr, deletion requests are sent but some relays may retain data.',
},
// Onboarding
onboarding: {
welcome: 'Welcome to SkateSpots!',
welcomeDesc: 'Let\'s get you set up in just a few steps.',
step1Title: 'Create Your Account',
step1Desc: 'Your Nostr keys are being generated. This is like a password that you control.',
step2Title: 'Backup Your Keys',
step2Desc: 'Save your recovery phrase somewhere safe. This is the only way to recover your account!',
step3Title: 'Set Up Your Profile',
step3Desc: 'Tell the community about yourself.',
step4Title: 'You\'re Ready!',
step4Desc: 'Start exploring spots, events, and connecting with skaters.',
skipForNow: 'Skip for now',
continueToApp: 'Continue to App',
},
// Lab Page
labPage: {
title: 'The Lab',
subtitle: 'Learn tricks. Capture moments. Connect with other Skaters',
coachesTab: 'Coaching',
filmersTab: 'Filming',
offerCoaching: 'OFFER COACHING',
offerFilming: 'OFFER FILMING',
loginToOffer: 'Login to offer services',
whatToLearn: 'WHAT DO YOU WANT TO LEARN?',
whatContent: 'WHAT TYPE OF CONTENT?',
selectSkillsDesc: "Select skills and we'll match you with coaches",
selectFilmingDesc: "Select filming services you're looking for",
searchPlaceholder: 'Search by name, location, or skills...',
noServicesYet: 'NO SERVICES YET',
beFirstToOffer: 'Be the first to offer coaching or filming services to the community!',
skills: 'Skills',
rate: 'Rate',
perHour: '/hour',
contact: 'Contact',
viewProfile: 'View Profile',
noProfilesYet: 'No profiles yet',
beFirstCoach: 'Be the first to register as a coach!',
beFirstFilmer: 'Be the first to register as a filmer!',
yearsExperience: 'years experience',
},
// Become Coach/Filmer Forms
becomeCoach: {
title: 'Become a Coach',
subtitle: 'Share your skills and help other skaters progress',
bioPlaceholder: 'Tell us about your skating background and teaching style...',
skillsPlaceholder: 'e.g., kickflip, heelflip, grinds, bowls',
selectSkills: 'Select skills you can teach',
hourlyRate: 'Hourly Rate (sats)',
ratePlaceholder: 'e.g., 5000',
locationPlaceholder: 'Your city or area',
publishing: 'Publishing profile...',
success: 'Coach profile created!',
error: 'Failed to create profile',
},
becomeFilmer: {
title: 'Become a Filmer',
subtitle: 'Capture the best clips for the community',
bioPlaceholder: 'Tell us about your filming experience and equipment...',
specialtiesPlaceholder: 'e.g., fisheye, long lens, drone, editing',
selectSpecialties: 'Select your specialties',
hourlyRate: 'Hourly Rate (sats)',
ratePlaceholder: 'e.g., 5000',
locationPlaceholder: 'Your city or area',
portfolioUrl: 'Portfolio URL',
portfolioPlaceholder: 'Link to your work (YouTube, Instagram, etc.)',
publishing: 'Publishing profile...',
success: 'Filmer profile created!',
error: 'Failed to create profile',
},
// Skills (keep in English as skate terminology)
skills: {
kickflip: 'Kickflip',
heelflip: 'Heelflip',
ollie: 'Ollie',
treflip: '360 Flip',
shuvit: 'Shuvit',
hardflip: 'Hardflip',
varial: 'Varial',
nollie: 'Nollie',
fakie: 'Fakie',
switch: 'Switch',
grind: 'Grind',
slide: 'Slide',
manual: 'Manual',
transition: 'Transition',
bowl: 'Bowl',
vert: 'Vert',
street: 'Street',
flatground: 'Flatground',
},
// Food Spots
food: {
title: 'Food Spots',
subtitle: 'Fuel up before or after your session',
addFood: 'Add Food Spot',
categories: {
all: 'All',
burgers: 'Burgers',
pizza: 'Pizza',
tacos: 'Tacos',
coffee: 'Coffee',
drinks: 'Drinks',
snacks: 'Snacks',
},
skaterFriendly: 'Skater Friendly',
nearSpots: 'Near Skate Spots',
},
// Marketplace
marketplace: {
title: 'Marketplace',
subtitle: 'Buy, sell, and trade skate gear',
addListing: 'Add Listing',
condition: 'Condition',
conditions: {
new: 'New',
likeNew: 'Like New',
good: 'Good',
fair: 'Fair',
poor: 'Poor',
},
categories: {
all: 'All',
decks: 'Decks',
trucks: 'Trucks',
wheels: 'Wheels',
bearings: 'Bearings',
shoes: 'Shoes',
clothing: 'Clothing',
protection: 'Protection',
other: 'Other',
},
priceRange: 'Price Range',
location: 'Location',
seller: 'Seller',
contact: 'Contact Seller',
makeOffer: 'Make Offer',
sold: 'Sold',
available: 'Available',
gear: 'Gear',
sellGear: 'Sell Gear',
wantToBuy: 'Want to Buy',
},
// Weather
weather: {
title: 'Weather',
current: 'Current Conditions',
forecast: 'Forecast',
humidity: 'Humidity',
wind: 'Wind',
feelsLike: 'Feels like',
goodForSkating: 'Good for skating!',
notIdeal: 'Not ideal conditions',
allWeather: 'All Weather',
goodWeather: 'Good Weather',
perfectWeather: 'Perfect Weather',
},
mapUi: {
mapFooter: {
links: 'Links',
hide: 'Hide map links bar',
},
// Map chrome
findingSpots: 'Finding spots',
loadingSpots: 'Loading spots',
loadingMap: 'Loading map...',
mapViewAria: 'Map view',
listViewAria: 'List view',
layers: 'Layers',
mapLayers: 'Map layers',
showOnMap: 'Show on Map',
showResults: 'Show Results',
quickActions: 'Quick Actions',
spots: 'Spots',
shops: 'Shops',
gear: 'Gear',
tours: 'Tours',
events: 'Events',
crews: 'Crews',
spotType: 'Spot Type',
sortBy: 'Sort By',
trustLevel: 'Trust Level',
weather: 'Weather',
buySellGear: 'Buy/Sell Gear',
stash: 'Stash',
more: 'More',
// Welcome banner
newHere: 'New here?',
welcomeDesc: 'Discover skate spots, events & the community.',
getOnZapstore: 'Get on Zapstore',
dismiss: 'Dismiss',
// Spot sheet / popups
addedBy: 'Added by',
directions: 'Directions',
viewDetails: 'View Details',
viewDetailsArrow: 'View Details →',
spotDetails: 'Spot details',
skateShop: 'Skate Shop',
skateSpot: 'Skate Spot',
youAreHere: 'You are here',
conditions: 'Conditions',
weatherScoreSuffix: '{{score}}/100 weather',
wind: 'Wind',
humidity: 'Humidity',
airQuality: 'Air quality',
threeDayForecast: '3-Day Forecast',
gearForSale: 'Gear for sale ({{count}})',
viewItems: 'View Items →',
viewAllItems: 'View All {{count}} Items →',
viewGear: 'View Gear →',
localTour: 'Local Tour',
meetAt: 'Meet at: {{place}}',
hours: '{{count}} hours',
level: '{{level}} level',
viewTour: 'View Tour →',
upcomingEvent: 'Upcoming Event',
viewEvent: 'View Event →',
skateCrew: 'Skate Crew',
members: '{{count}} members',
since: 'Since {{year}}',
viewCrew: 'View Crew →',
acceptsBitcoin: 'Accepts Bitcoin',
boosted: 'Boosted',
website: 'Website',
// Offline / empty states
offlineSavedSpots: 'Offline · showing saved spots',
cantReachRelays: "Can't reach the relays",
relayOnlineNoAnswer: "You're online, but the community relay isn't answering right now.",
youreOffline: "You're offline.",
savedOnDevice: "Spots you've already seen are saved on this device and will show here.",
noSpotsMatchTitle: 'No spots match',
noSpotsMatchDesc: 'Try a different search or clear your filters.',
noSpotsMappedYet: 'No spots mapped yet',
noSpotsMappedDesc: "Be the first to map your park — it takes a minute, it's yours, and the next skater who rolls through will thank you.",
addFirstSpot: 'Add the first spot',
findSpotsNearMe: 'Find spots near me',
locating: 'Locating…',
// Near Me toasts
refiningLocation: 'Refining your location…',
refiningLocationDesc: 'Centered on your last spot — dialing in a fresh GPS fix.',
locatingYou: 'Locating you…',
locatingDesc: 'Getting a GPS fix. This can take up to a minute the first time.',
foundYou: 'Found you',
foundYouDesc: 'Centered the map on your location.',
locationPermissionNeeded: 'Location permission needed',
permissionNeededDesc: 'Allow location for SkateSpots in your device Settings → Apps → SkateSpots → Permissions, then tap Near Me again.',
locationTimedOut: 'Location timed out',
timeoutNativeDesc: "Couldn't get a location fix in time. If you're indoors, GPS can't see satellites — enable Network Location (Settings → Location) or step outside, then try again.",
timeoutWebDesc: 'Could not get your location in time. Check your signal and try again.',
noLocationProvider: 'No location provider',
noProviderNativeDesc: "Your phone isn't providing a location. On privacy-focused ROMs (e.g. GrapheneOS) enable Network Location under Settings → Location, or make sure GPS is on and try outdoors.",
noProviderWebDesc: 'No location provider is available. Enable location services and try again.',
locationUnavailable: 'Location unavailable',
unavailableNativeDesc: 'Turn on Location/GPS in your phone settings, allow location for SkateSpots (Settings → Apps → SkateSpots → Permissions), then try again.',
unavailableWebDesc: 'Your location could not be determined. Make sure location services are enabled.',
// Location help dialog
locationBlocked: 'Location Blocked',
locationBlockedDesc: "Your browser has blocked location access for this site. Here's how to re-enable it:",
locationPlatformAndroid: 'Android Chrome',
locationPlatformIos: 'iOS Safari',
locationPlatformDesktop: 'Desktop Chrome',
locationPlatformFirefox: 'Firefox',
locationStepsAndroid: 'Tap the lock icon in the address bar → Site settings → Location → Allow',
locationStepsIos: 'Settings app → Safari → Location → Allow (or tap the "AA" button in Safari\'s address bar)',
locationStepsDesktop: 'Click the lock icon → Site settings → Location → Allow',
locationStepsFirefox: 'Click the lock icon → Connection secure → More info → Permissions → Access your location → Allow',
gotIt: 'Got it',
// Add speed-dial / FAB
addSpot: 'Add Spot',
addShop: 'Add Shop',
addEvent: 'Add Event',
sellGear: 'Sell Gear',
offerTour: 'Offer Tour',
closeAddMenu: 'Close add menu',
addMenuAria: 'Add spot, shop, event, gear, or tour',
// Local chat
localChat: 'Local Chat',
localChatDesc: 'Live chat for wherever the map is centered. Pick how wide the channel reaches.',
// BTC Map
btcMapShowing: 'Showing {{count}} places where you can Spedn your sats nearby',
btcMapShow: 'Show places where you can Spedn your sats (via BTC Map)',
// Stash drawer
yourPrivateStash: 'Your Private Stash',
stashIntro: 'Track your gear, set prices, and find trades with other skaters.',
encryptionRequired: 'Encryption Required',
encryptionRequiredDesc: 'Use a signer with NIP-44 support (Alby, nos2x, Amber).',
myStash: 'My Stash',
encrypted: 'Encrypted',
itemsCount: '{{count}} items',
items: 'Items',
wishlist: 'Wishlist',
types: 'Types',
stashEmpty: 'Your stash is empty',
addFirstItem: 'Add First Item',
wishlistCount: 'Wishlist ({{count}})',
myGearCount: 'My Gear ({{count}})',
openFullStash: 'Open Full Stash',
addItem: 'Add Item',
// Spot picker
searchSpotsPlaceholder: 'Search spots...',
selected: 'Selected',
noSpotsFound: 'No spots found. Try a different search.',
addingSpotsHint: 'Adding spots helps users find you when browsing specific locations',
// Spot card
report: 'Report',
// Spot collections
spotSingular: '1 spot',
spotPlural: '{{count}} spots',
view: 'View',
myCollections: 'My Collections',
collectionsSubtitle: 'Organize your favorite spots into personalized lists',
newCollection: 'New Collection',
loginToCreate: 'Log in to create collections',
loginToCreateDesc: 'Save your favorite spots to personalized lists like "Barcelona Trip 2024" or "Best Rails in LA"',
noCollections: 'No collections yet',
noCollectionsDesc: 'Create your first collection to start organizing spots',
createFirstCollection: 'Create Your First Collection',
createNewCollection: 'Create New Collection',
createCollectionDesc: 'Give your collection a name and optional description',
collectionName: 'Collection Name',
collectionNamePlaceholder: 'Barcelona Trip 2024',
descriptionOptional: 'Description (optional)',
descriptionPlaceholder: 'My favorite spots from my Barcelona trip...',
creating: 'Creating...',
createCollection: 'Create Collection',
editCollection: 'Edit Collection',
saving: 'Saving...',
saveChanges: 'Save Changes',
deleteCollectionTitle: 'Delete Collection?',
deleteCollectionDesc: 'This will permanently delete "{{title}}". The spots themselves won\'t be affected.',
deleting: 'Deleting...',
collectionCreated: 'Collection created!',
collectionCreatedDesc: '"{{title}}" is ready for spots',
collectionCreateFailed: 'Failed to create collection',
collectionUpdated: 'Collection updated!',
changesSaved: 'Your changes have been saved',
collectionUpdateFailed: 'Failed to update collection',
collectionDeleted: 'Collection deleted',
collectionDeletedDesc: '"{{title}}" has been removed',
collectionDeleteFailed: 'Failed to delete collection',
somethingWentWrong: 'Something went wrong',
},
spotUi: {
claim: {
areYouOwner: 'Is this your spot?',
privateDesc: 'This spot is marked private. If you own or manage it, claim it — a SkateSpots moderator confirms ownership, then you take over the listing.',
claimSpot: 'Claim this spot',
nip05Required: 'You need a verified NIP-05 handle to claim a private spot (proof you control a Nostr identity). Add one in your profile, then come back.',
addNip05: 'Add NIP-05 in Settings',
pendingTitle: 'Claim pending review',
pendingDesc: 'Your claim has been submitted. A SkateSpots moderator will review it — you can finish claiming the spot once it\'s approved.',
approvedTitle: 'Claim approved',
approvedDesc: 'A moderator approved your claim. Finish to become the owner of this spot.',
complete: 'Finish claiming',
submitted: 'Claim submitted',
submittedDesc: 'A moderator will review your claim. You\'ll take over the spot once it\'s approved.',
submitFailed: 'Could not submit the claim',
submit: 'Submit claim',
submitting: 'Submitting…',
confirmTitle: 'Claim "{{title}}"?',
confirmIntro: 'You are asking to become the owner of this private spot.',
confirmBullet1: 'A SkateSpots moderator reviews the request (we can\'t verify property, only Nostr keys)',
confirmBullet2: 'If approved, you finish the claim to become the owner and can edit the spot',
confirmBullet3: 'The original spot stays on Nostr — the network is decentralized',
adminTitle: 'Pending ownership claims',
adminApprove: 'Approve',
adminApproved: 'Approved',
adminApprovedDesc: 'They can now finish claiming the spot.',
adminApproveFailed: 'Could not approve the claim',
loginRequired: 'Log in to claim this private spot, or to request ownership.',
},
abandonedDesc: 'Last updated {{time}}. If the original owner is inactive, you can claim this spot to maintain and improve it.',
claimedNotice: 'Claimed listing',
claimedNoticeDesc: 'This spot was re-published by a new owner after a claim. The original listing may still exist separately.',
abandonedLoginDesc: 'Last updated {{time}}. Log in to claim and maintain this spot.',
accessDeniedDesc: 'You can only edit spots that you created.',
accessDeniedTitle: 'Access Denied',
addAtLeastOneTag: 'Please add at least one tag.',
additionalDetails: 'Additional details (optional)',
additionalDetailsPlaceholder: 'Provide any additional context that might help...',
addNewTags: 'Add new tags:',
addNip05InSettings: 'Add NIP-05 in Settings',
addPhoto: 'Add Photo',
addPhotoDesc: 'Contribute a photo to help others discover this spot.',
addPhotoOrDescription: 'Add a photo or a description',
addPhotoOrDescriptionDesc: 'Give skaters something to go on — a picture, or a line about the place.',
addPhotos: 'Add Photos',
addPhotoToSpot: 'Add Photo to Spot',
adminDeleteDialogDesc: 'As a SkateSpots admin you\'re taking down "{{title}}". It will be hidden across the app for everyone and a deletion request will be sent to relays.',
adminDeleteReason: 'SkateSpots admin removed spot "{{title}}"',
airQuality: 'Air Quality',
altSkateSpot: 'Skate spot: {{title}}',
areYouShopOwner: 'Are You the Shop Owner?',
askBeforeSkating: 'Ask before skating',
backToSpot: 'Back to Spot',
beFirstToListGear: 'Be the first to list gear at this spot!',
beFirstToSay: 'Be the first to say something.',
byAuthor: 'by {{name}}',
captionPlaceholder: 'Add a caption (optional) - describe what\'s in the photo, when it was taken, etc.',
chatPublicNote: 'Anyone can read this — messages are public & ephemeral. For private chats, use Direct Messages.',
checkPinBelow: 'Check the pin below before adding a duplicate.',
claimBullet1: 'You become the owner and can edit the spot',
claimBullet2: 'The original spot remains (Nostr is decentralized)',
claimBullet3: 'Your version may appear alongside the original',
claimFailed: 'Claim failed',
claiming: 'Claiming...',
claimIntro: 'You\'re about to claim ownership of this spot. This will create your own version of the spot that you can edit and maintain.',
claimSpot: 'Claim Spot',
claimThisSpot: 'Claim This Spot',
claimTitle: 'Claim "{{title}}"?',
claimWhatHappens: 'What happens when you claim:',
coachesAndFilmersAvailable: 'Coaches and filmers available at this spot',
coachesCount: 'Coaches ({{count}})',
coachHere: 'Coach Here',
community: 'Community',
communityVerified: 'Community verified',
communityVerifiedTitle: 'Another skater has contributed here (photo or status report).',
conditions: 'Conditions',
contactForPermission: 'Contact for permission',
contactInfo: 'Contact Info',
contributePhoto: 'Contribute Photo',
contributing: 'Contributing...',
couldNotSendMessage: 'Could not send message.',
couldNotSubmit: 'Could not submit',
couldNotUploadPhoto: 'Could not upload the photo. You can add one below.',
crewsThatSkateHere: 'Crews that skate here',
current: 'Current:',
currentTags: 'Current tags on this spot:',
daysAgo: '{{count}} day ago',
daysAgoPlural: '{{count}} days ago',
deleteDialogDesc: 'This will permanently delete "{{title}}" from all relays. This action cannot be undone.',
deletedSpotReason: 'Deleted spot',
deleteFailed: 'Delete failed',
deleteSpot: 'Delete Spot',
deleteSpotTitle: 'Delete Spot?',
differentSpotPublishAnyway: 'It\'s a different spot — publish anyway',
dragDropOrClick: 'Drag & drop or click',
dropPhotos: 'Drop photos',
duplicateOne: 'There is a spot within 60 m of your pin. If it\'s the same place, add your photo or a status report there instead of creating a duplicate.',
duplicateOther: 'There are spots within 60 m of your pin. If it\'s the same place, add your photo or a status report there instead of creating a duplicate.',
editSpotSeoDesc: 'Edit your skateboarding spot.',
failedToContribute: 'Failed to contribute',
failedToPublish: 'Failed to publish',
failedToPublishDesc: 'Something went wrong. Check console for details.',
failedToSubmitReport: 'Failed to submit report',
failedToSuggestTags: 'Failed to suggest tags',
failedToUpdate: 'Failed to update',
failedToUploadImage: 'Failed to upload image',
failedToUploadPhoto: 'Failed to upload photo',
feelsLikeTemp: 'Feels like {{temp}}°C',
filmersCount: 'Filmers ({{count}})',
filmHere: 'Film Here',
gearSwap: 'Gear Swap',
geolocationNotSupported: 'Geolocation not supported',
geolocationNotSupportedDesc: 'Your browser does not support geolocation.',
getDirections: 'Get Directions',
hangTight: 'Hang tight',
hangTightDesc: 'Your photo is still uploading. It\'ll be attached in a second.',
helpImproveSpot: 'Help improve this spot by adding photos or suggesting tags',
hiddenByModerator: 'Hidden by a moderator',
hiddenByModeratorDesc: 'This spot is not shown on the map or in search. You can still see it because it\'s yours. Editing it will not un-hide it — it stays hidden until a moderator retracts the verdict.',
humidity: 'Humidity',
imageUploaded: 'Image uploaded',
imageUploadedDesc: 'Your image has been uploaded successfully.',
imHere: 'I\'m Here',
invalidCoordinates: 'Invalid coordinates',
invalidCoordinatesDesc: 'Please enter valid latitude (-90 to 90) and longitude (-180 to 180).',
invalidNpub: 'Invalid npub',
invalidNpubDesc: 'Please enter a valid npub address.',
lastReported: 'last {{time}}',
listGear: 'List Gear',
listYourGearHere: 'List Your Gear Here',
loadingChat: 'Loading chat…',
locationError: 'Location error',
locationFound: 'Location found',
locationFoundDesc: 'Your current location has been set.',
locationRequired: 'Location required',
locationRequiredDesc: 'Please enter coordinates or use your current location.',
loginRequiredTitle: 'Login Required',
logInToChat: 'Log in to join the local chat.',
logInToContributePhotos: 'Log in to contribute photos',
loginToEditSpot: 'You need to log in to edit this spot.',
logInToReport: 'You need to be logged in to report content.',
logInToReportStatus: 'Log in to report this spot\'s status.',
logInToSuggestTags: 'Log in to suggest tags',
manageNip05ToClaim: 'Manage NIP-05 to Claim',
meetAtSpotForPickup: '💡 Meet at this spot for safe pickup',
memberOne: '{{count}} member',
memberOther: '{{count}} members',
messageFailed: 'Message failed',
messageMapper: 'Message Mapper',
messagePlaceholder: 'Message this spot… (try /me or /who)',
messagesHiddenOne: '{{count}} message hidden by your trust filter. Lower the filter to see more.',
messagesHiddenOther: '{{count}} messages hidden by your trust filter. Lower the filter to see more.',
messagesHiddenShortOne: '{{count}} message hidden by your trust filter',
messagesHiddenShortOther: '{{count}} messages hidden by your trust filter',
metresAway: '{{distance}} m away',
moderatorVouchedDesc: 'A moderator reviewed this spot and vouched for it. Nice one.',
monthsAgo: '{{count}} month ago',
monthsAgoPlural: '{{count}} months ago',
newOwnerNpub: 'New Owner\'s npub',
newOwnerNpubHint: 'Enter the npub of the user you want to transfer this spot to.',
nip05RequiredToClaim: '⚠️ NIP-05 verification required to claim shops',
noCommunitySignalTitle: 'No community photo or status report yet — nobody has confirmed this place.',
breakdownPhotos: '{{count}} photo(s)',
breakdownStatus: '{{count}} status report(s)',
breakdownComments: '{{count}} comment(s)',
breakdownReactions: '{{count}} reaction(s)',
breakdownCollections: '{{count}} save(s)',
breakdownZaps: '{{count}} zap(s)',
breakdownTags: '{{count}} tag suggestion(s)',
breakdownCrews: '{{count}} crew(s)',
breakdownLab: '{{count}} pro link(s)',
verifyNudgeTitle: 'Be the first to confirm this spot',
verifyNudgeBody: "A photo or an “I've skated here” is how a spot gets verified — help the next skater out.",
verifyNudgeStatus: "I've skated here",
communityChip: 'Community',
seenChip: 'Seen',
noGearListedYet: 'No gear listed yet',
noMessagesYet: 'No messages yet in this local channel. Say hi — "anyone skating tonight?"',
noPhoto: 'No photo',
noReportsYet: 'No reports yet',
noTags: 'No tags',
notASkateSpot: 'Not a skate spot',
offerServicesHere: '💡 Offer your services at this spot',
photoCaptured: 'Photo captured!',
photoCapturedDesc: 'Your photo has been uploaded successfully.',
photoContributed: 'Photo contributed!',
photoContributedDesc: 'Your photo has been added to this spot.',
photosAdded: 'Your photos have been added.',
photosRecommended: 'strongly recommended — spots without a photo stay marked Unverified',
photosUploaded: '{{count}} photos uploaded',
photoUploaded: 'Photo uploaded',
photoUploadedDesc: 'Add a caption and submit to contribute!',
photoUploadFailed: 'Photo upload failed',
pleaseTryAgain: 'Please try again',
public: 'Public',
publicChatTitle: 'Anyone can read messages in this channel — it is public and unencrypted.',
recentReports: 'Recent reports',
recipientRequired: 'Recipient required',
recipientRequiredDesc: 'Please enter the npub of the new owner.',
removeAdmin: 'Remove (admin)',
removeSpot: 'Remove Spot',
removeSpotAdminTitle: 'Remove this spot? (admin)',
reportOne: 'report',
reportContent: 'Report Content',
reportContentDesc: 'Help keep the community safe by reporting inappropriate content.',
reportedBySkaters: ' by {{count}} skaters — under review. If this is a real spot, add a photo or a status report to confirm it.',
reportedNotASpot: 'Reported as not a skate spot',
reportingType: 'Reporting {{type}}',
reports: 'reports',
reportShop: 'Report Shop',
reportSpot: 'Report Spot',
reportStatus: 'Report Status',
reportSubmitted: 'Report submitted',
reportSubmittedDesc: 'Thank you for helping keep SkateSpots safe.',
reportSubmittedLong: 'Thanks for helping keep SkateSpots safe and accurate. Your report will be reviewed by trusted community members.',
reportSubmittedTitle: 'Report Submitted',
report: {
notASkateSpot: { label: 'Not a skate spot', description: 'A test entry, or a place that is not for skating at all' },
fakeSpot: { label: 'Fake/Non-existent spot', description: 'This spot does not exist or location is wrong' },
stolenPhotos: { label: 'Stolen/Fake photos', description: 'Photos are stock images or stolen from elsewhere' },
spamAd: { label: 'Spam or advertising', description: 'Promotional content or duplicate listings' },
dangerousLocation: { label: 'Dangerous location', description: 'Private property, unsafe, or illegal to access' },
inappropriateContent: { label: 'Inappropriate content', description: 'Contains offensive or adult content' },
impersonation: { label: 'Impersonation', description: 'Someone pretending to be someone else' },
other: { label: 'Other', description: 'Something else not listed above' },
},
targetType: {
spot: 'Spot',
event: 'Event',
tour: 'Tour',
shop: 'Shop',
user: 'User',
listing: 'Listing',
coach: 'Coach',
filmer: 'Filmer',
},
sameOwner: 'Same owner',
sameOwnerDesc: 'Cannot transfer to the current owner.',
saveChanges: 'Save Changes',
saving: 'Saving...',
services: 'Services',
shareSpotText: 'Check out this skate spot: {{title}}',
shelter: 'Shelter',
shelterHint: 'Is this spot exposed to the weather? Helps skaters find dry spots when it rains.',
shopClaimDesc: 'If you own or manage this shop, you can claim it to verify ownership and manage the listing.',
shopClaimNip05Hint: 'To claim this shop, add a NIP-05 identifier that matches the shop\'s website domain.',
skatersActiveDesc: 'People who posted in this channel in the last 24h.',
skatersActiveOne: '{{count}} skater active here',
skatersActiveOther: '{{count}} skaters active here',
skatersSellingGear: 'Skaters at this spot are selling gear',
skateSpot: 'Skate Spot',
somethingWentWrong: 'Something went wrong',
spot: 'Spot',
spotAdded: 'Spot added!',
spotAddedDesc: 'Your skate spot has been published to the network.',
spotAddedNoPhoto: 'Published — and marked Unverified until someone adds a photo. A picture of the spot goes a long way.',
spotChat: 'Spot Chat',
spotClaimed: 'Spot claimed!',
spotClaimedDesc: 'You are now the owner of this spot. You can edit it from your profile.',
spotDeleted: 'Spot deleted',
spotDeletedDesc: 'Deletion request sent to relays. Most relays will remove this content.',
spotMayAlreadyBeOnMap: 'That spot may already be on the map',
spotMayAlreadyExist: 'This spot may already exist',
spotMayBeAbandoned: 'Spot May Be Abandoned',
spotPhotoAlt: 'Spot',
spotRemoved: 'Spot removed',
spotRemovedDesc: 'This spot has been taken down and will be hidden across SkateSpots.',
spotSeoDesc: 'View this skateboarding spot on SkateSpots.',
spotStatus: 'Spot Status',
spotStatusDesc: 'Is "{{title}}" still skateable? Let the community know its current state.',
spotTypeRequired: 'Spot type required',
spotTypeRequiredDesc: 'Please pick a spot type so skaters know what they\'re rolling up to.',
spotUpdated: 'Spot updated!',
spotUpdatedDesc: 'Your changes have been published to the network.',
staffVerifiedTitle: 'A SkateSpots admin reviewed and vouched for this spot.',
statusNotePlaceholder: 'Optional: add details (e.g. "new knobs on the ledge", "guard kicks out after 6pm")',
statusReported: 'Status reported',
statusReportedDesc: 'Thanks for keeping the map fresh. Your report is live.',
submitReport: 'Submit Report',
submitting: 'Submitting...',
suggestedByUser: 'Suggested by {{count}} user',
suggestedByUsers: 'Suggested by {{count}} users',
suggestedTagsContent: 'Suggested tags for {{title}}: {{tags}}',
suggestNTags: 'Suggest {{count}} Tags',
suggestOneTag: 'Suggest 1 Tag',
suggestTags: 'Suggest Tags',
suggestTagsDesc: 'Help categorize this spot by suggesting relevant tags.',
tags: 'Tags',
tagsHint: 'Add tags for features, surfaces, vibes (max {{max}})',
tagsSuggested: 'Tags suggested!',
tagsSuggestedDesc: 'Your tag suggestions have been submitted.',
takePhoto: 'Take Photo',
threeDayForecast: '3-Day Forecast',
tipTheMapper: 'Tip the Mapper',
tipTheMapperDesc: 'Found this spot useful? Send a zap to thank the mapper!',
titleRequired: 'Title required',
titleRequiredDesc: 'Please enter a name for this spot.',
today: 'Today',
tomorrow: 'Tomorrow',
transferDesc: 'Transfer "{{title}}" to another user. This action cannot be undone.',
transferFailed: 'Transfer failed',
transferOwnership: 'Transfer Ownership',
transferRequestContent: 'Ownership transfer request for spot "{{title}}"',
transferRequested: 'Transfer requested',
transferRequestedDesc: 'The new owner will need to claim the spot to complete the transfer.',
transferring: 'Transferring...',
transferSpotOwnership: 'Transfer Spot Ownership',
transferWarningDesc: 'After transfer, you will no longer be able to edit or delete this spot.',
typeToSearchTags: 'Type to search or add tags...',
underReview: ' — under review. If this is a real spot, add a photo or a status report to confirm it.',
untitledSpot: 'Untitled spot',
unverified: 'Unverified',
updateDetailsFor: 'Update the details for {{title}}',
updateSpotDesc: 'Update the information about this spot. All fields marked with * are required.',
uploadAPhoto: 'Upload a photo',
uploadAPhotoHint: 'Drag & drop or click · JPG, PNG, WebP',
uploadedAlt: 'Uploaded',
uploadFailed: 'Upload failed',
uploadingPhoto: 'Uploading photo…',
uploadPhotoFirst: 'Please upload a photo first.',
uvIndex: 'UV Index',
verifiedBySkateSpots: 'Verified by SkateSpots',
viewPhotoFullScreen: 'View photo full screen',
viewPhotoOf: 'View photo {{n}} of {{total}}',
warning: 'Warning',
weatherConditions: 'Weather Conditions',
weatherOnly: 'weather only',
weatherOnlyUnconfirmed: 'Weather only. Nobody has confirmed this is a skate spot yet, so there\'s no conditions score — add a photo or a status report to confirm it.',
whyReporting: 'Why are you reporting this?',
wind: 'Wind',
yearsAgo: '{{count}} year ago',
yearsAgoPlural: '{{count}} years ago',
},
marketUi: {
gearSwap: {
title: 'Gear Swap',
subtitle: 'Buy & sell used skate gear with other skaters',
listGear: 'List Gear',
searchPlaceholder: 'Search gear...',
allCategories: 'All Categories',
forSale: 'For Sale',
wtb: 'WTB',
trust: 'Trust',
filterByTrust: 'Filter by Trust',
allSellers: 'All Sellers',
verifiedOnly: 'Verified Only',
trustedPlus: 'Trusted+',
followingOnly: 'Following Only',
pickupOrShip: 'Pickup or ship',
localPickup: 'Local pickup',
ships: 'Ships',
sortBy: 'Sort by',
newest: 'Newest',
priceLowHigh: 'Price: Low → High',
priceHighLow: 'Price: High → Low',
trustScore: 'Trust Score',
nearest: 'Nearest (local)',
listingFound: '{{count}} listing found',
listingsFound: '{{count}} listings found',
loadFailed: 'Failed to load listings',
tryLater: 'Please try again later.',
noneTitle: 'No listings found',
noneFiltered: 'No gear matches your current filters. Try clearing them or searching something else.',
noneFirst: 'Be the first to list your gear — decks, trucks, wheels, shoes, anything skate!',
listYourGear: 'List Your Gear',
faqLink: 'Have questions? Check the FAQ →',
},
hub: {
browse: 'Browse',
selling: 'Selling',
myTrades: 'My Trades',
reputation: 'Reputation',
loginListings: 'Log in to manage your listings.',
loginReputation: 'Log in to see your reputation and reviews.',
},
marketplace: {
heroTitle: 'Shops & Marketplace',
heroBefore: 'Our marketplace is powered by',
heroAfter: '- a decentralized marketplace on Nostr. Skate shops can list their products, and you can buy/sell used gear directly with Bitcoin.',
whatIsTitle: 'What is Plebeian Market?',
whatIsBody: 'Plebeian Market is a decentralized marketplace built on Nostr. Buy and sell skateboarding gear directly with other skaters using Bitcoin. No middlemen, no fees, total freedom.',
noFeesTitle: 'No Fees',
noFeesBody: 'No platform taking 30%. Direct peer-to-peer sales.',
bitcoinTitle: 'Bitcoin Payments',
bitcoinBody: 'Pay with Bitcoin. Fast, secure, global.',
decentralizedTitle: 'Decentralized',
decentralizedBody: 'Built on Nostr. No company controls it.',
visit: 'Visit Plebeian Market',
buySellTitle: 'What You Can Buy & Sell',
catCompletes: 'Complete Decks',
preBuilt: 'Pre-built completes',
customSetups: 'Custom setups',
beginnerBoards: 'Beginner boards',
catParts: 'Deck Parts',
decks: 'Decks',
trucks: 'Trucks',
wheels: 'Wheels',
bearings: 'Bearings',
gripTape: 'Grip tape',
hardware: 'Hardware',
catApparel: 'Apparel & Protection',
skateShoes: 'Skate shoes',
clothing: 'Clothing',
helmets: 'Helmets',
pads: 'Pads',
backpacks: 'Backpacks',
catAccessories: 'Accessories',
tools: 'Tools',
wax: 'Wax',
rails: 'Rails',
stickers: 'Stickers',
videos: 'Videos',
magazines: 'Magazines',
howItWorks: 'How It Works',
step1Title: 'Browse Products',
step1Body: 'Visit Plebeian Market and browse skateboarding gear from shops and individuals worldwide.',
step2Title: 'Contact Sellers',
step2Body: 'Message sellers directly via Nostr. Negotiate prices, ask questions, arrange shipping.',
step3Title: 'Pay with Bitcoin',
step3Body: 'Complete your purchase using Bitcoin Lightning for instant, low-fee payments.',
step4Title: 'Leave Feedback',
step4Body: 'Rate your experience and build seller reputation on Nostr. Your reviews help the community.',
needBitcoinTitle: 'Need Bitcoin?',
needBitcoinBefore: "You'll need a Lightning wallet with some sats to make purchases. Check out the",
onboardingGuide: 'onboarding guide',
needBitcoinAfter: 'for wallet recommendations.',
ownShopTitle: 'Own a Skate Shop?',
ownShopBody: 'List your shop on SkateSpots and sell your products on Plebeian Market. Reach customers worldwide with zero platform fees.',
addShop: 'Add Your Shop',
startSelling: 'Start Selling',
},
listingDetail: {
notFound: 'Listing not found',
notFoundBody: 'It may have been deleted, or the link is wrong.',
backToGearSwap: 'Back to Gear Swap',
gearSwap: 'Gear Swap',
viewPhoto: 'View photo {{n}}',
photoAlt: '{{title}} — photo {{n}}',
wantToBuy: 'WANT TO BUY',
sold: 'SOLD',
description: 'Description',
usuallyHere: 'Usually here {{availability}}',
listed: 'Listed {{date}}',
listingCountOne: '{{count}} listing · {{age}}d old',
listingCountMany: '{{count}} listings · {{age}}d old',
relistActive: 'Relist as active',
markSold: 'Mark as sold',
deleteListing: 'Delete listing',
makeOffer: 'Make an offer',
shareListing: 'Share listing',
unknown: 'Unknown',
linkCopied: 'Link copied',
linkCopiedBody: 'Share it with a skater.',
copyFailed: 'Could not copy the link',
relisted: 'Relisted',
relistedBody: 'Your listing is active again.',
couldNotUpdate: 'Could not update',
somethingWrong: 'Something went wrong',
},
create: {
imageUploaded: 'Image uploaded',
uploadFailed: 'Upload failed',
failed: 'Failed',
cooldownActive: 'Cooldown active',
cooldownWait: 'Please wait {{time}} before creating another listing',
limitReached: 'Listing limit reached',
limitReachedBody: "You've reached your monthly limit of {{limit}} listings",
feeRequired: 'Fee required',
feeRequiredBody: 'Please pay the anti-spam fee to continue',
titleRequired: 'Title required',
categoryRequired: 'Category required',
photoRequired: 'Photo required',
photoRequiredBody: 'At least one photo is required when selling gear',
nip05Required: 'NIP-05 verification required',
nip05RequiredBody: 'You need a verified Nostr address to list gear',
priceRequired: 'Price required for fixed price listings',
sizeRequired: 'Size required',
sizeRequiredBody: 'Please select a size for {{category}}',
conditionRequired: 'Condition required',
conditionRequiredBody: 'Please select the condition of your {{category}}',
wtbPosted: 'Want to Buy posted!',
wtbPostedBody: 'Sellers will be able to find your request.',
listingCreated: 'Listing created!',
listingCreatedBody: 'Your gear is now listed.',
publishFailed: 'Failed to publish',
loginRequired: 'Login Required',
loginRequiredBody: 'You need to log in to list gear.',
backToStash: 'Back to Stash',
backToSpot: 'Back to {{title}}',
backToMarketplace: 'Back to Marketplace',
backToMap: 'Back to Map',
wantToBuy: 'Want to Buy',
listYourGear: 'List Your Gear',
wtbSubtitle: "Tell the community what gear you're looking for",
sellSubtitle: 'Sell skateboarding gear to the community',
fromStashTitle: 'Listing from your Stash',
fromStashBody: 'Pre-filled with your stash item details. Review and pick a meetup spot!',
nip05Banner: 'To prevent spam and protect buyers, you need a verified Nostr address (NIP-05) to list gear.',
verifiedLabel: 'Verified:',
checkingTrust: 'Checking your trust level...',
trustLevel: 'Trust Level:',
trusted: 'Trusted',
active: 'Active',
newcomer: 'Newcomer',
newUser: 'New User',
remaining: '{{remaining}} of {{limit}} listings remaining this month',
cooldownBody: 'New users have a 24-hour cooldown between listings.',
timeRemaining: 'Time remaining: {{time}}',
monthlyLimitTitle: 'Monthly Limit Reached',
monthlyLimitBody: "You've reached your limit of {{limit}} listings this month. Build your reputation by creating spots or completing sales to increase your limit.",
powAccepted: 'Proof of work accepted!',
feePaid: 'Fee Paid!',
canCreateNow: 'You can now create your listing.',
whatDoTitle: 'What do you want to do?',
sellGear: 'SELL GEAR',
categoryTitle: 'Category *',
categoryHint: 'What type of gear?',
detailsTitle: 'Gear Details',
detailsWtbHint: "Describe what you're looking for",
detailsSellHint: 'Tell buyers about your item',
titleLabel: 'Title *',
titlePlaceholderWtb: 'Looking for 8.0" deck',
titlePlaceholderSell: 'Almost New Palace Deck 8.25"',
summaryLabel: 'Short Summary',
summaryPlaceholderWtb: 'Any brand, prefer minimal graphics',
summaryPlaceholderSell: 'Great condition, barely used',
descriptionLabel: 'Description',
descriptionPlaceholderWtb: "Describe exactly what you're looking for - brand preferences, condition requirements, etc.",
descriptionPlaceholderSell: 'Got this deck for Christmas but prefer a smaller size. Happy to let you try it before buying!',
fulfilmentLabel: 'How is it fulfilled?',
pickup: 'Pickup',
pickupHint: 'Meet locally',
ships: 'Ships',
shipsHint: 'Post it',
both: 'Both',
bothHint: 'Either',
addressNote: 'We never collect your address — for shipping, share it privately in the trade chat.',
postageLabel: 'Who pays postage?',
requireEscrow: 'Require escrow',
requireEscrowHint: 'Ask the buyer to settle through Cashu escrow.',
postageBuyer: 'Buyer pays',
postageSeller: 'I pay',
postageSplit: 'Split',
sizeLabel: 'Size',
selectSize: 'Select size',
truckHeight: 'Truck Height',
selectHeight: 'Select height',
wheelHardness: 'Wheel Hardness',
selectHardness: 'Select hardness',
pricingTitle: 'Pricing',
pricingWtbHint: 'How do you want to pay?',
pricingSellHint: 'How do you want to sell?',
priceLabel: 'Price *',
currencyLabel: 'Currency',
photosTitle: 'Photos',
photosSellHint: 'At least one photo is required',
photosWtbHint: 'Optional - add reference images',
productPhoto: 'Product {{n}}',
upload: 'Upload',
photosNote: 'Add up to 5 photos. First image will be the main photo.',
pickupLocationTitle: 'Pickup Location',
pickupLocationHint: 'Link to a skate spot for easy meetups',
searchSpots: 'Search spots...',
noSpotsFound: 'No spots found. Try a different search.',
availabilityLabel: 'When are you usually available?',
availabilityPlaceholder: 'Weekends 10am-2pm, Tue/Thu evenings',
availabilityHint: 'Helps buyers coordinate meetups with you',
durationTitle: 'Listing Duration',
durationHint: 'How long should this listing stay active?',
publishing: 'Publishing...',
checkingEligibility: 'Checking eligibility...',
cooldownButton: 'COOLDOWN: {{time}}',
limitReachedButton: 'LIMIT REACHED',
payFeeButton: 'PAY FEE TO CONTINUE',
postWtbButton: 'POST WANT TO BUY',
listForSaleButton: 'LIST GEAR FOR SALE',
},
trades: {
title: 'My Trades',
loginBody: 'Log in to manage your marketplace trades and reputation.',
subtitle: 'Confirmations, requests, and the reputation they build. Payment is arranged with the other skater — the app only records the deal.',
actionNeeded: 'Action needed',
askedToConfirmSale: 'asked you to confirm a sale',
recordedSale: 'recorded a sale — confirm your side',
confirmSale: 'Confirm sale',
confirm: 'Confirm',
nothingWaiting: 'Nothing waiting on you right now. 🛹',
yourRecords: 'Your trade records',
noTrades: 'No trades yet. Close a deal and confirm it to start building reputation.',
browseGearSwap: 'Browse Gear Swap',
roleSold: 'Sold',
roleBought: 'Bought',
roleTrade: 'Trade',
with: 'with',
viewListing: 'View listing',
revoke: 'Revoke',
revokeTitle: 'Revoke this trade record?',
revokeBody: 'It will stop counting toward your reputation. The other party keeps their own attestation. Use this for a disputed or mistaken deal.',
revokeNote: 'Retracted by the author.',
revoked: 'Trade record revoked',
revokedBody: 'It no longer counts toward reputation.',
revokeFailed: 'Could not revoke the trade',
saleConfirmed: 'Sale confirmed',
saleConfirmedBody: 'They can now confirm their side.',
saleConfirmFailed: 'Could not confirm the sale',
tradeConfirmed: 'Trade confirmed',
tradeConfirmedBody: 'Reputation updated for both of you.',
tradeConfirmFailed: 'Could not confirm the trade',
somethingWrong: 'Something went wrong.',
},
card: {
listingDeleted: 'Listing deleted',
listingRemoved: 'Listing removed',
listingDeletedBody: 'Your listing has been removed. Relays that honor deletions will stop serving it.',
listingRemovedBody: 'This listing has been taken down and will be hidden across SkateSpots.',
removeFailed: 'Remove failed',
somethingWrong: 'Something went wrong',
markedSold: 'Marked as sold',
markedSoldBody: 'Your listing is now shown as sold.',
couldNotUpdate: 'Could not update',
relisted: 'Relisted',
relistedBody: 'Your listing is active again.',
listingOptions: 'Listing options',
report: 'Report',
removeAdmin: 'Remove (admin)',
postage: 'postage',
newSeller: 'New seller',
saleOne: '{{count}} sale',
saleMany: '{{count}} sales',
iHaveThis: 'I Have This!',
deleteTitle: 'Delete "{{title}}"?',
adminRemoveTitle: 'Remove this listing? (admin)',
deleteBody: 'This asks relays to remove your listing "{{title}}". It can\'t be undone from here — you\'d have to create a new one.',
adminRemoveBody: 'As a SkateSpots admin you\'re taking down "{{title}}". It will be hidden across the app for everyone and a deletion request will be sent to relays.',
deleting: 'Deleting…',
removing: 'Removing…',
deleteListing: 'Delete Listing',
removeListing: 'Remove Listing',
reportCount: '{{count}} report',
reports: '{{count}} reports',
viewTitle: 'View {{title}}',
reportListing: 'Report Listing',
manageListing: 'Manage listing',
trusted: 'Trusted',
daysOld: '{{n}}d old',
monthsOld: '{{n}}mo old',
yearsOld: '{{n}}y old',
listingsCount: '{{count}} listings',
postageBuyer: 'postage: buyer',
postageSeller: 'postage: seller',
postageSplit: 'postage: split',
powTitle: 'NIP-13 proof of work — free anti-spam',
verifiedTradeOne: '{{count}} verified trade',
verifiedTrades: '{{count}} verified trades',
reportSubmitted: 'Report submitted',
reportSubmittedBody: 'Thank you for helping keep the marketplace safe.',
reportFailed: 'Failed to submit report',
selectReason: 'Please select a reason',
reportDialogBody: 'Help keep the marketplace safe by reporting inappropriate or misleading listings.',
reasonLabel: 'Reason for report *',
detailsLabel: 'Additional details (optional)',
detailsPlaceholder: 'Provide any additional context that might help...',
reportNote: 'Reports are published to Nostr and help the community identify problematic listings. Listings with multiple reports may be automatically hidden.',
submitting: 'Submitting...',
submitReport: 'Submit Report',
},
tradeActions: {
tradeConfirmed: 'Trade confirmed',
tradeConfirmedWithProof: 'A verifiable receipt was attached — this counts toward verified trades.',
tradeConfirmedBody: 'Reputation updated for both of you.',
tradeConfirmFailed: 'Could not confirm the trade',
somethingWrong: 'Something went wrong.',
requestNote: 'Please confirm our trade for "{{title}}".',
requestSent: 'Confirmation requested',
requestSentBody: 'The seller can now confirm the sale and build both reputations.',
requestFailed: 'Could not send the request',
escrowRefunded: 'Escrow refunded',
escrowRefundedBody: 'The funds are back in your Cashu wallet.',
escrowRefundFailed: 'Could not refund escrow',
confirmThisTrade: 'Confirm this trade',
escrowAndRequest: 'Escrow & request',
shipsEscrowHint: 'Shipped deal — use escrow so neither side ships into the void. Share your address privately in the trade chat, never here.',
shipsHint: 'Shipped deal — prefer escrow if the seller offers it, and share your address only in the chat.',
askSeller: 'Ask seller to confirm',
waitingSeller: 'Confirmation requested — waiting on the seller.',
refundEscrow: 'Refund escrow',
buyWithSats: 'Buy with sats',
depositTitle: 'Good-faith deposit (optional)',
payDeposit: 'Pay deposit',
depositBefore: 'A swap has no invoice, so this is how it gets a',
verifiableCost: 'verifiable cost',
depositAfter: '. Refund on delivery (or use Cashu escrow for an automatic refund). ⚠️ Proves commitment, not delivery.',
depositPaid: 'Deposit paid',
depositPaidBody: 'Saved — it will be attached when the trade is confirmed.',
paid: 'Paid ⚡',
paidWithProof: 'Saved a verifiable receipt — confirm the trade to make it count.',
paidBody: 'Ask the seller to confirm the trade so it counts for both of you.',
},
offers: {
accepted: 'Accepted',
declined: 'Declined',
withdrawn: 'Withdrawn',
open: 'Open',
noAmount: 'no amount',
titleOwner: 'Offers ({{count}})',
titleBuyer: 'Your negotiations',
you: 'You',
them: 'Them',
paidAskConfirm: 'Paid — ask the seller to confirm the trade.',
payWithSats: 'Pay with sats',
accept: 'Accept',
decline: 'Decline',
counter: 'Counter',
counterPlaceholder: 'Your counter (sats)',
send: 'Send',
counterItemPlaceholder: '…or counter with an item to trade (optional)',
waitingOn: 'Waiting on',
waitingOnSuffix: '— your turn will come back if they counter.',
withdraw: 'Withdraw',
dmAccepted: '✅ Your offer was accepted! Message to arrange the handoff.',
dmDeclined: '❌ Your offer was declined.',
offerAccepted: 'Offer accepted',
offerDeclined: 'Offer declined',
respondFailed: 'Could not respond',
somethingWrong: 'Something went wrong.',
offerWithdrawn: 'Offer withdrawn',
withdrawFailed: 'Could not withdraw',
enterAmountOrItem: 'Enter an amount or an item to trade',
counterDm: 'Counter-offer',
counterSent: 'Counter-offer sent',
counterSentBody: "It's their turn now.",
counterFailed: 'Could not send the counter',
},
offer: {
validAmount: 'Enter a valid amount',
describeOffer: 'Describe your offer',
describeOfferBody: 'Add an amount, an item to trade, or a message.',
dmAmount: ' for {{amount}} sats',
dmItem: ' (offering: {{item}})',
dmBody: '🛹 New offer on your listing "{{title}}"{{amount}}{{item}}.{{note}}',
sent: 'Offer sent',
sentBody: '{{sellerName}} can accept or decline it.',
sendFailed: 'Could not send the offer',
somethingWrong: 'Something went wrong.',
title: 'Make an offer',
description: 'Offer sats, or swap something — {{sellerName}} can accept, decline or counter. You arrange the handover between yourselves.',
amountLabel: 'Amount (sats, optional)',
amountPlaceholder: 'e.g. 45000',
itemLabel: 'Or trading something (optional)',
itemPlaceholder: 'e.g. my 8.25 deck + spare wheels',
itemHint: "Swapping? Put what you're offering here — you can combine it with sats.",
messageLabel: 'Message (optional)',
messagePlaceholder: 'Anything the seller should know…',
send: 'Send offer',
},
markSold: {
enterBuyer: "Enter the buyer's npub",
dealClosed: 'Deal closed 🎉',
dealClosedBody: 'Listing marked sold and the trade was published for reputation.',
closeFailed: 'Could not close the deal',
somethingWrong: 'Something went wrong.',
title: 'Close the deal',
description: 'Marks this listing sold and records a signed trade attestation for you and the buyer.',
buyerLabel: "Buyer's npub",
methodLabel: 'How was it paid? (self-reported)',
amountLabel: 'Amount in sats (optional)',
submit: 'Mark sold',
},
handoff: {
title: 'Arrange the handoff',
suggest: 'Suggest a time & place',
suggestDm: 'Hi! I\'d like to buy "{{title}}". Suggest a time and place to meet — happy to meet at a public skate spot.',
orMeetAt: 'Or meet at a nearby public spot:',
spotDm: 'Hi! I\'d like to buy "{{title}}". Want to meet at {{spot}}? {{url}}',
shareDelivery: 'Share delivery details',
deliveryDm: 'Hi! I\'d like to buy "{{title}}". Here are my delivery details:\n\nName:\nAddress:\n\n(Only share what the courier needs.)',
useLocker: 'Use a locker / parcel point',
lockerDm: 'Hi! I\'d like to buy "{{title}}". Could we use a parcel locker / parcel shop instead of a home address?',
noteBefore: 'These details stay in the private chat — SkateSpots never sees them. For shipped deals, prefer',
escrow: 'escrow',
noteMiddle: ', and',
neverPay: "never pay outside the app's rails",
noteAfter: '. Meet in public.',
},
shipping: {
noCounterparty: 'No counterparty yet',
noCounterpartyBody: 'The seller should record the sale (naming the buyer) first.',
markedShipped: 'Marked as shipped',
markedReceived: 'Marked as received',
issueReported: 'Issue reported',
receivedBody: 'Thanks — this does not release escrow; settle directly with the seller.',
notifiedBody: 'The other side has been notified.',
updateFailed: 'Could not update',
somethingWrong: 'Something went wrong.',
title: 'Shipping',
shipped: 'shipped',
received: 'received',
issue: 'issue',
trackingPlaceholder: 'Tracking number (optional)',
iShipped: "I've shipped it",
iReceived: 'I received it',
reportIssue: 'Report an issue',
noteBefore: 'Self-reported — a logistics note,',
notProof: 'not proof',
noteAfter: '. Marking "received" does not release escrow. ⚠️ Don\'t photograph a parcel with an address visible, and only share a tracking number if you\'re comfortable with it being public.',
},
pay: {
sent: 'Payment sent ⚡',
sentWithProof: 'Saved a verifiable payment receipt — it will count as payment-verified.',
sentBody: 'Let the seller know — they can confirm the trade so it counts for both of you.',
failed: 'Payment failed',
title: 'Pay with sats',
descSendPrefix: 'Send',
descSendSuffix: 'to {{sellerName}} to settle this deal.',
descSettle: 'Settle this deal directly with {{sellerName}}.',
paid: 'Paid!',
paidBody: 'Ask the seller to confirm the trade — two confirmations build both reputations.',
noMethod: "{{sellerName}} hasn't set a payment method (no Lightning invoice or address). Message them to arrange payment.",
ownListing: 'This is your own listing.',
paying: 'Paying…',
pay: 'Pay',
payAmount: 'Pay {{amount}} sats',
footnote: 'Pays from your connected wallet (NWC, WebLN, or Cashu) straight to {{sellerName}} — SkateSpots never holds the funds.',
},
spamFee: {
invoiceComment: 'SkateSpots listing fee',
generateFailed: 'Failed to generate invoice',
paymentSuccess: 'Payment successful!',
satsSent: '{{amount}} sats sent to {{name}}',
autoPayFailed: 'Auto-pay failed',
autoPayFailedBody: 'Please pay manually using the QR code or invoice',
invoiceCopied: 'Invoice copied!',
copyFailed: 'Failed to copy',
received: 'Payment Received!',
receivedBody: 'Your {{amount}} sat fee has been paid. You can now create your listing.',
title: 'One-Time Listing Fee',
subtitle: 'As a new member, a small fee is required for your first listing.',
whyTitle: 'Why do I need to pay?',
spamLabel: 'Spam prevention:',
spamBody: 'This fee stops bots and scammers from flooding the marketplace with fake listings.',
trustLabel: 'Trust building:',
trustBody: 'New accounts need to prove commitment before listing. After a few successful listings, this fee is waived.',
supportsLabel: 'Supports the community:',
supportsPrefix: 'Fee goes to',
supportsSpotOwner: ' — the person who created this spot!',
supportsPlatform: ' — helping keep SkateSpots running.',
oneTimeFee: '~${{usd}} USD • One-time fee',
payToContinue: 'Pay {{amount}} sats to Continue',
proveWork: 'Prove work instead — free',
proveWorkBody: 'Spend a few seconds of CPU instead of {{amount}} sats. No money, no wallet — a spam tax, not a payment.',
generating: 'Generating invoice...',
processing: 'Processing payment...',
qrAlt: 'Payment QR Code',
copyInvoice: 'Copy Invoice',
openWallet: 'Open Wallet',
payWithWallet: 'Pay with Connected Wallet',
scanHint: 'Scan the QR code with a Lightning wallet or use the buttons above',
skipTitle: 'How to skip this fee in the future:',
skip1: 'Add a skate spot to the map',
skip2: 'Complete 2-3 successful listings',
skip3: 'Build your community reputation',
skipNote: 'Trusted members get unlimited free listings!',
},
reputation: {
highlyTrusted: 'Highly trusted trader',
building: 'Building reputation',
newTrader: 'New trader',
title: 'Trades',
verified: 'Verified',
sales: 'Sales',
purchases: 'Purchases',
tradeConfirmedOne: 'mutually-confirmed trade',
tradeConfirmedMany: 'mutually-confirmed trades',
with: 'with',
personOne: 'person',
personMany: 'people',
awaiting: 'Awaiting mutual confirmation',
withPaymentProof: 'with a payment proof attached',
selfReported: '(self-reported — not verified)',
paymentVerified: 'payment-verified',
zapReceipt: '(zap receipt)',
singlePartnerWarning: 'Every confirmation is with the same account. Mutual confirmation is self-reported, so treat this record with care.',
lowDiversityWarning: 'Most confirmations come from very few accounts. A small group vouching for each other can look busy — treat this record with care.',
ringWarning: 'These accounts mostly trade with each other — a closed cluster. Mutual confirmation inside a ring is weak evidence.',
weighted: 'Trust & activity weighted',
footnote: 'Self-reported and mutually signed — a signal, not proof of payment.',
},
reputationPanel: {
reviewOfYou: 'Review of you',
yourRecord: 'your record',
listing: 'Listing',
averageOne: 'average from {{count}} rating',
averageMany: 'average from {{count}} ratings',
reviewsTitle: 'Reviews & trade records',
fromOthers: '{{count}} from others',
emptyBefore: 'No trade records yet. Complete a deal and confirm it in',
emptyAfter: '— each confirmation becomes a review on both profiles.',
},
recommendations: {
published: 'Recommendation published',
publishedBody: 'Thanks for vouching.',
failed: 'Could not recommend',
somethingWrong: 'Something went wrong.',
title: 'Trusted trader',
recommendedBy: 'Recommended by',
skaterOne: 'skater',
skaterMany: 'skaters',
none: 'No recommendations yet.',
youRecommend: 'You recommend this trader ✓',
recommend: 'Recommend as trusted trader',
},
notification: {
newOffer: 'New offer',
counterOffer: 'Counter-offer',
offerAccepted: 'Offer accepted',
offerDeclined: 'Offer declined',
offerWithdrawn: 'Offer withdrawn',
confirmTrade: 'Confirm a trade',
tradeConfirmed: 'Trade confirmed',
reviewOffer: 'Review the offer',
openListing: 'Open listing',
},
confirmRow: {
rateTrade: 'Rate the trade:',
},
deckRating: {
title: '{{value}}/10 decks',
aria: '{{n}} of 10',
},
clink: {
tip: 'Tip in Bitcoin',
copiedBody: 'CLINK code copied to clipboard.',
copyFailed: 'Copy failed',
copyFailedBody: 'Could not access the clipboard.',
whatIs: 'What is CLINK?',
tooltipTitle: 'Pay directly with Bitcoin over Nostr',
tooltipBody: 'CLINK is a self-custodial payment code — no app account, no middleman. Scan it with a compatible Lightning wallet and the tip goes straight to them.',
descRecipient: 'Send a self-custodial Bitcoin (Lightning) payment to {{recipientName}} — no middleman.',
descGeneric: 'Send a self-custodial Bitcoin (Lightning) payment over Nostr — no middleman.',
qrAlt: 'CLINK Offer QR code',
copyCode: 'Copy CLINK code',
openWallet: 'Open in CLINK wallet',
footnote: 'Scan with a CLINK-compatible wallet (e.g. ShockWallet). If nothing opens, copy the code above — you may not have a CLINK-compatible wallet installed. No custodian, no web server: the invoice is requested directly over Nostr.',
},
paymentReadiness: {
ready: 'You can get paid',
notReady: 'Set up payments to get paid',
readyBefore: 'Your Lightning address',
readyAfter: ' is on your profile — people can zap and pay you directly.',
silentSet: ' A Bitcoin Silent Payment address is set too.',
notReadyBefore: 'Add a Lightning address (and optionally a Bitcoin Silent Payment address) to your',
nostrProfile: 'Nostr profile',
notReadyAfter: "so skaters can pay you. It's set once and works across every profile you have here.",
},
seller: {
trade: 'Trade',
active: 'Active',
markSold: 'Mark sold',
yourListings: 'Your listings',
activeCount: '{{count}} active',
listItem: 'List an item',
loading: 'Loading your listings…',
empty: 'You have no listings yet.',
listFirst: 'List your first item',
},
noffer: {
label: 'CLINK Offer Code',
optional: '(optional, advanced)',
invalidBefore: "That doesn't look like a valid",
invalidAfter: 'code.',
valid: 'Valid CLINK code — a "Pay via CLINK" button will show on your profile.',
help: 'Paste a self-custodial CLINK payment code (e.g. from ShockWallet / Lightning.Pub) to accept Nostr-native Lightning payments — no web server or custodian.',
learnSpecs: 'Learn specs',
},
escrowPanel: {
escrowed: 'Escrowed {{amount}} sats',
mint: 'Mint',
refundSinceBefore: 'Buyer can refund since',
redeemBefore: 'Seller can redeem now · buyer refunds after',
noExpiry: 'No expiry set on this token.',
},
escrowRequest: {
enterAmount: 'Enter an escrow amount',
minLock: 'Lock for at least {{days}} day',
requestNote: 'Escrowed {{amount}} sats for our trade — please confirm the sale.',
created: 'Escrow created 🎉',
createdBody: '{{amount}} sats locked for {{days}} days. {{sellerName}} can redeem on delivery.',
createFailed: 'Could not create escrow',
somethingWrong: 'Something went wrong.',
title: 'Escrow this trade',
description: 'Funds are locked to {{sellerName}} and refundable by you if the deal falls through after the deadline. Requires a connected Lightning wallet.',
amountLabel: 'Amount (sats)',
daysLabel: 'Buyer refund after (days)',
daysHint: "After this many days you can reclaim the funds if the seller hasn't delivered.",
nativeOnly: 'Cashu escrow is available in the SkateSpots app only (not the web build).',
lockAndRequest: 'Lock & request',
},
},
labUi: {
activeCount: '{{count}} active',
addMusicProfile: 'Add Music Profile',
all: 'All',
clearAll: 'Clear all',
collective: 'Collective',
filters: 'Filters',
following: 'Following',
getRiderCard: 'Get Rider Card',
judgeEvents: 'Judge Events',
justASkater: 'Just a',
labForServices: "The Lab's for services. Browse & join the whole skater community",
loginToOffer: 'Login to offer services',
newest: 'Newest',
noResults: 'No results',
offer: 'Offer',
offerBuilding: 'Offer Building',
offerCoaching: 'Offer Coaching',
offerCuts: 'Offer Cuts',
offerDesignArt: 'Offer Design / Art',
offerEditing: 'Offer Editing',
offerFilming: 'Offer Filming',
offerGraffiti: 'Offer Graffiti',
offerPhotography: 'Offer Photography',
offerServices: 'Offer Services',
yourProfiles: 'Your Lab profiles',
preview: {
button: 'Preview',
title: 'Live preview',
desc: 'This is how your {{noun}} profile will look once published.',
untitled: 'Your name',
noBio: 'No bio yet — add a line about what you offer.',
years: '{{count}} years experience',
portfolio: 'Portfolio',
note: 'Preview only — nothing is published yet.',
},
draft: {
title: 'You have an unsaved draft',
body: 'Pick up where you left off — nothing is published until you press Publish.',
resume: 'Resume draft',
discard: 'Discard draft',
},
progress: {
title: 'Profile completeness',
count: '{{done}} of {{total}} done',
photo: 'Photo',
bio: 'Bio',
skills: 'Skills',
rate: 'Rate',
location: 'Location',
contact: 'Contact',
},
wizard: {
identity: 'Identity',
offer: 'What you offer',
rates: 'Rates',
contact: 'Contact',
media: 'Media',
},
offerTattoo: 'Offer Tattoo',
offerTraining: 'Offer Training / Physio',
organizeEvents: 'Organize Events',
resultsMany: '{{count}} results',
resultsOne: '{{count}} result',
searchCityPlaceholder: 'City…',
searchNamePlaceholder: 'Name, bio…',
showResults: 'Show Results',
showResultsActive: 'Show Results · {{count}} active',
skaterWord: 'skater',
sortBy: 'Sort By',
theCrew: 'The Crew',
topRated: 'Top Rated',
trusted: 'Trusted',
trustLevel: 'Trust Level',
yearsExp: '{{count}}yr exp',
tabCoaches: 'Coaches',
tabFilmers: 'Filmers',
tabPhoto: 'Photo',
tabEditors: 'Editors',
tabBuilders: 'Builders',
tabArtists: 'Artists',
tabGraffiti: 'Graffiti',
tabTattoo: 'Tattoo',
tabTrainers: 'Trainers',
tabMusic: 'Music',
tabJudges: 'Judges',
tabOrganizers: 'Organizers',
tabBarbers: 'Barbers',
filterSkillsToLearn: 'Skills to Learn',
filterContentType: 'Content Type',
filterEquipmentStyle: 'Equipment & Style',
filterServices: 'Services',
filterCamera: 'Camera',
filterLenses: 'Lenses',
filterLighting: 'Lighting',
filterSupportRig: 'Support & Rig',
filterStyle: 'Style',
filterWhatTheyBuild: 'What They Build',
filterMaterialsTrade: 'Materials & Trade',
filterProjectScale: 'Project Scale',
filterWhatTheyMake: 'What They Make',
filterMedium: 'Medium',
filterSoftwareTools: 'Software & Tools',
filterWorkContext: 'Work Context',
filterMethod: 'Method',
filterFocus: 'Focus',
filterInjuriesTreated: 'Injuries Treated',
filterCredentialsSetting: 'Credentials & Setting',
filterGenre: 'Genre',
filterInstrumentsRole: 'Instruments & Role',
filterEventFormats: 'Event Formats',
filterSpecialty: 'Specialty',
filterLevelCircuit: 'Level / Circuit',
filterEventTypes: 'Event Types',
filterWhatYouHandle: 'What You Handle',
filterScale: 'Scale',
filterWhatTheyOffer: 'What They Offer',
filterSignatureStyles: 'Signature Styles',
filterSetting: 'Setting',
filterEditType: 'Edit Type',
role: {
coach: 'Coach',
filmer: 'Filmer',
builder: 'Builder',
editor: 'Video Editor',
photographer: 'Photographer',
skater: 'Skater',
},
empty: {
footer: 'Have questions? Check the FAQ →',
loginSuffix: 'to create a profile.',
coachesTitle: 'No coaches yet',
coachesBody: 'Be the first to offer skateboarding coaching in your area.',
filmersTitle: 'No filmers yet',
filmersBody: 'Be the first to offer skate filming — clips, full parts, contest coverage and more.',
photographersTitle: 'No photographers yet',
photographersBody: 'Be the first to offer skate photography in your area — action shots, sequences, editorial and more.',
editorsTitle: 'No video editors yet',
editorsBody: 'Be the first to offer skate video editing services.',
buildersTitle: 'No builders yet',
buildersBody: 'Be the first to list your ramp, park or DIY building — concrete, wood, rails and more.',
artistsTitle: 'No artists yet',
artistsBody: 'Be the first to offer board graphics, logos, posters and merch for the scene.',
graffitiTitle: 'No graffiti artists yet',
graffitiBody: 'Be the first to offer pieces, murals and skatepark art in your area.',
tattooTitle: 'No tattoo artists yet',
tattooBody: 'Be the first skate-culture tattoo artist to list — flash, custom, fine line and more.',
trainersTitle: 'No trainers yet',
trainersBody: 'Be the first trainer or physio to help skaters get strong and stay healthy.',
musiciansTitle: 'No musicians yet',
musiciansBody: 'Skate and make music? Add your band or solo project — genres, instruments and links.',
judgesTitle: 'No judges yet',
judgesBody: 'Run a fair contest? List yourself to judge Game of S.K.A.T.E., best trick jams, contests and touring stops.',
organizersTitle: 'No organizers yet',
organizersBody: 'Put on jams, comps, meetups or demos? List yourself so skaters can find and book you.',
barbersTitle: 'No barbers yet',
barbersBody: 'Cut hair for skaters? List your chair or mobile service so the scene can get a fresh trim.',
},
become: {
addNip05: 'Add NIP-05 in Settings',
backToLab: 'Back to The Lab',
bioWorkLabel: 'Bio / About Your Work',
checkContact: 'Check your contact details',
cityArea: 'City / Area',
cityCountry: 'City, Country',
connectWallet: 'Connect Wallet',
connectWalletNwc: 'Connect Wallet with NWC',
contactInfo: 'Contact Information',
contactInfoDesc: 'How clients can reach you (all optional, but recommended)',
coordinatesOptional: 'Coordinates (optional)',
email: 'Email',
failedToPublish: 'Failed to publish',
fromNostrProfile: 'From your Nostr profile',
geolocationUnsupported: 'Geolocation not supported',
getting: 'Getting…',
headline: 'Headline',
headlineHint: 'Shown as the title on your Lab card — e.g. “{{example}}”. Defaults to your name.',
imageUploaded: 'Image uploaded',
instagram: 'Instagram',
invalidEmail: 'Enter a valid email address.',
invalidPortfolio: 'Enter a valid portfolio URL.',
invalidWebsite: 'Enter a valid website URL.',
latitude: 'Latitude',
learnHow: 'Learn How',
locationError: 'Location error',
locationFound: 'Location found',
loginRequired: 'Login Required',
loginRequiredDesc: 'You need to log in to create a {{role}} profile.',
loginRequiredDescCraft: 'You need to log in to create a {{noun}} profile.',
longitude: 'Longitude',
nip05Recommended: 'NIP-05 Verification Recommended',
nip05RecommendedDesc: 'Verify your identity to build trust with clients.',
noWallet: 'No Wallet Connected',
otherSocial: 'Other Social',
paymentSetup: '⚡ Payment Setup Required',
phoneNumber: 'Phone Number',
profilePhoto: 'Profile Photo',
profilePublished: 'Your profile has been published.',
publishing: 'Publishing...',
socialMedia: 'Social Media',
tip: '💡 Tip: Most Lightning wallets support NWC (Alby, Minibits, Zeus, Coinos).',
tipWithTime: '💡 Tip: Most Lightning wallets support NWC (Alby, Minibits, Zeus, Coinos). Takes 30 seconds to set up.',
upload: 'Upload',
uploadFailed: 'Upload failed',
uploadImage: 'Upload image',
uploading: 'Uploading…',
useCurrentLocation: 'Use current location',
useMyLocation: 'Use My Location',
usingNwc: 'Using Nostr Wallet Connect',
usingWebln: 'Using WebLN browser extension',
walletConnected: 'Wallet Connected — Ready to Receive Payments!',
website: 'Website',
yearsExperience: 'Years Experience',
},
coachForm: {
anonymous: 'Anonymous Coach',
bioLabel: 'Bio / Coaching Philosophy',
bioPlaceholder: 'Your background, teaching style...',
create: 'CREATE COACH PROFILE',
created: 'Coach profile created!',
createPro: 'CREATE PRO COACH PROFILE',
dmNote: "💬 Students can message you directly through SkateSpots' encrypted messaging",
heading: 'Become a Coach',
headlineExample: 'Skate Coach Berlin',
hourlyRate: 'Hourly Rate',
identity: 'Your Coach Identity',
nip05Reason: "Add a NIP-05 identifier to verify your identity and build trust with students — it shows them you're a legitimate coach.",
paymentDesc: 'To receive lesson payments from students, you need to connect a Lightning wallet. Students will zap you directly for coaching sessions.',
profileDesc: 'Tell students about your experience',
profileTitle: 'Coach Profile',
ratePlaceholder: '5000 sats/hr',
selectSkill: 'Select at least one skill.',
skillsLabel: 'Skills I Teach * (select at least one)',
skillsRequired: 'Skills required',
formatsLabel: 'Session formats',
formatsHint: 'How you deliver coaching.',
levelsLabel: 'Levels taught',
agesLabel: 'Age groups',
credentialsLabel: 'Credentials & safety',
credentialsHint: 'Self-attested — SkateSpots does not verify these.',
languagesLabel: 'Languages spoken',
languagesHint: 'Add the languages you can coach in.',
languagePlaceholder: 'e.g. English, Español',
availabilityLabel: 'Availability',
bookingLabel: 'Booking link',
bookingHint: 'Optional — a page where students can book and pay.',
galleryLabel: 'Portfolio photos',
galleryHint: 'Show real sessions, students and spots (up to 12).',
galleryAdd: 'Add portfolio photos',
galleryUploaded: '{{count}} photo(s) uploaded',
videoLabel: 'Highlight reel',
videoHint: 'Optional — a YouTube or Instagram video of your coaching.',
formatOneOnOne: '1-on-1',
formatGroup: 'Group',
formatCamp: 'Camp',
formatVideo: 'Video analysis',
formatContestPrep: 'Contest prep',
formatParkLesson: 'Park lesson',
formatAdaptive: 'Adaptive',
formatGirls: 'Girls session',
formatAfterSchool: 'After school',
levelBeginner: 'Beginner',
levelIntermediate: 'Intermediate',
levelAdvanced: 'Advanced',
levelCompetition: 'Competition',
ageKids: 'Kids',
ageTeens: 'Teens',
ageAdults: 'Adults',
ageSeniors: 'Seniors',
certFirstAid: 'First aid',
certCpr: 'CPR',
certSafeguarding: 'Safeguarding',
certBackgroundCheck: 'Background check',
certInsured: 'Insured',
certFederation: 'National federation coach',
certQualification: 'Coaching qualification',
certJudge: 'Competition judge',
certNoteLabel: 'Other credentials (optional)',
certNotePlaceholder: 'e.g. Skateboard GB Level 2, 8 years coaching…',
availWeekdays: 'Weekdays',
availWeekends: 'Weekends',
availEvenings: 'Evenings',
availAfterSchool: 'After school',
availHolidays: 'School holidays',
availSeasonal: 'Seasonal',
sectionWhatYouCoach: 'What you coach',
sectionCredentials: 'Credentials & safety',
sectionAvailability: 'Availability & booking',
sectionWhereYouCoach: 'Where you coach',
sectionPortfolio: 'Portfolio',
rateAmountLabel: 'Rate',
rateCurrencySats: 'sats',
rateCurrencyUsd: 'USD',
rateCurrencyEur: 'EUR',
rateCurrencyGbp: 'GBP',
perHour: 'per hour',
perSession: 'per session',
perHalfDay: 'per half-day',
perDay: 'per day',
perCamp: 'per camp',
perProject: 'per project',
perEvent: 'per event',
perGig: 'per gig',
perShoot: 'per shoot',
perPiece: 'per piece',
perCut: 'per cut',
perMonth: 'per month',
availByAppointment: 'By appointment',
rateNotesLabel: 'Rate notes (optional)',
rateNotesPlaceholder: 'Sliding scale, first session free, travel included…',
sectionPayments: 'Payments',
paymentMethodsHint: 'How clients can pay you. This is a statement — SkateSpots never holds funds.',
payMethodCash: 'Cash on site',
payMethodLightning: 'Lightning',
payMethodCashu: 'Cashu',
payMethodBank: 'Bank transfer',
payMethodOther: 'Other',
payWhatYouCanLabel: 'Sliding scale / pay what you can',
bookingFeeLabel: 'Booking fee (optional)',
bookingFeeHint: 'A small fee paid directly to you to confirm a booking. SkateSpots does not hold or refund it.',
paymentNoteLabel: 'Payment note (optional)',
paymentNotePlaceholder: 'First session free, travel included, pay after the session…',
spotsDesc: 'Select spots where you offer coaching sessions',
spotsLabel: 'Spots Where You Coach',
tagline: 'Share your skateboarding knowledge',
update: 'UPDATE COACH PROFILE',
updated: 'Coach profile updated!',
},
filmerForm: {
anonymous: 'Anonymous Filmer',
bioPlaceholder: 'Your background, style, equipment...',
camerasLabel: 'Cameras & Equipment * (select at least one)',
create: 'CREATE FILMING PROFILE',
created: 'Filming profile created!',
createPro: 'CREATE PRO FILMING PROFILE',
dmNote: "💬 Clients can message you directly through SkateSpots' encrypted messaging",
gearHelper: 'List your exact cameras, lenses and rigs — skaters care about the setup.',
gearLabel: "What's in the Bag (optional)",
gearPlaceholder: 'e.g. Sony VX1000 + MK1 fisheye, Panasonic HVX200, DJI RS3 gimbal, GoPro Hero 12, Sennheiser mic...',
heading: 'Become a Filmer',
headlineExample: 'Skate Filmer Berlin',
identity: 'Your Filmer Identity',
nip05Reason: "Add a NIP-05 identifier to verify your identity and build trust with clients — it shows them you're a legitimate filmer.",
paymentDesc: 'To receive payments from clients for filming services, you need to connect a Lightning wallet. Clients will zap you directly for your work.',
profileDesc: 'Tell clients about your filming expertise',
profileTitle: 'Filming Profile',
rate: 'Rate',
ratePlaceholder: '10000 sats/video',
reelLabel: 'Reel / Portfolio',
selectService: 'Select at least one service.',
selectSkill: 'Select at least one filming skill.',
servicesLabel: 'Services Offered * (select at least one)',
servicesRequired: 'Services required',
skillsRequired: 'Skills required',
spotsDesc: 'Select spots where you offer filming services',
spotsLabel: 'Spots Where You Film',
tagline: 'Offer your skate video filming — clips, lines, full parts & contest coverage',
update: 'UPDATE FILMING PROFILE',
updated: 'Filming profile updated!',
},
editorForm: {
anonymous: 'Anonymous Editor',
bioPlaceholder: 'Your editing style, background, notable projects…',
contactPortfolio: 'Contact & Portfolio',
create: 'Publish Editor Profile',
created: 'Editor profile created!',
createPro: 'Publish Pro Editor Profile',
editTypes: 'Edit Types Offered * (select at least one)',
hardware: 'Hardware',
hardwarePlaceholder: 'e.g. MacBook Pro M3, Sony FX3',
heading: 'Become a Video Editor',
headlineExample: 'Skate Video Editor',
identity: 'Your Identity',
locationOptional: 'Location (optional)',
loginDesc: 'Log in to create your video editor profile.',
phoneTelegram: 'Phone / Telegram',
portfolioPlaceholder: 'Portfolio (Vimeo, YouTube…)',
profileDesc: 'Tell the community about your editing style and skills',
profileTitle: 'Editor Profile',
published: 'Your profile has been published to SkateSpots.',
rate: 'Rate / Pricing',
ratePlaceholder: 'e.g. 50k sats/min, DM for quote',
selectService: 'Select at least one edit type.',
servicesRequired: 'Services required',
socialPlaceholder: 'Instagram / Social handle',
software: 'Software',
softwareHardware: 'Software & Hardware Skills',
softwarePlaceholder: 'e.g. DaVinci Resolve, Premiere',
tagline: 'Offer your video editing services to the skate community',
thumbnail: 'Profile / Showreel Thumbnail',
update: 'Update Editor Profile',
updated: 'Editor profile updated!',
walletConnected: 'Wallet Connected ✓',
walletDesc: 'Receive Bitcoin ⚡ payments directly from clients for your editing work.',
walletTitle: '⚡ Lightning Wallet for Payments',
yearsExperience: 'Years of Experience',
yearsPlaceholder: 'e.g. 5 years',
},
photoForm: {
anonymous: 'Anonymous Photographer',
bioPlaceholder: 'Your background, style, what you love shooting...',
cameraLabel: 'Camera Bodies * (select at least one)',
cameraRequired: 'Camera required',
contactDesc: 'How riders can see your work and reach you (all optional, but recommended)',
create: 'CREATE PHOTOGRAPHY PROFILE',
created: 'Photography profile created!',
createPro: 'CREATE PRO PHOTOGRAPHY PROFILE',
dmNote: "💬 Riders can message you directly through SkateSpots' encrypted messaging",
gearHelper: 'List exact bodies, lenses and lights — riders love knowing the setup.',
gearPlaceholder: 'e.g. Canon R6 II, 8-15mm fisheye, 70-200mm f/2.8, 2× Godox AD200, remote triggers, Manfrotto tripod...',
heading: 'Become a Photographer',
headlineExample: 'Skate Photography',
identity: 'Your Photographer Identity',
lensesLabel: 'Lenses',
lightingLabel: 'Lighting Gear',
nip05Reason: 'Add a NIP-05 identifier to verify your identity and build trust with clients.',
paymentDesc: 'To get paid for shoots, connect a Lightning wallet. Clients zap you directly for your work.',
portfolioLabel: 'Portfolio / Instagram',
profileDesc: 'Tell riders about your eye, your kit and your services',
profileTitle: 'Photography Profile',
ratePlaceholder: '20000 sats/shoot',
selectCamera: 'Select at least one camera type you shoot on.',
selectService: 'Select at least one service you offer.',
servicesLabel: 'Services Offered * (select at least one)',
servicesRequired: 'Services required',
spotsDesc: 'Select spots where you offer photography',
spotsLabel: 'Spots Where You Shoot',
styleLabel: 'Shooting Style',
supportLabel: 'Support & Rig',
tagline: 'Shoot the sessions — offer your skate photography',
update: 'UPDATE PHOTOGRAPHY PROFILE',
updated: 'Photography profile updated!',
},
builderForm: {
anonymous: 'Anonymous Builder',
bioLabel: 'Bio / About Your Builds',
bioPlaceholder: 'Your crew, notable builds, how you approach a project...',
buildTypesLabel: 'What You Build * (select at least one)',
buildTypesRequired: 'Build types required',
cityBase: 'City / Base',
contactPortfolio: 'Contact & Portfolio',
contactPortfolioDesc: 'How the scene can see your builds and reach you (all optional, but recommended)',
create: 'CREATE BUILDER PROFILE',
created: 'Builder profile created!',
createPro: 'CREATE PRO BUILDER PROFILE',
dmNote: "💬 Clients can message you directly through SkateSpots' encrypted messaging",
gearHelper: 'Crews and clients love knowing your kit and process.',
gearLabel: 'How You Work / Equipment (optional)',
gearPlaceholder: 'e.g. Full concrete crew, own mixer + shotcrete rig, mobile welding, we handle permits and drainage...',
heading: 'Become a Builder',
headlineExample: 'Ramp & Park Builder',
identity: 'Your Builder Identity',
insured: 'Licensed / Insured',
insuredHelper: 'Relevant for public park & council work',
logoPhoto: 'Logo / Photo',
materialsLabel: 'Materials & Trade Skills',
nip05Reason: 'Add a NIP-05 identifier to verify your identity and build trust with clients.',
pastBuilds: 'Past Builds / Instagram',
paymentDesc: 'To get paid for builds, connect a Lightning wallet. Clients zap you directly for your work.',
profileDesc: 'Tell the scene what you build and how you work',
profileTitle: 'Builder Profile',
rateLabel: 'Rate / Quote Basis',
ratePlaceholder: 'per m² / by project',
scaleLabel: 'Project Scale',
selectBuild: 'Select at least one thing you build.',
serviceArea: 'Service Area',
serviceAreaPlaceholder: 'e.g. Catalonia, will travel across EU',
spotsDesc: 'Link spots and parks you built so people can see your work on the map',
spotsLabel: "Spots You've Built",
tagline: 'Pour it, weld it, build it — list your ramp & park building',
update: 'UPDATE BUILDER PROFILE',
updated: 'Builder profile updated!',
yearsBuilding: 'Years Building',
},
skaterForm: {
backToSkaters: 'Back to Skaters',
bioHelperPulled: 'Pulled from your profile — tweak it for your skater card, or leave it as is.',
bioHelperShort: 'A short intro for your skater card.',
bioPlaceholder: "How long you've been skating, what you're into, what you're working on…",
browseSpots: 'Browse spots',
changePhoto: 'Change profile photo',
chipBio: 'Bio',
chipPhoto: 'Photo',
cityArea: 'City / area',
created: "You're on the map! 🛹",
custom: 'Custom',
customHelper: 'Your own label — shown on your skater card instead of a preset.',
customPlaceholder: 'e.g. OG, Weekend Warrior, Retired…',
discoverable: 'Other skaters can now discover you.',
editFullProfile: 'edit your full profile',
fromProfile: 'From your profile',
heroEditPrefix: 'Edit your',
heroJoinPrefix: 'Join the',
heroSuffix: 'Directory',
homeHelper: 'Type it freeform, or link a real spot from the map so people can go there.',
homePlaceholder: 'Local park / plaza — or tap “Browse spots”',
homeSpot: 'Home spot',
layerEnd: 'to change them everywhere.',
layerIntro: 'This is your',
layerMid: ', built on your Nostr profile. Your name, photo, bio, lightning & verification come from there —',
layerWord: 'skate layer',
level: 'Level',
linked: 'Linked to a spot on the map',
loadingProfile: 'Loading your profile…',
loadingSpots: 'Loading spots…',
locationHelper: 'Helps others find skaters near them. Only a coarse area is shared.',
loginToJoin: 'Log in to join',
loginToJoinDesc: 'Create your skater profile so the community can find you.',
mappedSpot: 'Mapped spot',
noSpots: 'No spots found yet.',
noSpotsMatch: 'No spots match your search.',
openMap: 'Open the map',
pickEverything: 'Pick everything that fits your style.',
pickHomeSpot: 'Pick your home spot',
pickStyles: 'Pick your styles',
pickStylesDesc: 'Choose at least one thing you skate.',
profilePic: 'Profile Pic',
profilePicHelper: 'Your skater card uses your Nostr profile photo — same picture everywhere.',
publishingAs: 'Publishing as',
putOnMap: 'Put me on the map',
searchSpots: 'Search spots by name or city…',
skateStory: 'Your skate story',
stance: 'Stance',
unlinkSpot: 'Unlink spot',
updateButton: 'Update my profile',
updated: 'Profile updated!',
whatYouSkate: 'What you skate *',
yearsPlaceholder: 'e.g. 8',
yearsSkating: 'Years skating',
yourProfilePic: 'Your profile pic',
zapsOn: 'Zaps on',
},
skaters: {
clearSearch: 'Clear search',
community: 'The Community',
countMany: 'skaters',
countOne: 'skater',
editProfile: 'Edit my skater profile',
heroDesc: 'Find your people. Browse the crew of skaters repping SkateSpots — by style, stance, level and city.',
nip05Verified: 'NIP-05 verified',
noMatches: 'No matches',
noneDesc: 'Be the first to join the directory and put SkateSpots on the map.',
noneTitle: 'No skaters yet',
noVerifiedMatch: 'No verified skaters match',
searchPlaceholder: 'Search by name, city or npub…',
tryDifferentSearch: 'Try a different search or clear your filters.',
tryTurnOffVerified: 'Try turning off the “Verified” filter to see everyone.',
verifiedFilterTitle: 'Show only skaters with a verified NIP-05 identity',
},
skaterCard: {
inDirectory: "You're in the Skater Directory",
joinDesc: 'Add your stance, styles & level so skaters can find you.',
joinTitle: 'Join the Skater Directory',
},
crewsStrip: {
allCrews: 'All crews',
memberMany: '{{count}} members',
memberOne: '{{count}} member',
title: 'Crews in this scene',
},
labLink: {
coach: 'Coach',
filmer: 'Filmer',
editor: 'Editor',
photographer: 'Photographer',
builder: 'Builder',
artist: 'Artist',
graffiti: 'Graffiti',
tattoo: 'Tattoo',
trainer: 'Trainer',
musician: 'Musician',
judge: 'Judge',
barber: 'Barber',
organizer: 'Organizer',
lab: 'Lab',
linkedProfiles: 'Linked Lab Profiles',
desc: 'Run a studio, coaching or build service too? Link your Lab listings so people can hire your crew. Optional. When the Lab profile also links back to this crew, both show a green',
descEnd: 'badge.',
empty: "You don't have any Lab profiles yet. Create one in The Lab to link it here.",
},
proUpgrade: {
goPro: 'Go Pro',
optional: '· optional',
freeTo: 'Free to list. Pro adds a',
proBadge: 'Pro badge',
featuredPlacement: 'featured placement',
atTop: 'at the top of The Lab.',
active: 'Active',
benefit1: 'Gold "Pro" badge on your profile & cards',
benefit2: 'Featured first when skaters browse The Lab',
benefit3: 'Supports SkateSpots — no ads, no middlemen',
platformOwner: 'Platform owner — Pro is always on.',
proActiveOne: 'Pro active · {{count}} day remaining. Your profile will be featured.',
proActiveMany: 'Pro active · {{count}} days remaining. Your profile will be featured.',
context: 'Your profile stays live for free after Pro expires — you just lose the badge & boost.',
},
profilePreview: {
viewPublic: 'View public',
unnamed: 'Unnamed skater',
noHandle: 'No handle yet',
},
pyramid: {
pyramidWord: 'PYRAMID',
rankingsWord: 'RANKINGS',
subtitle: 'Top contributors in your city',
yourRank: 'Your Rank',
keepContributing: 'Keep contributing to climb higher!',
topContributors: 'Top Contributors',
noContributors: 'No contributors yet. Be the first to add a spot or create an event!',
spots: 'Spots',
events: 'Events',
total: 'Total',
trust: 'Trust',
},
detail: {
joined: 'Joined',
about: 'About',
howWeWork: 'How We Work',
inTheBag: 'In the Bag',
hardware: 'Hardware',
software: 'Software',
eventsJudged: 'Events Judged',
eventsOrganized: 'Events Organized',
spots: 'Spots',
viewSpot: 'View spot',
credentialsTitle: 'Credentials & safety',
selfAttested: 'Self-attested by the coach — not verified by SkateSpots.',
formats: 'Formats',
levels: 'Levels',
ages: 'Ages',
languages: 'Languages',
availability: 'Availability',
bookSession: 'Book a session',
portfolio: 'Portfolio',
watchReel: 'Watch on {{platform}}',
paymentsTitle: 'Payments',
payWhatYouCan: 'Pay what you can',
bookingFee: 'Booking fee',
payBookingFee: 'Pay booking fee',
bookingFeeNote: 'Paid directly to the coach. SkateSpots doesn’t hold or refund it.',
bookingFeeNoRail: 'This coach has no Lightning address set, so in-app payment isn’t available — contact them to arrange payment.',
serviceArea: 'Service Area',
licensedInsured: 'Licensed / Insured',
studio: 'Studio',
shopStudio: 'Shop / Studio',
booksOpen: 'Books Open',
credentials: 'Credentials',
remoteOnline: 'Remote / Online available',
graffitiCrew: 'Graffiti Crew',
act: 'Act',
listen: 'Listen',
upcomingGigs: 'Upcoming Gigs',
past: 'Past',
experience: 'Experience',
yearsCount: '{{count}} years',
rate: 'Rate',
contact: 'Contact',
sendMessage: 'Send Message',
bookGig: 'Book for a Gig',
portfolioDomain: 'Portfolio · {{domain}}',
manage: 'Manage',
admin: 'Admin',
editProfile: 'Edit profile',
publishedTitle: "You're live",
publishedBody: 'Your profile is published to The Lab. Share it so skaters can find you.',
manageRemove: 'Remove your {{noun}} profile from The Lab.',
adminTakeDown: 'Take down this {{noun}} profile. It will be hidden across SkateSpots.',
entityLabel: '{{noun}} profile',
deleteProfile: 'Delete Profile',
location: 'Location',
servicesSkills: 'Services & Skills',
noun: {
coach: 'coach',
filmer: 'filmer',
editor: 'video editor',
photographer: 'photographer',
builder: 'builder',
artist: 'artist',
graffiti: 'graffiti artist',
tattoo: 'tattoo artist',
trainer: 'trainer',
musician: 'musician',
judge: 'event judge',
organizer: 'event organizer',
barber: 'barber',
profile: 'profile',
},
skillsTitle: {
skills: 'Skills',
servicesGear: 'Services & Gear',
whatWeBuild: 'What We Build',
whatTheyMake: 'What They Make',
stylesWork: 'Styles & Work',
styles: 'Styles',
focusSpecialties: 'Focus & Specialties',
genresInstruments: 'Genres & Instruments',
formatsSpecialties: 'Formats & Specialties',
servicesStyles: 'Services & Styles',
eventTypesServices: 'Event Types & Services',
},
},
rider: {
heroDesc: 'Your verifiable skater identity. Nostr-signed, print-ready, impossible to fake.',
heroDesc2: 'Earn witnessed skill badges from other skaters. Get park access passes. Share your card anywhere.',
headerTagline: 'Nostr-signed · Print-ready · QR-verified',
tabCard: 'My Card',
tabBadges: 'Badges',
tabAward: 'Award',
rendering: 'Rendering…',
shareCard: 'Share Card',
savePng: 'Save PNG',
print: 'Print',
copyId: 'Copy ID',
cardType: 'Card Type',
licenseLabel: 'Skater License',
licenseSub: 'Your personal rider identity',
parkLabel: 'Park Access',
parkSub: 'Issue passes as a park owner',
theme: 'Theme',
holoTitle: 'Holographic Shimmer',
holoDesc: 'Animated foil effect',
cardDetails: 'Card Details',
skateStyle: 'Skate Style',
ridesFor: 'Rides For',
ridesForPlaceholder: 'Local shop…',
sponsors: 'Sponsors',
sponsorsPlaceholder: 'Brands…',
showOnCard: 'Show on Card',
zaps: 'Zaps ⚡',
skills: 'Skills',
selected: '{{count}}/6 selected',
goldBadgesHint1: 'Gold badges on your card come from other skaters witnessing you — see the',
goldBadgesHint2: '.',
parkOwnersOnly: 'Park owners only.',
parkOwnersDesc: 'Issue this using the same Nostr key that published your park listing. Anyone can scan the QR and verify the signature on-chain — no central authority needed.',
parkDetails: 'Park Details',
parkName: 'Park Name',
parkNamePlaceholder: 'The Berrics…',
riderName: 'Rider Name',
riderNamePlaceholder: 'Name on card',
riderNpub: "Rider's Nostr ID (npub)",
accessLevel: 'Access Level',
validThru: 'Valid Thru',
publishToNostr: 'Publish to Nostr',
badgesIntro: 'These badges were witnessed and signed by other skaters on Nostr. They appear as gold ⭐ chips on your card.',
earnMore: 'Want to earn more? Skate in front of someone and ask them to witness your trick.',
awardSomeone: 'Award a Badge to Someone Else',
youSawIt: 'You saw it happen',
youSawItDesc: 'Sign the proof on Nostr. It becomes part of their permanent record — cryptographically tied to your key.',
skillsWitnessed: 'Skills you witnessed',
whereSeen: 'Where did you see it?',
optional: '(optional)',
signingOnNostr: 'Signing on Nostr…',
awardBadge: 'Award Badge',
addRiderSkill: 'Add rider npub + at least one skill',
invalidNpub: 'Invalid npub',
badgeAwarded: '🏅 Badge awarded!',
badgeSent: '{{skills}} sent.',
failed: 'Failed',
couldNotRender: 'Could not render card',
popupBlocked: 'Popup blocked',
popupBlockedDesc: 'Allow popups for this site.',
linkCopied: '🔗 Link copied!',
enterRiderNpub: "Enter the rider's npub",
parkPublished: '✅ Park Access Card published!',
publishFailed: 'Publish failed',
noBadges: 'No badges yet. Skate in front of someone and ask them to witness your tricks!',
showMore: 'Show {{count}} more',
witnessedBy: 'Witnessed by',
at: 'at',
previewLicense: 'Skater License',
previewSince: 'Since {{year}}',
previewEvts: 'Evts',
previewScan: 'Scan to verify',
previewSkatepark: 'Skatepark',
previewAccessPass: 'Access Pass',
previewMemberName: 'Member Name',
previewIssuedBy: 'Issued by park owner · Nostr-signed',
previewValidThru: 'Valid thru',
previewNostrVerified: 'Nostr-verified',
},
craftFields: {
detailsTitle: 'Additional details',
deliverables: 'Deliverables',
turnaround: 'Turnaround',
surfaces: 'Surfaces / Walls',
maxSize: 'Max Size',
setLength: 'Set Length',
techRider: 'Tech Rider',
travel: 'Travel',
certifications: 'Certifications',
insurance: 'Insurance',
permits: 'Permits',
deposit: 'Deposit',
minimum: 'Minimum',
modalities: 'Modalities',
duration: 'Appointment Length',
drone: 'Drone',
leadTime: 'Lead Time',
warranty: 'Warranty',
},
craftForm: {
profileImage: 'Profile image',
nameHeadline: 'Name / headline',
about: 'About',
aboutPlaceholder: 'Tell skaters about your {{noun}} work…',
required: 'required',
nip05Title: 'Add NIP-05 to build trust',
nip05Desc: "A NIP-05 identifier helps skaters verify you're the real deal. Highly recommended before listing.",
editingNote: 'Editing your existing {{noun}} profile — publishing replaces it.',
location: 'Location',
pinned: '📍 Pinned to map ({{lat}}, {{lng}})',
experienceYears: 'Experience (years)',
expPlaceholder: 'e.g. 5',
ratePlaceholder: 'e.g. 20k sats / by project',
contactLinks: 'Contact & Links',
phoneOptional: 'Phone (optional)',
emailOptional: 'Email (optional)',
socialPlaceholder: 'Social (Instagram, etc.)',
gettingPaid: 'Getting Paid',
updateProfile: 'Update Profile',
publishProfile: 'Publish Profile',
rateLeftBlankNote: 'Optional — leave this blank if the rate above is enough.',
sectionRequired: '{{section}} required',
sectionRequiredDesc: 'Select at least one option under "{{section}}".',
updated: 'Profile updated!',
created: 'Profile created!',
headlineExample: 'Skate Coach Berlin',
},
},
chromeUi: {
skipToContent: 'Skip to content',
menu: 'Menu',
inBadge: 'In',
loggedIn: 'Logged In',
profile: 'Profile',
logIn: 'Log In',
logOut: 'Log Out',
notLoggedIn: 'Not Logged In',
loggedOutPrompt: 'Log in to add spots, zap skaters, and save your favorites',
accounts: 'Accounts',
addAnotherAccount: 'Add another account',
setStatus: 'Set Status',
setYourStatus: 'Set Your Status',
status: 'Status',
addSpot: 'Add Spot',
myData: 'My Data',
myStash: 'My Stash',
myCollections: 'My Collections',
viewMyProfile: 'View my profile',
customizeNavBar: 'Customize nav bar',
ecash: 'Ecash',
madeWith: 'Made with',
forSkateCommunity: 'for the skateboarding community',
beta: 'Beta',
license: 'License',
privacy: 'Privacy',
collaborators: 'Collaborators & Friends',
notSponsoredBy: 'Not sponsored by',
errorTitle: 'Couldn’t load this',
errorDescription: 'The network request failed. Check your connection and try again.',
tryAgain: 'Try again',
weatherOn: 'Weather On',
weatherOff: 'Weather Off',
trustFilter: 'Trust Filter',
filterByTrustLevel: 'Filter by Trust Level',
trust: {
all: 'All Users',
allDesc: 'Show content from everyone',
verified: 'Verified',
verifiedDesc: 'You follow them or they are verified (trust ≥90)',
trusted: 'Trusted',
trustedDesc: 'Friends-of-friends or NIP-05 verified (trust ≥60)',
following: 'Following Only',
followingDesc: 'Only show content from people you follow',
followingShort: 'Following',
},
sort: {
recent: 'Most Recent',
recentDesc: 'Newest first',
trust: 'Highest Trust',
trustDesc: 'Most trusted creators',
proximity: 'Closest to Me',
proximityDesc: 'Nearest locations',
popular: 'Most Popular',
popularDesc: 'Most comments/reactions',
zaps: 'Most Zapped',
zapsDesc: 'Highest value received',
},
spotType: {
street: 'Street',
transition: 'Transition',
park: 'Park',
plaza: 'Plaza',
diy: 'DIY',
bowl: 'Bowl',
vert: 'Vert',
indoor: 'Indoor',
skatepark: 'Skatepark',
miniramp: 'Mini Ramp',
},
},
placesUi: {
addToCalendar: {
add: 'Add to Calendar',
appleOutlook: 'Apple / Outlook (.ics)',
google: 'Google Calendar',
},
attendees: {
goingCount: 'going',
goingTab: 'Going ({{count}})',
interestedTab: 'Interested ({{count}})',
nobodyGoing: 'Nobody has confirmed yet.',
nobodyInterested: 'Nobody marked interested yet.',
noRsvps: "No RSVPs yet — be the first to say you're going.",
trusted: 'Trusted',
trustedTitle: 'Someone you follow or a trusted mapper',
youTrust: 'you trust',
},
bounty: {
addressCoords: 'Address & coordinates',
basic: 'Basic',
contactInfo: 'Contact info',
description: 'Description',
earningPotential: 'Earning Potential',
excellent: 'Excellent!',
gettingStarted: 'Getting started',
good: 'Good',
onePhoto: 'At least 1 photo',
openingHours: 'Opening hours',
shopName: 'Shop name',
threePhotos: '3+ photos',
tip: '💡 Higher quality = More zaps from the community!',
},
calendar: {
allDay: 'All day',
eventDay: 'Event day',
eventsOnDay: '{{count}} events',
eventsThisMonth: 'events this month',
going: 'going',
months: {
january: 'January', february: 'February', march: 'March',
april: 'April', may: 'May', june: 'June',
july: 'July', august: 'August', september: 'September',
october: 'October', november: 'November', december: 'December',
},
nextMonth: 'Next month',
nothingOnDay: 'Nothing on this day.',
prevMonth: 'Previous month',
tapHighlighted: "Tap a highlighted day to see what's on.",
today: 'Today',
tourDay: 'Tour day',
toursOnDay: '{{count}} tours',
toursThisMonth: 'tours this month',
weekdays: { sun: 'Sun', mon: 'Mon', tue: 'Tue', wed: 'Wed', thu: 'Thu', fri: 'Fri', sat: 'Sat' },
weekdaysShort: { sun: 'S', mon: 'M', tue: 'T', wed: 'W', thu: 'T', fri: 'F', sat: 'S' },
},
countdown: {
ended: 'Ended',
happeningNow: 'Happening now',
inDays: 'In {{count}} days',
inHoursMins: 'In {{hours}}h {{mins}}m',
inMins: 'In {{count}} min',
startingNow: 'Starting now',
tomorrow: 'Tomorrow',
},
createEvent: {
add: 'Add',
addNewSpot: 'Add a new spot →',
changeSpot: 'Change Spot',
created: 'Event created!',
createdDesc: 'Your event has been published to the network.',
entryFee: 'Entry Fee',
entryFeeHint: '(optional — e.g. "500 sats" or "£10")',
entryFeePlaceholder: 'Leave blank for free events',
featuring: 'Featuring {{name}}',
featuringDesc: 'This act will be tagged as live music and surfaced on their gig list.',
fundingGoal: 'Funding Goal',
fundingGoalHint: '(optional — sats to crowdfund via zaps)',
fundingGoalPlaceholder: 'e.g. 50000',
geoNotSupported: 'Geolocation not supported',
geoNotSupportedDesc: 'Your browser does not support geolocation.',
hideSpots: 'Hide Spots',
hostingAs: 'Hosting as {{name}}',
hostingAsDesc: 'This event will show on your crew page and be tagged as crew-hosted.',
imageUploaded: 'Image uploaded',
imageUploadedDesc: 'Your image has been uploaded successfully.',
invalidDate: 'Invalid date',
invalidDateDesc: 'Please enter a valid start date and time.',
invalidEnd: 'Invalid end time',
invalidEndDesc: 'End time must be after start time.',
linkedTo: 'Linked to: {{title}}',
linkToSpot: 'Link to a Skate Spot',
linkToShop: 'Link to a Shop',
selectedShop: 'Linked to {{name}}',
selectShopHint: 'Optional — held at or hosted by a skate shop.',
selectShop: 'Select shop',
changeShop: 'Change shop',
hideShops: 'Hide shops',
searchShops: 'Search shops…',
noShopsMatch: 'No shops match your search.',
noShopsAvailable: 'No shops available yet.',
linkToTour: 'Link to a Tour (Optional)',
linkTourDesc: "Link this event to a tour to show it's part of a larger series. Great for national tours, international adventures, or World Destruction Tour challenge days.",
loadingSpots: 'Loading spots...',
loadingTours: 'Loading tours...',
locationError: 'Location error',
locationFound: 'Location found',
locationFoundDesc: 'Your current location has been set.',
logoUrl: 'Logo URL (optional)',
noSpotsAvailable: 'No spots available',
noSpotsMatch: 'No spots match your search',
noTourLinked: 'No tour linked',
optional: '(optional)',
pastDesc: 'Pick a date and time in the future so skaters can RSVP and show up.',
pastTitle: 'That start time is in the past',
publishFailed: 'Failed to publish',
publishFeatured: 'Publish Featured Event',
removeSponsor: 'Remove sponsor',
searchSpots: 'Search spots by name, location, or category...',
selectedSpot: 'Selected: {{name}}',
selectSpot: 'Select Spot',
selectSpotHint: 'Select a spot from the map where this event takes place',
selectTour: 'Select Tour',
somethingWrong: 'Something went wrong',
sponsorName: 'Sponsor name',
sponsors: 'Sponsors',
sponsorsHint: 'Got a shop or brand backing this? Add their logo — it shows as a "Presented by" strip on the event.',
startRequired: 'Start time required',
startRequiredDesc: 'Please enter when the event starts.',
titleRequired: 'Title required',
titleRequiredDesc: 'Please enter a name for this event.',
uploadFailed: 'Upload failed',
uploadFailedDesc: 'Failed to upload image',
websiteUrl: 'Website URL (optional)',
},
createShop: {
address: 'Shop Address',
addressPlaceholder: 'e.g., 411 N Fairfax Ave, Los Angeles, CA 90036',
addressRequired: 'Address required',
addressRequiredDesc: 'Please enter your shop address.',
contactHint: 'Help customers reach you (at least one contact method recommended)',
contactInfo: 'Contact Information',
created: '🎉 Shop added!',
createdDesc: 'Your skate shop is now on the map. Skaters can zap you to say thanks!',
descriptionPlaceholder: 'Tell people about your shop, what you sell, your specialty...',
detailsDesc: 'Tell the community about your shop. All fields marked with * are required.',
email: 'Email',
hoursOfOperation: 'Hours of Operation',
hoursPlaceholder: 'e.g., Mon-Sat 10AM-8PM, Sun 12PM-6PM',
instagram: 'Instagram',
invalidCoords: 'Invalid coordinates',
invalidCoordsDesc: 'Please enter valid latitude (-90 to 90) and longitude (-180 to 180).',
latPlaceholder: 'Latitude (e.g., 34.0737)',
lngPlaceholder: 'Longitude (e.g., -118.3614)',
locationRequired: 'Location required',
locationRequiredDesc: 'Please enter coordinates or use your current location.',
loginToAdd: 'You need to log in to add a skate shop.',
namePlaceholder: 'e.g., Supreme Los Angeles',
nameRequired: 'Shop name required',
nameRequiredDesc: 'Please enter a name for your shop.',
phoneNumber: 'Phone Number',
publishFailedDesc: 'Something went wrong. Check console for details.',
shopPhotoAlt: 'Shop',
shopPhotos: 'Shop Photos',
subtitle: 'List your shop on the map and connect with the community',
upload: 'Upload',
},
createTour: {
acceptedClipSources: 'Accepted Clip Sources',
acceptedClipSourcesDesc: 'What formats will you accept for clip submissions?',
accommodationInfo: 'Accommodation Info',
accommodationPlaceholder: "Accommodation recommendations or arrangements, e.g., 'Budget hostels along the route', 'Camping gear required', 'Airbnbs included'",
addChallenge: 'Add Challenge',
addCity: 'Add a city...',
addCountry: 'Add a country...',
addShopFirst: 'Add a shop first',
alsoSelected: 'Also selected:',
availabilityPlaceholder: 'e.g., Weekends, Mornings only, By appointment',
availabilityQuestion: 'When are you usually available?',
availableShops: 'Available Skate Shops ({{count}})',
challengeDescPlaceholder: 'Optional description...',
challenges: 'Challenges',
challengesRequired: 'Challenges required',
challengesRequiredDesc: 'Please add at least one challenge for teams to complete.',
challengesSecret: 'All challenges are SECRET until competition start date. Teams will not see them until then!',
challengeTitlePlaceholder: "Challenge title, e.g., 'Kickflip a 10 stair'",
citiesLabel: 'Cities',
citiesOnTour: 'Cities on the Tour',
cityPlaceholder: 'e.g., Berlin, Germany',
cityRegion: 'City / Region',
cityRequired: 'City/region required',
cityRequiredDesc: 'Please enter your city or region.',
clipAnyUrl: '🔗 Any URL',
clipBlossom: '📦 Blossom Upload',
clipInstagram: '📸 Instagram',
clipYouTube: '▶️ YouTube',
competitionDates: 'Competition Dates',
competitionDatesWarning: '⚠️ All challenges are SECRET until the start date! Teams will not see challenges until competition begins.',
competitionLocations: 'Competition Locations',
competitionLocationsDesc: 'Where will teams be competing? Add all cities and countries involved.',
coordsHint: 'Coordinates are optional. Add them to pin your meeting point on the map.',
countriesLabel: 'Countries',
countriesRequired: 'Countries required',
countriesRequiredDesc: 'Please add at least one country for your international tour.',
createdDestruction: '💀 WORLD DESTRUCTION TOUR CREATED!',
createdDestructionDesc: 'Teams can register. Challenges revealed on start date!',
createDestructionTour: 'Create World Destruction Tour',
createdInternational: '✈️ International Tour Created!',
createdInternationalDesc: 'Your global adventure is listed! Connect with skaters worldwide.',
createdLocal: '🏠 Local Tour Created!',
createdLocalDesc: 'Your tour is now visible. Skaters can book via DM!',
createdNational: '🚐 National Tour Created!',
createdNationalDesc: 'Your road trip is live! Start coordinating with interested skaters.',
departure: 'Departure *',
departureRequired: 'Departure date required',
departureRequiredDesc: 'Set when the trip departs so skaters can plan and book.',
descriptionPlaceholder: "Describe what skaters will experience: spots you'll visit, vibe, what makes your tour unique...",
destructionDesc: 'Set up your global KOTR-style competition - you define EVERYTHING!',
discountHint: 'Tour participants can show their booking confirmation at the shop to receive this discount',
discountPercentage: 'Discount Percentage',
dropTour: 'Drop a Tour',
duration: 'Duration',
durationHintDestruction: 'How long is the competition? (1-12 months)',
durationHintInternational: 'How many weeks is the adventure? (up to 2 months)',
durationHintLocal: 'How long is your typical tour?',
durationHintNational: 'How many days is the road trip?',
durationWeeksMonths: '{{weeks}} weeks (~{{months}} months)',
editTour: 'Edit Tour',
enable: 'Enable',
enabled: 'Enabled',
endBeforeStart: 'End before start',
endBeforeStartDesc: 'The competition end date must be on or after the start date.',
endDate: 'End Date',
entryFeeHint: 'You keep 95% • Platform takes 5% • Leave empty for free entry',
entryFeePerTeam: 'Entry Fee per Team (sats)',
entryFeePlaceholder: 'e.g., 100000',
entryPrizes: 'Entry & Prizes',
filmPremieres: 'Team Film Premieres',
filmPremieresDesc: "After the competition, each team's clips become a full film that can premiere at local skate shops worldwide.",
filmPremieresInfo: '🎥 Teams compile their challenge clips into a final film. Shops can host premiere events, connecting global competition to local communities.',
filmReleaseDate: 'Film Release Date (Optional)',
filmReleaseDateHint: 'When films will be available. Leave blank to release immediately after competition ends.',
fixedSession: 'Fixed session',
fixedSessionDesc: 'A set date & time',
flexible: 'Flexible',
flexibleDesc: 'Book anytime by DM',
hide: 'Hide',
howDestruction1: '• Global team competition - up to 12 months long',
howDestruction2: '• YOU define challenges, points, prizes - we just provide the platform',
howDestruction3: '• All challenges are SECRET until competition start date',
howDestruction4: '• Clips via Blossom upload OR external links (YouTube, Instagram, any URL)',
howDestruction5: '• Advanced level only - all spot types included automatically',
howDestruction6: '• Platform takes only 5% of entry fees (NOT prize pool)',
howDestructionTitle: '💀 WORLD DESTRUCTION TOUR - KOTR Style',
howInternational1: '• Cross-border adventures across multiple countries',
howInternational2: '• You set the price — platform takes only 5%',
howInternational3: '• Set departure & return dates so the crew can book flights',
howInternational4: '• Connect skaters from around the world',
howInternational5: '• Create unforgettable global experiences!',
howInternationalTitle: 'How International Tours Work',
howItWorks: 'How it works:',
howLocal1: '• You set your price — platform takes 10%',
howLocal2: '• Hidden spots stay hidden — you show them in person',
howLocal3: '• Run a scheduled session or keep it flexible / by-appointment',
howLocal4: '• Skaters book via DM, pay in Lightning & can add it to their calendar',
howLocal5: '• Build your rep with reviews and zaps',
howLocalTitle: 'How Local Tours Work',
howNational1: '• Multi-city road trips across your country',
howNational2: '• You set the price — platform takes only 7%',
howNational3: '• Set trip dates so skaters can plan time off & travel',
howNational4: '• Include transport / accommodation info for the crew',
howNational5: '• Build legendary skate adventures!',
howNationalTitle: 'How National Tours Work',
imageUploadedDesc: 'Your tour image has been uploaded.',
incompleteCoords: 'Incomplete coordinates',
incompleteCoordsDesc: 'Please enter both latitude and longitude, or leave both empty.',
internationalDetails: 'International Adventure Details',
languagesSelected: '{{count}} selected',
languagesSpoken: 'Languages Spoken',
latOptional: 'Latitude (optional)',
lngOptional: 'Longitude (optional)',
loadingProfile: 'Loading Profile...',
loadingProfileDesc: 'Checking your NIP-05 verification status.',
loadingShops: 'Loading shops...',
localShopPremieres: 'Local Shop Premieres',
localShopPremieresDesc: 'Skate shops worldwide can host film nights',
loginToCreate: 'You need to log in to create a tour.',
maxGroupSize: 'Max Group Size',
maxTeams: 'Max Teams',
maxTeamSize: 'Max Team Size',
meetingLocationSet: 'Your current location has been set as meeting point.',
meetingPointDesc: 'Where will you meet tour participants? Choose a public, well-known spot.',
meetingPointLabel: 'Meeting Point',
meetingPointPlaceholder: 'e.g., Warschauer Straße Station',
meetingPointRequired: 'Meeting point required',
meetingPointRequiredDesc: 'Please enter where you will meet tour participants.',
minTeamSize: 'Min Team Size',
noChallenges: 'No challenges yet',
noChallengesDesc: 'Add challenges for teams to complete',
noPartnerShop: 'No partner shop',
noShopsFound: 'No skate shops found on the network yet.',
noShopsRegistered: 'No skate shops registered yet',
onlineRelease: 'Online Release',
onlineReleaseDesc: 'Also release films on YouTube/Nostr',
organizerContact: 'Organizer Contact Details',
organizerContactHint: 'This will be visible to registered teams',
organizerContactPlaceholder: 'How can teams contact you? e.g., Nostr npub, email, Telegram, etc.',
partnerDiscountDesc: 'Offer tour participants a discount at a local skate shop',
partnerStep1: '1. Partner with a local skate shop you trust',
partnerStep2: '2. Set a discount percentage for tour participants',
partnerStep3: '3. After the tour, give participants a confirmation to show at the shop',
partnerStep4: '4. Shop honors the discount - builds community & drives foot traffic!',
percentOff: '{{percent}}% OFF',
photoHint: 'Add a photo of yourself skating or a cool spot from your tour.',
pickRide: 'Pick your ride',
place1: '🥇 1st',
place2: '🥈 2nd',
place3: '🥉 3rd',
platformFee: 'You set the price • Platform takes {{percent}}',
points: 'Points:',
pricePerPerson: 'Price per Person (sats)',
pricePlaceholder: 'e.g., 25000',
prizeDistribution: 'Prize Distribution',
prizePoolHint: 'Optional - total prizes available',
prizePoolPlaceholder: 'e.g., 2000000',
proTipDesc: "Before listing a partner discount, contact the shop owner via DM to agree on the discount percentage. This ensures they're on board and will honor the discount for your tour participants.",
proTipTitle: '💡 Pro Tip: Coordinate with the shop first!',
return: 'Return',
returnBeforeDeparture: 'Return before departure',
returnBeforeDepartureDesc: 'The return date must be on or after the departure date.',
roadTripDetails: 'Road Trip Details',
schedule: 'Schedule',
scheduleDesc: 'Run this as a one-off scheduled session, or keep it flexible and let skaters book a time by DM.',
secretUntilStart: 'Secret until start',
selectPartnerShop: 'Select Partner Shop',
sessionDateRequired: 'Session date required',
sessionDateRequiredDesc: 'Pick the date and time for your scheduled session, or switch to a flexible schedule.',
sessionDateTime: 'Session date & time',
sessionDateTimeHint: 'Skaters will see this time and can add it to their calendar from the tour page.',
shopPremieresContact: 'Contact these shops directly to arrange film premiere nights. All arrangements are private.',
shopPremieresHow: '💡 How Shop Premieres Work: Contact shops directly to arrange premiere nights. All arrangements are private between you and the shops.',
shopsCanRegister: 'Shops can register at /create-shop',
show: 'Show',
showAllLanguages: 'Show all {{count}} languages',
somethingWrong: 'Something went wrong.',
spotTypes: 'Types of Spots',
spotTypesRequired: 'Spot types required',
spotTypesRequiredDesc: 'Please select at least one type of spot you will show.',
startDate: 'Start Date',
startDateRequired: 'Start date required',
startDateRequiredDesc: 'Please set when the competition starts (challenges will be revealed on this date).',
startingCity: 'Starting City',
startingCityPlaceholder: 'e.g., Los Angeles, USA',
teamSettings: 'Team Settings',
teamsCount: '{{count}} teams',
titlePlaceholder: 'e.g., Berlin Kreuzberg - Underground Spots Tour',
titleRequired: 'Tour title required',
titleRequiredDesc: 'Please enter a name for your tour.',
totalPercent: 'Total: {{percent}}%',
totalPrizePool: 'Total Prize Pool (sats)',
tourDesc: "Tell skaters what to expect on your tour",
tourDetails: 'Tour Details',
tourPhoto: 'Tour Photo',
tourTitle: 'Tour Title',
transportDestructionPlaceholder: 'How will teams travel between locations? Any transport provided or recommended?',
transportInfo: 'Transport Info',
transportPlaceholder: "How will participants travel? e.g., 'Van provided', 'Fly into Barcelona, we rent a van', 'Train between cities'",
tripDatesDesc: 'Lock in the dates so skaters can request time off, book flights, and plan the trip.',
tripDatesTitle: 'Trip Dates',
updated: 'Tour updated!',
updatedDesc: 'Your changes have been published.',
upload: 'Upload',
view: 'View →',
worldDestruction: 'World Destruction Tour',
youReceive: 'You receive: {{amount}} sats per person',
},
editEvent: {
accessDenied: 'Access Denied',
accessDeniedDesc: 'You can only edit events that you created.',
backToEvent: 'Back to Event',
detailsDesc: 'Update the information about this event. All fields marked with * are required.',
eventCover: 'Event cover',
locationUpdated: 'Location updated.',
loginToEdit: 'You need to log in to edit this event.',
newSpot: 'New spot: {{location}}.',
newTime: 'New time: {{date}} at {{time}}.',
saveChanges: 'Save Changes',
saving: 'Saving...',
title: 'Edit Event',
updated: 'Event updated!',
updatedDesc: 'Your changes have been published to the network.',
updateDetails: 'Update the details for {{title}}',
updatedNotified: 'Changes published — {{count}} attendees notified.',
updateFailed: 'Failed to update',
updateMessage: '{{parts}} — see the event for details.',
},
editShop: {
accessDeniedDesc: 'You can only edit shops that you created.',
backToShop: 'Back to Shop',
detailsDesc: 'Update your shop information. All fields marked with * are required.',
loginToEdit: 'You need to log in to edit this shop.',
updated: 'Shop updated!',
updateDetails: 'Update the details for {{name}}',
},
editTour: {
accessDeniedDesc: 'You can only edit tours you created.',
loginToEdit: 'You need to log in to edit this tour.',
},
eventCard: {
report: 'Report',
},
eventDetail: {
aCrew: 'a crew',
addToCalendar: 'Add to calendar',
backToEvents: 'Back to Events',
cancelled: 'This event was cancelled',
checkUpdate: '— check the update',
copyFailed: 'Copy failed',
date: 'Date',
editEvent: 'Edit event',
eventFallback: 'Event',
eventOrganizer: 'Event Organizer',
getDirections: 'Get Directions',
linkCopied: 'Link copied',
linkCopiedDesc: 'Event link copied to your clipboard — paste it anywhere.',
live: 'Live:',
liveMusic: 'live music',
location: 'Location',
newLocation: 'New location',
noRsvps: 'No RSVPs were recorded.',
organizer: 'Organizer',
rsvpsClosed: 'RSVPs are closed — this event was cancelled by the host.',
seeMap: 'See map',
shareEvent: 'Share event',
shareText: '{{title}} — a skate event on SkateSpots',
shareTextFallback: 'Check out this skate event on SkateSpots',
time: 'Time',
timeChanged: 'Time changed',
updatesFromHost: 'Updates from the host',
where: 'Where',
whoCame: 'Who came',
whosGoing: "Who's going",
},
events: {
calendar: 'Calendar',
createTour: 'Create Tour',
events: 'Events',
faqFooter: 'Have questions? Check the FAQ →',
hosting: 'Hosting',
hostingSubtitle: 'Your events — RSVPs, updates & quick edits',
list: 'List',
noEventsMatchFilters: 'No events match your filters. Try adjusting or clearing them.',
noPast: 'No past events',
noPastBody: 'No past skate events found in your current filters.',
noTours: 'No tours found',
noToursBody: 'No skate tours yet. Create the first one and take skaters on a spot discovery!',
noToursMatchFilters: 'No tours match your filters. Try adjusting or clearing them.',
noUpcoming: 'No upcoming events',
noUpcomingBody: 'No upcoming skate events found. Be the first to create one!',
past: 'Past',
resultsCount: '{{count}} results',
searchCity: 'City…',
searchEvent: 'Event name…',
searchTour: 'Tour name…',
tours: 'Tours',
toursSubtitle: 'Local guides, road trips & world destruction competitions',
upcoming: 'Upcoming',
},
eventTypes: {
contest: 'Contest',
demo: 'Demo',
jam: 'Jam',
meetup: 'Meetup',
premiere: 'Premiere',
session: 'Session',
workshop: 'Workshop',
},
featured: {
active: 'Active',
bodyEvent: 'Free to post. Featured events get a badge and sit at the top of the events page.',
bodyTour: 'Free to post. Featured tours get a badge and sit at the top of the tours list.',
contextEvent: 'Your event stays listed for free either way — featured just adds the badge & boost.',
contextTour: 'Your tour stays listed for free either way — featured just adds the badge & boost.',
daysRemaining: 'Featured active · {{count}} days remaining.',
headingEvent: 'Feature this event',
headingTour: 'Feature this tour',
optional: 'optional',
platformOwner: 'Platform owner — featured is always on.',
},
filters: {
activeCount: '{{count}} active',
all: 'All',
allLevels: 'All Levels',
eventType: 'Event Type',
following: 'Following',
newest: 'Newest',
popular: 'Popular',
showResults: 'Show Results',
showResultsActive: 'Show Results · {{count}} active',
skillLevel: 'Skill Level',
sortBy: 'Sort By',
topRated: 'Top Rated',
tourCategory: 'Tour Category',
trusted: 'Trusted',
trustLevel: 'Trust Level',
verified: 'Verified',
},
food: {
bar: 'Bar',
cafe: 'Café',
call: 'Call',
chooseSpot: 'Choose a spot...',
directions: 'Directions',
fastFood: 'Fast Food',
foodCourt: 'Food Court',
locationLabel: 'Location:',
noneFound: 'No Food Spots Found',
noneFoundDesc: "We couldn't find any restaurants or cafés within 1km of this spot. Try selecting a different location.",
placesFound: '{{count}} places found within 1km of {{spot}}',
pub: 'Pub',
restaurant: 'Restaurant',
selectSpot: 'Select a skate spot',
selectSpotDesc: 'Choose a skate spot from the dropdown above to find nearby restaurants, cafés, and other food options.',
selectSpotTitle: 'Select a Skate Spot',
subtitle: 'Find places to eat near your favorite skate spots',
title: 'Food Near Spots',
unknown: 'Unknown',
website: 'Website',
},
rsvp: {
areYouGoing: 'Are you going?',
cant: "Can't",
cantMakeIt: "Can't make it",
going: "I'm Going",
goingCount: 'going',
goingShort: 'Going',
hostNote: "You're hosting this — but you can still set your status.",
interested: 'Interested',
interestedCount: 'interested',
loginToRsvp: 'Log in to RSVP.',
maybe: 'Maybe',
toastError: 'Could not RSVP',
toastGoing: "You're going! 🛹",
toastInterested: 'Marked as interested.',
toastNotGoing: 'Marked as not going.',
tryAgain: 'Please try again.',
},
shop: {
addNip05Settings: 'Add NIP-05 in Settings',
adminRemovedReason: 'SkateSpots admin removed shop "{{name}}"',
areYouOwner: 'Are You the Shop Owner?',
backToMap: 'Back to Map',
claimDesc: 'If you own or manage this shop, you can claim it to verify ownership using NIP-05.',
claimNip05Intro: "To claim this shop, add a NIP-05 identifier that matches the shop's website domain. For example, if the shop website is",
claimNip05Your: 'your NIP-05 should be',
contactShop: 'Contact Shop',
deleted: 'Shop deleted',
deletedDesc: 'Deletion request sent to relays. Most relays will remove this content.',
deleteDialogDesc: 'This will permanently delete "{{name}}" from all relays. This action cannot be undone.',
deleteDialogTitle: 'Delete Shop?',
deletedReason: 'Deleted shop',
deleteFailed: 'Delete failed',
deleteShop: 'Delete Shop',
linkCopied: 'Link copied!',
linkCopiedDesc: 'Shop link copied to clipboard.',
nip05Required: '⚠️ NIP-05 verification required to claim shops',
opens: 'Opens: {{domain}}',
removeAdmin: 'Remove (admin)',
removed: 'Shop removed',
removedDesc: 'This shop has been taken down and will be hidden across SkateSpots.',
removeDialogDesc: 'As a SkateSpots admin you are taking down "{{name}}". It will be hidden across the app for everyone and a deletion request will be sent to relays.',
removeDialogTitle: 'Remove this shop? (admin)',
removeShop: 'Remove Shop',
tipMapper: 'Tip the Mapper',
tipMapperDesc: 'This shop was added by a community member. Zap them to say thanks!',
updateNip05: 'Update NIP-05 to Claim',
viewPhotoFull: 'View photo full screen',
viewPhotoOf: 'View photo {{index}} of {{total}}',
},
sponsors: {
presentedBy: 'Presented by',
},
tourCard: {
active: 'Active',
completed: 'Completed',
contactForPrice: 'Contact for price',
durationDays: '{{count}} days',
durationHours: '{{count}} hours',
durationMonths: '{{count}} months',
durationWeeks: '{{count}} weeks',
entry: 'Entry: {{amount}} sats',
happeningNow: 'Happening now',
inDays: 'In {{count}} days',
inHours: 'In {{count}}h',
inMonths: 'In {{count}} months',
inWeeks: 'In {{count}} weeks',
maxGroup: 'Max {{count}}',
price: '{{amount}} sats',
prizePool: '{{amount}} sats',
registrationOpen: 'Registration Open',
startingSoon: 'Starting soon',
teamsMax: '{{count}} teams max',
tomorrow: 'Tomorrow',
worldwide: 'Worldwide',
},
tourDetail: {
about: 'About This Tour',
availability: 'Availability',
competitionWindow: 'Competition Window',
contactToBook: 'Contact Guide to Book',
discountAt: '{{percent}}% OFF at {{shop}}',
duration: 'Duration',
languages: 'Languages',
loginToBook: 'Login to Book',
loginToBookDesc: 'Login to contact the guide and book this tour',
maxGroup: 'Max Group',
meetingPoint: 'Meeting Point',
notFound: 'Tour Not Found',
notFoundDesc: "This tour may have been removed or doesn't exist.",
organizer: 'Organizer',
partnerDiscount: 'Partner Shop Discount',
partnerDiscountDesc: 'Book this tour to receive a discount at the partner skate shop. Show your booking confirmation to the shop staff.',
people: '{{count}} people',
perPerson: 'per person',
price: 'Price',
runsAbout: 'Runs about {{duration}}',
scheduledSession: 'Scheduled Session',
shareText: 'Check out this local skate tour: {{title}}',
skillLevel: 'Skill Level',
spotTypes: 'Types of Spots',
tourGuide: 'Tour Guide',
tourSponsors: 'Tour Sponsors',
tripDates: 'Trip Dates',
viewProfile: 'View Profile',
},
updates: {
admin: 'Admin',
adminNote: "You're posting as a SkateSpots admin. It'll be clearly labelled as an admin notice, not from the host.",
kindCancelled: 'Cancelled',
kindNewSpot: 'New spot',
kindNewTime: 'New time',
kindUpdate: 'Update',
loading: 'Loading updates…',
notifiedCount: '{{count}} people notified.',
notifiedEveryone: 'Everyone will see it on the event.',
notifiesRsvps: 'Notifies {{count}} RSVPs',
noUpdates: "No updates from the host yet. RSVP and you'll be notified if anything changes.",
placeholder: 'Rain — moved to Sunday 2pm. Meet at the back ledge…',
post: 'Post',
postAdminUpdate: 'Post an admin update',
posted: 'Update posted',
postError: 'Could not post update',
postUpdate: 'Post an update',
tryAgain: 'Please try again.',
visibleToEveryone: 'Visible to everyone on this event',
},
destination: {
bitcoin: 'Bitcoin',
closeSearch: 'Close search',
nothingMatches: 'Nothing matches “{{query}}”.',
placeholder: 'Search spots, shops, Bitcoin merchants…',
shop: 'Shop',
spot: 'Spot',
typeChars: 'Type at least 2 characters. Searches skate spots, skate shops, and Bitcoin merchants.',
typeCharsNear: 'Type at least 2 characters. Searches skate spots, skate shops, and Bitcoin merchants near here.',
},
dateWindow: {
any: 'Any',
anytime: 'Anytime',
month: 'This Month',
monthShort: 'Month',
today: 'Today',
todayShort: 'Today',
week: 'This Week',
weekend: 'Weekend',
weekendShort: 'Wknd',
weekShort: 'Week',
},
},
socialUi: {
agentPanel: {
spot: 'Spot',
event: 'Event',
labPro: 'Lab pro',
gear: 'Gear',
crew: 'Crew',
description: 'Knows this crew — its jams, roster & open call — plus the wider scene with live data.',
greeting: "Yo! I'm Sasquatch, {{crew}}'s guide. Ask me about the crew — our next jam, who runs it, what we're after — or the wider scene.",
suggestion1: "What's our next jam?",
suggestion2: 'Who runs this crew?',
suggestion3: 'What are we looking for?',
suggestion4: 'Best spots near us?',
suggestion5: "What's the weather for skating?",
thinking: 'Sasquatch is thinking…',
placeholder: 'Ask about your local scene…',
disclaimer: 'AI can be wrong — links go to real listings.',
},
badge: {
upcomingOne: '{{count}} upcoming event',
upcomingMany: '{{count}} upcoming events',
comingOne: '{{count}} event coming',
comingMany: '{{count}} events coming',
},
bell: {
youAreIn: "You're in {{crew}}! 🛹",
membershipConfirmed: 'Membership confirmed.',
couldNotConfirm: 'Could not confirm',
addedYouTo: 'added you to',
confirmToAppear: 'Confirm to appear as a member.',
notifications: 'Notifications',
notificationsNew: 'Notifications ({{count}} new)',
unreadCount: '{{count}} unread notifications',
markRead: 'Mark read',
allClear: 'All clear',
tabAll: 'All',
tabTrades: 'Trades',
tabSocial: 'Social',
tabNews: 'News',
tabCrews: 'Crews',
checkingUpdates: 'Checking for updates…',
checkingTrades: 'Checking offers and trades…',
loadingDrops: 'Loading the latest drops…',
emptyAll: 'Offers, trades, zaps, replies, newsletter drops and crew activity will show up here.',
emptyTrades: "Offers, counter-offers and trade confirmations all land here — whether you're selling, buying or swapping.",
emptySocial: 'Zaps, reactions, reposts, mentions, replies, event RSVPs and host updates all land here.',
emptyNews: 'SkateSpots newsletter drops will land here. Subscribe on the Newsletter page.',
emptyCrews: 'Crew invites, collective requests and membership requests will show up here.',
subscribeBanner: 'Subscribe to get new drops in your bell',
read: 'Read',
allCaughtUp: "You're all caught up",
},
chat: {
hidden: 'Hidden',
hideMessageAria: 'Hide this message',
hideMessageTitle: 'Hide message (admin)',
messageHidden: 'Message hidden',
messageHiddenDesc: 'Hidden from the crew chat for everyone.',
messageHideFailed: 'Failed to hide message',
encryptionUnavailable: 'Encryption unavailable',
encryptionUnavailableDesc: 'Your signer does not support NIP-44 encryption. Please upgrade your extension.',
messageFailed: 'Message failed',
couldNotSend: 'Could not send message.',
noOneToMessage: 'No one to message yet',
noOneToMessageDesc: 'This crew has no other members to chat with.',
subject: '{{crew}} crew chat',
title: 'Crew Chat',
cordnBadgeTitle: "Routed through this crew's Cordn coordinator over ContextVM. End-to-end encrypted.",
private: 'Private',
privateBadgeTitle: 'End-to-end encrypted — only crew members can read these messages.',
encryptedCordn: 'Encrypted & routed through the crew coordinator (Cordn) — no messages hit public relays.',
encryptedNip17: 'Encrypted group chat for the founder & all listed crew members.',
moderatorNote: ' You can hide messages from the crew as a moderator.',
loginToSee: 'Log in to see if you can join the crew chat.',
membersOnly: 'This chat is private to crew members. Ask the founder to add you to the crew to join the conversation.',
noMessages: 'No messages yet. Kick off the crew chat — plan a session, share a spot, hype a clip.',
newMessages: 'New messages',
new: 'New',
placeholder: 'Message your crew…',
sendAria: 'Send message',
},
collectiveJoinRequest: {
addedTitle: 'Added {{name}} to {{collective}} 🛹',
addedDesc: "They'll confirm from their side to appear as a verified member.",
couldNotAdd: 'Could not add member',
couldNotDismiss: 'Could not dismiss',
addedDone: 'Added',
addedTo: 'to',
dismissedDone: 'Dismissed request from {{name}}',
wantsToJoin: 'wants to join',
alreadyOnRoster: 'Already on the roster',
openCollective: 'Open collective',
dismiss: 'Dismiss',
addToCollective: 'Add to collective',
},
collectiveMembers: {
confirmed: 'Confirmed',
pending: 'Pending',
pendingTooltip: 'Listed by the collective — awaiting this member’s confirmation.',
crewOnly: 'Crew only',
crewOnlyTooltip: "This member chose to be visible only to crew-mates. You can see them because you're in the crew.",
hiddenMember: 'Hidden member',
title: 'Members',
verifiedOf: '{{confirmed}} of {{total}} verified — each confirmed member signed with their own Nostr key.',
verifiedPerMember: 'Membership is verified per member. Each member confirms with their own Nostr key.',
adminsOne: '{{count}} admin helps run the crew.',
adminsMany: '{{count}} admins help run the crew.',
},
confirmMembership: {
public: 'Public',
crewOnly: 'Crew only',
confirmedTitle: 'Membership confirmed!',
confirmedCrew: "You're verified in {{name}} — visible to crew-mates only.",
confirmedPublic: 'You now show as a verified member of {{name}}.',
failed: 'Failed to confirm',
addedTo: "You've been added to {{name}}",
addedDesc: 'Confirm your membership so it shows as verified on your profile and theirs.',
yourVisibility: 'Your visibility',
confirming: 'Confirming…',
confirmButton: 'Confirm Membership',
},
contacts: {
message: 'Message',
removePrivate: 'Remove from private contacts',
savePrivate: 'Save to private contacts',
seoTitle: 'Contacts - SkateSpots',
seoDescription: 'Your scene — crew-mates, people you follow and recent conversations, with private encrypted contacts.',
encryptionUnavailableDesc: 'Your signer does not support NIP-44 encryption.',
removed: 'Removed from private contacts',
saved: 'Saved to private contacts',
savedDesc: 'Only you can see this list.',
updateFailed: 'Could not update contacts',
loginTitle: 'Your scene, in one place',
loginDesc: 'Log in to see your crew-mates, people you follow, and recent conversations — and keep a private, encrypted contacts list.',
title: 'Contacts',
subtitle: 'Your scene — crew-mates, follows & conversations',
filterPlaceholder: 'Filter by npub…',
tabCrew: 'Crew',
tabFollows: 'Follows',
tabDms: 'DMs',
tabSaved: 'Saved',
emptyCrewmates: 'No crew-mates yet. Join or start a crew to build your scene network.',
emptyFollowing: "You're not following anyone yet.",
emptyDms: 'No conversations yet. Message a skater to start one.',
savedHint: 'This list is encrypted to your key — only you can read it. Tap the star on anyone to add them.',
emptySaved: 'No saved contacts yet. Star people from any tab to build your private address book.',
},
conversation: {
unknown: 'Unknown',
seoTitle: 'Chat with {{name}} - SkateSpots',
seoDescription: 'Private encrypted conversation',
sendFailed: 'Message failed to send',
sendFailedDesc: 'Please try again in a moment.',
tradeConfirmed: 'Trade confirmed',
tradeConfirmedDesc: 'Reputation updated for both of you.',
tradeConfirmFailed: 'Could not confirm the trade',
somethingWrong: 'Something went wrong.',
soldNote: 'Sold to {{name}}.',
saleConfirmed: 'Sale confirmed',
saleConfirmedDesc: '{{name}} can now confirm their side.',
saleConfirmFailed: 'Could not confirm the sale',
escrowRedeemed: 'Escrow redeemed',
escrowRedeemedDesc: 'The funds are now in your Cashu wallet.',
escrowRedeemFailed: 'Could not redeem escrow',
loginTitle: 'Log in to view messages',
secureLoginTitle: 'Secure Login Required',
secureLoginDesc: 'Messages require a secure signer app.',
encrypted: 'Encrypted',
refresh: 'Refresh',
didCompleteTrade: 'Did you complete this trade?',
recordedSale: '{{name}} recorded the sale. Confirm to update reputation for both of you.',
confirm: 'Confirm',
confirmSale: 'Confirm this sale?',
askedToRecord: '{{name}} asked you to record it. Confirming builds reputation for both of you.',
confirmSaleButton: 'Confirm sale',
redeemEscrow: 'Redeem escrow',
escrowAppOnly: 'Cashu escrow is app-only — open SkateSpots on your phone to redeem.',
startThread: 'Send a message to start the conversation',
placeholder: 'Type a message...',
e2eeNote: 'Messages are end-to-end encrypted using NIP-17',
},
coordinator: {
secondsAgo: '{{count}}s ago',
minutesAgo: '{{count}}m ago',
hoursAgo: '{{count}}h ago',
daysAgo: '{{count}}d ago',
statusIdle: 'Not checked',
statusChecking: 'Checking…',
statusOnline: 'Connected',
statusStale: 'Quiet',
statusOffline: 'Not seen',
statusError: 'Unreachable',
notACoordinator: "That's not a coordinator",
notACoordinatorDesc: 'Paste an nprofile, an npub, or a cordn.net link.',
added: 'Coordinator added',
relayOne: '{{count}} relay',
relayMany: '{{count}} relays',
lastSeen: 'last seen {{time}}',
onlineDesc: 'Announcing. Groups that use it can deliver messages now.',
staleDesc: 'Found, but it has not announced recently — it may be asleep.',
offlineDesc: 'No announcement seen. It may be private (that is fine), or simply offline.',
errorDesc: 'Could not reach the relays.',
idleDesc: 'Checking…',
checkingDesc: 'Looking for its announcement…',
eventFoundOne: '{{count}} event found.',
eventFoundMany: '{{count}} events found.',
check: 'Check',
addLinkCopied: 'Add link copied',
copyFailed: 'Copy failed',
copyManually: 'Copy it manually.',
addLink: 'Add link',
removeConfirm: 'Remove this coordinator from this device?',
scanToJoin: 'Scan in Cordn to join this coordinator.',
noCoordinator: 'No coordinator yet',
noCoordinatorDescA: "A coordinator delivers your crew's",
encryptedWord: 'encrypted',
noCoordinatorDescB: 'group messages in order. It sees who, when and which group — never the contents. Pick one, or run your own.',
change: 'Change coordinator',
addTitle: 'Add a coordinator',
add: 'Add',
scanQR: 'Scan a coordinator QR',
inputPlaceholder: 'nprofile… or a cordn.net link',
runOwn: 'Run your own',
runOwnDesc: "It can live on an old phone, a Raspberry Pi, or any small box you leave on. Your group keeps working while it's up — and you can move the group whenever you like.",
onPhone: 'On a phone',
onPhoneDesc: 'Use the Cordn Coordinator app, then scan its QR here to add it.',
openCordn: 'Open Cordn',
scanItsQR: 'Scan its QR',
onServer: 'On a server',
commandCopied: 'Command copied',
copyCommand: 'Copy command',
androidSleep: 'Android may sleep a phone coordinator. Expect to move a group now and then.',
privacyNote: '🔒 A coordinator sees ciphertext and metadata only — never message contents. Run your own to keep even the metadata yours.',
customTitle: 'Custom Chat Coordinator',
customBadge: '· experimental · your own relay',
customDescShort: "Optional — your crew already has end-to-end encrypted chat built in, so you don't need this. Only set it if you run your own",
remove: 'Remove',
pointerNote: "Pointer only for now: a wss:// endpoint is used as the crew's chat relay; an https:// one via the shared ContextVM relay. Real Cordn MLS isn't wired yet.",
cordnCoordinator: 'Cordn coordinator',
invalid: "That doesn't look like a coordinator. Paste an nprofile, a cordn.net link, or an https/wss URL.",
useMine: 'Use my coordinator',
},
createCrew: {
seoTitle: 'Create a Crew - SkateSpots',
seoDescription: 'Create your skate crew — home turf, members and spots.',
loginRequired: 'Login Required',
loginRequiredDesc: 'You need to log in to create a crew.',
backToCrews: 'Back to Crews',
title: 'Create a Crew',
intro: 'Your skate family — the people you skate with. Just a name gets you started; everything else is optional.',
},
crew: {
seoTitleNamed: '{{name}} - SkateSpots Crew',
seoTitle: 'Crew - SkateSpots',
seoDescription: 'A skate crew on SkateSpots.',
postHidden: 'Post hidden',
postHiddenDesc: 'Removed from the crew feed for everyone.',
postHideFailed: 'Failed to hide post',
backToCrews: 'Back to Crews',
disband: 'Disband',
youAreCaptain: "You're a captain",
member: 'Member',
askSasquatch: 'Ask Sasquatch',
agent: 'Crew Agent',
agentDesc: "Your crew's AI guide — nearby spots, events, coaches & gear, with live data.",
eventsTab: 'Events',
chatTab: 'Chat',
newMessages: 'New messages',
hostJam: 'Host a jam',
noEvents: 'No crew events yet. Host a jam, comp or meetup as the crew — it connects your crew to the events calendar and lets people zap it.',
support: 'Support',
zapCrew: 'Zap the Crew',
bandTitle: 'Crew Band',
bandTagline: 'Live music · skate & sound',
listenTitle: 'Listen: {{domain}}',
listen: 'Listen to the tunes',
setUpGig: 'Set up a gig',
bookGigMessage: 'Hey {{crew}} — interested in booking {{band}} for a gig. Are you around to talk dates?',
messageAboutGig: 'Message about a gig',
gigHintA: 'adds a music event to the calendar hosted by this crew.',
messageShort: 'Message',
gigHintB: 'opens an encrypted DM to the crew to sort dates & details.',
homeSpots: 'Home Spots',
viewSpot: 'View spot',
seeOnMap: 'See on the map →',
founderLinks: 'Founder & Links',
startedBy: 'Started by {{name}}',
opens: 'Opens: {{domain}}',
},
crews: {
seoTitle: 'Crews - SkateSpots',
seoDescription: 'Find your people. Discover local skate crews, collectives and communities near you.',
videoPartTooltip: 'This crew has a video part',
part: 'Part',
bandTooltip: 'Crew band: {{name}}',
band: 'Band',
focusAll: 'All',
focusAllShort: 'All',
focusDiy: 'DIY',
focusPhoto: 'Photo',
focusCoaching: 'Coaching',
focusArt: 'Art',
focusMusic: 'Music',
focusPhysio: 'Physio',
focusMixed: 'Mixed',
title: 'Crews',
heroLine: 'Your map-anchored skate family — the people you skate',
heroWith: 'with',
heroRest: ", not who you hire (that's The Lab).",
crewCountOne: '{{count}} crew',
crewCountMany: '{{count}} crews',
cityCountOne: '{{count}} city',
cityCountMany: '{{count}} cities',
createCrew: 'Create Crew',
create: 'Create',
searchPlaceholder: 'Search crews by name or city…',
filterTitle: 'Filter crews',
filterDesc: 'What the crew is into.',
nearMe: 'Near me',
emptySearchTitle: 'No crews match your search',
emptySearchHint: 'Nothing found for “{{query}}”. Try another name or city.',
clearSearch: 'Clear search',
emptyFocusTitle: 'No {{label}} crews yet',
emptyFocusHint: 'Nobody\'s flying the “{{label}}” flag here yet. Show every crew, or start this one yourself.',
showAll: 'Show all crews',
claimTitle: 'Claim your local scene',
claimHint: 'No crews planted here yet. Be the first — start your crew, link your home spots, and put your scene on the map for everyone to find.',
},
crewCoordinator: {
privateChat: 'Private Crew Chat',
on: 'On',
defaultA: 'The',
chatWord: 'Chat',
defaultB: 'tab is',
e2ee: 'end-to-end encrypted',
defaultC: "for the founder and all listed members — nothing to set up. Messages are sealed to each member's key (NIP-17), so only your crew can read them.",
advancedA: 'Advanced: run your own',
cordnCoordinator: 'Cordn coordinator',
routeThrough: 'Route chat through it',
coordinator: 'Coordinator',
deliveredOver: 'Delivered over',
sharedRelay: '— the shared ContextVM relay.',
ownRelay: "— your crew's own relay.",
chatLive: 'Private chat is live',
routesA: 'The',
routesB: 'tab routes through this coordinator: messages are end-to-end encrypted (NIP-44, sealed to each member), so it only ever handles sealed traffic.',
publicWarning: 'Heads up: it is addressed over the shared ContextVM relay, so the ciphertext and its metadata (which crew, and when) are publicly visible — just not readable. Point the crew at a private wss:// relay to keep even that private.',
ownRelaySafe: "Nothing crew-specific leaves your crew's own relay.",
manage: 'Manage coordinator',
open: 'Open {{domain}}',
runBy: 'Coordinator run by',
},
dms: {
securityReminder: 'Security reminder',
phishingStrong: 'SkateSpots will never DM you',
phishingRest: "asking to verify your account, share your keys, or send payments. If someone claims to be SkateSpots support via DM — it's a scam.",
dismissReminder: 'Dismiss security reminder',
peopleCount: '{{count}} people',
youPrefix: 'You: ',
unreadMessages: 'Unread messages',
seoTitle: 'Messages - SkateSpots',
seoDescription: 'Private encrypted messages with the skateboarding community',
loginTitle: 'Log in to access messages',
loginDesc: 'Private, encrypted messaging with skaters in the community',
secureLoginTitle: 'Secure Login Required',
secureLoginDesc: 'Private messages require a secure signer app to encrypt your conversations.',
onAndroid: 'On Android:',
useAmber: 'Use the Amber app',
getAmber: 'Get Amber →',
onIOS: 'On iOS:',
iosHint: 'Try logging in with a bunker connection (nsec.app) from Settings',
browserHint: 'Try logging in with a browser extension like Alby or nos2x that supports encryption.',
title: 'Messages',
subtitleFull: 'End-to-end encrypted with NIP-17',
subtitleShort: 'Encrypted',
newMessage: 'New Message',
groups: 'Groups',
loadFailed: 'Failed to load messages',
emptyTitle: 'No messages yet',
emptyDesc: 'Start a conversation with someone in the community',
startConversation: 'Start a Conversation',
undecryptableOne: "{{count}} encrypted message couldn't be opened by your signer.",
undecryptableMany: "{{count}} encrypted messages couldn't be opened by your signer.",
},
editCrew: {
seoTitleNamed: 'Edit {{name}} - SkateSpots',
seoTitle: 'Edit Crew - SkateSpots',
seoDescription: 'Edit your skate crew.',
loginRequired: 'Login Required',
notYours: 'Not your crew',
notYoursDesc: 'Only the crew founder can edit this crew.',
backToCrew: 'Back to Crew',
title: 'Edit Crew',
},
form: {
add: 'Add',
added: 'Added',
addNameInBasicsA: 'Add a crew name back in',
addNameInBasicsB: 'to finish.',
adminsDescA: 'Make trusted members',
adminsWord: 'admins',
adminsDescB: "— they can invite members and moderate the crew feed & chat with their own keys. You (the founder) always stay the owner of the crew's identity.",
agentDescA: 'Add',
agentDescB: ', an AI guide who actually knows your crew. Members can ask about',
agentDescC: "— plus nearby spots, events, coaches and gear from live SkateSpots data. Runs right in the browser; no keys or setup. It's early (free-tier AI, so occasionally slow).",
agentDescStrong: "your next jam, who runs the crew, what you're looking for and your vibe",
agentEnabledNote: 'Sasquatch will appear on your crew page once you save.',
bandDesc: 'Got a band? Skate scenes and local music go hand in hand — drop your band name and a link to the tunes.',
bandFooterA: 'For a full band profile (genres, instruments, gigs), create a',
musicianWord: 'Musician',
bandFooterB: 'in The Lab, then link it in the',
linkedLabWord: 'Linked Lab profiles',
bandFooterC: 'section above.',
bandMusicWord: 'Band & Music',
bandName: 'Band name',
bandNamePlaceholder: 'e.g. The Bail Outs',
bandNudgeA: "You've got a band — tap to set your",
crewTypeWord: 'Crew Type',
bandNudgeB: 'to',
bandNudgeC: "so you also show up in The Lab's Music scene.",
beta: 'Beta',
bioLabel: 'About / Manifesto',
bioPlaceholder: 'Who you are, what you skate, your story...',
continue: 'Continue',
crewCreated: 'Crew created!',
crewUpdated: 'Crew updated!',
crewName: 'Crew Name',
crewOnlyWord: 'Crew-only',
crewType: 'Crew Type',
crewTypeDescA: 'Is your crew focused on a craft? Pick one to also show up in that scene in',
theLab: 'The Lab',
crewTypeDescB: '. Regular skate crews stay',
mixedWord: 'Mixed',
crewTypeDescC: '.',
customVibeHint: 'Add your own vibe or focus with a #',
customVibePlaceholder: 'e.g. downhill, night-sessions, no-comply',
editDesc: "Your crew's identity, home turf and members",
enableAgent: 'Enable Crew Agent',
enableTreasury: 'Enable Crew Treasury',
failed: 'Failed',
founded: 'Founded (Year)',
geolocationUnsupported: 'Geolocation not supported',
gettingLocation: 'Getting...',
goalHint: 'Make sure your Nostr profile has a Lightning address (lud16) so contributions can land.',
goalPlaceholder: 'e.g. Concrete the DIY spot',
goalTarget: 'Target (sats)',
goalWhat: "What's the goal?",
homeSpots: 'Home Spots',
homeSpotsDesc: "Link the spots your crew haunts — they'll connect to your crew on the map",
homeTurf: 'Home Turf (City / Area)',
homeTurfPlaceholder: 'Bristol, UK',
instagramDetected: 'Instagram clip detected',
invalidMember: 'Enter a valid npub, nprofile, or hex pubkey.',
latitude: 'Latitude',
leaveBlankLocation: 'Leave blank to keep the current home turf location.',
links: 'Links',
locationError: 'Location error',
locationFound: 'Location found',
logoAlt: 'Crew',
logoHint: 'A logo makes your crew instantly recognisable on the map and in feeds. Square works best.',
logoLabel: 'Crew Logo / Photo',
logoUploaded: 'Logo uploaded',
longitude: 'Longitude',
looksGood: 'Looks good',
makeAdmin: 'Make admin',
mapPin: 'Map Pin',
mapPinHint: 'Drop your crew on the map so locals can find you. Tap the button — no typing needed.',
memberAlreadyAdded: 'That member is already added.',
members: 'Members',
membersDesc: "Add your crew's Nostr accounts. Each member confirms from their own key — until then they show as “pending”.",
musicLink: 'Music link',
nameCrew: 'Name your crew',
nameCrewDesc: 'A crew needs a name before you continue.',
nameCrewFirst: 'Name your crew first',
nameHint: 'The only thing you truly need. You can change it later.',
namePlaceholder: 'e.g. Backyard Bandits',
nameRequired: 'Name required',
nameRequiredDesc: 'Give your crew a name.',
nip05DescA: 'A verifiable handle you control on any domain, e.g.',
nip05DescB: ". It's verified against this crew's founder key, and you can add a few — each shows a green check when its domain confirms it.",
nip05Label: 'Crew NIP-05',
nip05Placeholder: 'crew@yourdomain.com',
nip05Add: 'Add',
nip05Invalid: 'Enter a handle like crew@yourdomain.com',
nip05TooMany: 'You can add up to {{count}} handles.',
nip05Unverified: 'Unchecked',
nip05Remove: 'Remove {{handle}}',
notAKeyA: "That doesn't look like a Nostr key yet — paste an",
notAKeyB: ',',
notAKeyC: ', or hex pubkey.',
onlyYouTubeInstagram: 'Only YouTube and Instagram links are supported.',
openCall: 'Open Call',
openCallDescA: 'Looking for a filmer, editor or new blood? Put out a call — it shows on your crew page so matching folks from',
openCallDescB: 'can reach out.',
optional: '· optional',
optionalParen: '(optional)',
otherSocials: 'Other Socials',
otherSocialsDesc: 'Add as many as you like — YouTube, TikTok, Discord, Telegram, etc.',
ownVisibility: 'own visibility',
progress: 'Progress',
publicWord: 'Public',
publishedToScene: 'Published to the scene.',
publishFailed: 'Failed to publish',
publishing: 'Publishing...',
ready: 'Ready',
readyToRollA: 'Ready to roll — hit',
readyToRollB: 'whenever you like.',
removeAdminRole: 'Remove admin role',
removeMember: 'Remove member',
removeSocial: 'Remove social link',
removeVibe: 'Remove {{vibe}}',
roles: 'Roles:',
sasquatch: 'Sasquatch',
saveCrew: 'Save Crew',
skipToEnd: 'Skip to end',
socialPlaceholder: '@username or link',
sponsorDesc: 'If a local shop backs your crew, pick them here to show a “Sponsored by” banner linking to their shop.',
stepAria: 'Step {{step}}: {{title}}',
stepBasics: 'The Basics',
stepBasicsShort: 'Basics',
stepBasicsHint: 'Just the essentials — you can spin up a crew with only this step.',
stepVibe: 'Identity & Vibe',
stepVibeShort: 'Vibe',
stepVibeHint: 'How your crew shows up around the app. All optional.',
stepSquad: 'Squad & Spots',
stepSquadShort: 'Squad',
stepSquadHint: 'Add your people and your home turf. You can always do this later.',
stepExtras: 'Extras',
stepExtrasShort: 'Extras',
stepExtrasHint: 'Power-ups for later — skip anything you’re unsure about.',
extrasAllOptional: 'Everything on this step is optional.',
groupIdentity: 'Identity',
groupIdentityHint: 'Handle, links',
groupAi: 'Sasquatch AI',
groupShowcase: 'Showcase',
groupPayments: 'Payments & treasury',
groupIntegrations: 'Integrations',
groupAdvanced: 'Advanced',
groupAdvancedHint: 'experimental',
pro: 'Pro',
agentProNote: 'Powered by Sasquatch. Pro gets the best models; without Pro it uses a free fallback.',
stepOf: 'Step {{step}} of {{total}}',
treasuryDescA: "Raise sats for a shared goal — fixing the local DIY spot, a crew trip, a comp prize pot. It's a transparent Lightning goal; zaps go",
treasuryDescStrong: 'straight to your wallet',
treasuryDescB: "(via your profile's Lightning address). SkateSpots never holds the funds.",
upload: 'Upload',
uploadFailed: 'Upload failed',
useCurrentLocation: 'Use current location',
vibeLabel: 'Vibe / Focus',
videoDescA: "Your crew's full part, montage or latest clip. Paste a",
videoDescB: "link — it'll play right on your crew page.",
visibilityDescA: 'Each member controls their',
visibilityDescB: '— when they confirm from their key they can choose',
visibilityDescC: "— hidden from outsiders). You can't set this for them; it's their call.",
website: 'Website',
yourRoles: 'Your roles (Team Leader)',
youtubeDetected: 'YouTube video detected',
invalidUrl: 'Enter a valid link (e.g. https://example.com).',
},
groupConversation: {
seoTitle: 'Group chat - SkateSpots',
invalidLink: 'This group chat link is invalid.',
loginTitle: 'Log in to view this group',
secureSignerTitle: 'Secure signer required',
secureSignerDesc: 'Group messages need a NIP-44 capable signer.',
encryptedGroup: 'End-to-end encrypted group',
empty: 'No messages yet — say hi to the group.',
placeholder: 'Message the group...',
},
hero: {
established: 'Est. {{year}}',
teamLeader: 'Team leader: {{name}}',
ledBy: 'Led by',
},
inviteCard: {
acceptedTitle: 'Invite accepted 🛹',
acceptedDesc: "The crew founder confirms members on {{crew}}. You'll appear on the roster once they approve.",
couldNotAccept: 'Could not accept',
couldNotDecline: 'Could not decline',
acceptedDone: 'Accepted —',
confirmsMembers: 'confirms members',
declinedDone: 'Declined invite to {{crew}}',
invitedYou: 'invited you to join',
admin: 'Admin',
accept: 'Accept',
decline: 'Decline',
},
inviteDialog: {
messageIntro: 'Yo — join the {{crew}} crew on SkateSpots 🛹',
messageBody: 'Log in with your Nostr key, then request to join from the crew page.',
copyFailed: 'Copy failed',
shareTitle: 'Join {{crew}}',
sentTitle: 'Invite sent! 🛹',
sentDesc: "They'll get a notification to accept or decline.",
couldNotSend: 'Could not send invite',
trigger: 'Invite',
title: 'Invite to {{crew}}',
description: "Send an invite to someone's Nostr key, or share a link.",
sendTab: 'Send invite',
shareTab: 'Share link',
npubLabel: "Skater's npub",
invalidNpub: "That doesn't look like a valid npub.",
noteLabel: 'Note',
optional: '(optional)',
notePlaceholder: 'Come skate with us…',
inviteAsAdmin: 'Invite as admin',
inviteAsAdminDesc: 'Can manage the crew & invite others.',
sendButton: 'Send invite',
sendFooter: "They'll get a notification to accept or decline. Membership only counts once they accept.",
inviteLink: 'Invite link',
messageLabel: 'Message',
copyMessage: 'Copy message',
founderHint: "As founder you can also add a member's key directly, then they confirm.",
addMemberSettings: 'Add member in crew settings',
},
joinRequest: {
moreCount: '+{{count}} more',
addedTitle: 'Added {{name}} to {{crew}} 🛹',
addedDesc: "They'll confirm from their side to appear as a member.",
vouchedTitle: 'You vouched for {{name}} 🤝',
vouchedDesc: 'The founder of {{crew}} will see your endorsement and can add them.',
couldNotEndorse: 'Could not endorse',
endorsedDoneA: 'You vouched for',
endorsedDoneB: '— founder will confirm',
vouchedBy: 'Vouched by',
approveAndAdd: 'Approve & add',
addToCrew: 'Add to crew',
vouchedWaiting: 'Vouched — waiting on the founder',
vouchForThem: 'Vouch for them',
leadershipOnly: 'Crew leadership only.',
adminHelp: "Admins can't add members directly — vouching tells the founder you back this rider.",
},
leadership: {
teamLeader: 'Team Leader',
captain: 'Captain',
verifiedMember: 'Verified member',
messageName: 'Message {{name}}',
title: 'Leadership',
captainsOne: '{{count}} Captain',
captainsMany: '{{count}} Captains',
footer: 'The Team Leader runs the roster; Captains help invite, host jams and keep the crew tidy.',
},
linkField: {
title: 'Linked Crew',
description: "Run a crew too? Link it so people can jump between your studio and your crew. Optional. Link this crew back to the profile from the crew editor and it'll show a green",
descriptionRest: 'badge on both.',
none: "You don't have any crews yet. Create one on the Crews page to link it here.",
linkedCrew: 'Linked crew ({{id}})',
},
lookingFor: {
title: 'Open Call',
subtitle: 'This crew is looking for:',
},
memberVisibility: {
updatedTitle: 'Visibility updated',
updatedCrew: 'You now appear only to crew-mates.',
updatedPublic: 'You now appear publicly in this crew.',
couldNotUpdate: 'Could not update',
title: 'Your visibility in this crew',
save: 'Save visibility',
},
newMessage: {
inCrew: 'In your crew: {{crews}}',
crewmate: 'Crew-mate',
following: 'Following',
invalidKey: "That doesn't look like a valid npub or public key.",
description: 'Search your people by name, or paste an npub to message anyone. Select two or more to start a group.',
searchPlaceholder: 'Search name or paste npub1...',
messageThisKey: 'Message this key',
loadingPeople: 'Loading your people...',
noPeople: 'No crew-mates or follows yet.',
noPeopleHint: "Paste someone's npub above to start a conversation.",
yourCrews: 'Your crews',
selected: '{{count}} selected',
startGroup: 'Start group ({{count}})',
message: 'Message',
},
nip05: {
mismatch: 'This domain points the handle at a different Nostr key than the crew founder. Not verified.',
verified: "Verified — the domain confirms this handle belongs to the crew's founder key.",
},
requestCollective: {
sentTitle: 'Request sent 🤝',
sentDesc: '{{name}} will see it in their notifications and can add you.',
couldNotSend: 'Could not send request',
title: 'Join this collective',
waiting: 'Request sent — waiting on {{name}}.',
askA: 'Ask the crew behind',
askB: "to add you to the roster. They'll get a notification and can add you in one tap.",
placeholder: "Optional: say why you'd be a good fit…",
sendButton: 'Send request',
},
requestToJoin: {
sentTitle: 'Request sent! 🛹',
sentDesc: 'The {{crew}} leadership will see it in their notifications.',
couldNotSend: 'Could not send request',
trigger: 'Request to join',
title: 'Join {{crew}}',
description: "Notifies the crew's founder & admins so they can add you.",
placeholder: 'Say hi — where you skate, who you know, why you want in…',
sending: 'Sending…',
sendButton: 'Send join request',
},
sponsorBanner: {
loading: 'Loading sponsor…',
sponsoredBy: 'Sponsored by',
},
sponsorPicker: {
selected: 'Sponsor selected',
setLoading: 'Sponsor set (shop loading…)',
set: 'Sponsor set',
clearAria: 'Clear sponsor',
searchPlaceholder: 'Search skate shops by name or city…',
loading: 'Loading shops…',
noMatch: 'No shops match — try a different search.',
none: 'No shops found yet.',
},
treasury: {
title: 'Crew Treasury',
ofGoal: 'of {{target}} goal',
goalReached: 'Goal reached!',
funded: '{{percent}}% funded',
contributionOne: '{{count}} contribution',
contributionMany: '{{count}} contributions',
daysLeft: '{{days}}d left',
closed: 'closed',
nonCustodial: "Non-custodial · zaps go straight to the crew's Lightning wallet",
},
userStatus: {
atSpot: '📍 At a spot',
autoClears: '⏱️ Auto-clears in {{min}} min',
},
video: {
title: 'The Part',
fullVideo: 'Full video · montage',
latestClip: 'Latest clip',
watch: 'Watch',
iframeTitle: '{{crew}} — video',
playAria: 'Play {{crew}} video',
thumbAlt: '{{crew}} video thumbnail',
instagramTitle: 'Instagram clip',
},
vouch: {
removedTitle: 'Vouch removed',
vouchedTitle: 'Vouched!',
removedDesc: 'You no longer vouch for {{name}}.',
vouchedDesc: 'You vouched for {{name}}. The scene trusts its own.',
couldNotVouch: 'Could not vouch',
title: 'Scene Vouches',
sceneVerified: 'Scene-verified',
vouched: 'Vouched',
newUnvouched: 'New · unvouched',
checking: 'Checking the scene…',
empty: "No vouches yet. Vouches from skaters in your circles are the best signal that someone's the real deal — not a random who wandered in.",
countOne: '{{count}} vouch',
countMany: '{{count}} vouches',
trusted: '{{count}} from skaters in your web of trust',
remove: 'Remove vouch',
vouchFor: 'Vouch for {{name}}',
ownerNote: 'Skaters who know your work can vouch for you here.',
},
},
settingsUi: {
proSupport: {
title: 'Pro & Support',
subtitle: 'Optional extras that keep SkateSpots free',
prepaidTitle: 'Prepaid, not a subscription',
prepaidBody: 'Every purchase is a one-off Bitcoin/Lightning payment — no account, no recurring charge, no custody. Paying again extends your time.',
sasquatchTitle: 'Sasquatch Pro',
sasquatchBenefit: 'Unlocks the personal AI assistant — it can read your own Stash, crews and listings — and helps fund the project.',
active: 'Active',
activeUntil: 'Active until {{date}}',
daysLeft: '{{count}} days left',
notActive: 'Not active',
goPro: 'Go Pro',
extend: 'Extend Pro',
day: 'day',
featuredTitle: 'Featured placement',
featuredBenefit: 'Push a listing to the top with a Pro badge — for Lab profiles, events, shops and tours.',
featuredHow: 'Duration tiers apply to every type; longer commitments cost less per day. You feature a listing when you create or edit it.',
featuredUntil: 'Until {{date}}',
yourFeatured: 'Your featured listings',
noneFeatured: 'No active featured placements.',
freeTitle: 'Free paths',
freeBody: 'The AI FAQ and local search are always free, and you can publish a marketplace listing with a free proof-of-work instead of the sats fee.',
},
notFound: {
seoTitle: '404 - Page Not Found',
seoDescription: 'The page you are looking for could not be found. Return to the home page to continue browsing.',
message: 'Oops! Page not found',
returnHome: 'Return to Home',
},
loginArea: {
signUp: 'Sign Up',
logIn: 'Log In',
},
accountSwitcher: {
switchAccount: 'Switch Account',
walletSettings: 'Wallet Settings',
addAccount: 'Add another account',
logout: 'Log out',
},
amberHint: {
defaultAction: 'send',
multiBefore: 'Your signer will ask you to approve',
multiTimes: '{{approvals}} times',
multiAfter: 'to {{action}} — that\'s normal for encrypted messages (one approval per recipient). Approve each.',
single: 'Your signer will ask you to approve to {{action}}.',
},
amberCallback: {
processing: 'Processing Amber login...',
gotResult: 'Got result: {{result}}',
noResultDebug: 'No result from Amber',
noResultToast: 'No result received from Amber. Operation may have been cancelled.',
validPubkey: 'Valid pubkey! Logging in...',
loginSuccessful: 'Login successful!',
loginErrorDebug: 'Login error: {{error}}',
invalidResultDebug: 'Invalid result format: {{result}}',
loggedInTitle: 'Logged in',
amberLoginDesc: 'Successfully logged in with Amber!',
loginFailedTitle: 'Login failed',
loginFailedDesc: 'Failed to complete Amber login. Please try again.',
invalidPubkeyDesc: 'Invalid pubkey received from Amber.',
sessionLostToast: 'Session data was lost. Please try again.',
eventSignedTitle: 'Event signed',
eventSignedDesc: 'Your event has been signed by Amber.',
signingFailedTitle: 'Signing failed',
signingFailedDesc: 'Failed to process signed event from Amber.',
operationCompleteTitle: 'Operation complete',
operationCompleteDesc: '{{op}} completed.',
},
loginDialog: {
loginWithAmber: 'Login with Amber (Android)',
welcomeTitle: 'Welcome to SkateSpots',
welcomeSubtitle: 'Sign up or log in to continue',
newHere: 'New here?',
createFree: 'Create a free account. No email required.',
createAccount: 'Create Account',
orLogIn: 'Or log in',
loggedInTitle: '🛹 Logged in!',
loggedIn: 'Logged in!',
connectedAmber: 'Connected via Amber!',
amberLoginFailed: 'Amber login failed',
amberCancelled: 'Login cancelled. Tap "Open Amber" to try again.',
amberNotFound: 'Amber not found. Make sure Amber is installed from the ZapStore.',
loginFailed: 'Login failed',
pubkeyError: 'Paste your hex pubkey (64 chars) or npub from Amber',
pairedSigner: 'Paired with your signer.',
connectFailed: 'Could not connect to your signer.',
openedEncrypted: 'Opened your encrypted key.',
openKeyFailed: 'Could not open that key.',
bunkerPrefix: 'Must start with bunker://',
connectedBunker: 'Connected via bunker!',
bunkerFailed: 'Failed: {{error}}',
checkBunkerUri: 'Check your bunker URI',
nsecPrefix: 'Must start with nsec1',
invalidNsec: 'Invalid nsec: {{error}}',
checkKey: 'Check your key',
noExtensionTitle: 'No extension found',
noExtensionDesc: 'Install Alby or another NIP-07 extension.',
amberIdleDesc: 'Your key stays inside Amber. One tap — approve in Amber and you\'re back here, signed in. Nothing to copy.',
continueWithAmber: 'Continue with Amber',
noAmberGetIt: 'Don\'t have Amber? Get it →',
waitingAmberTitle: 'Waiting for Amber…',
waitingAmberDesc: 'Approve the request in Amber. You\'ll be signed in automatically the moment you come back.',
openAmberAgain: 'Open Amber again',
havingTrouble: 'Having trouble? Enter your key manually',
enterPublicKey: 'Enter your public key',
enterPublicKeyDescBefore: 'Only if the automatic flow didn\'t work. In Amber: tap your profile →',
copyPublicKey: 'Copy public key',
enterPublicKeyDescAfter: 'then paste it here.',
connect: 'Connect',
back: '← Back',
signInWithPhone: 'Sign in with your phone',
signInWithPhoneDesc: 'Scan with a signer app (Amber, nsec.app). Your key never leaves your phone.',
showPairingCode: 'Show pairing code',
waitingSigner: 'Waiting for your signer…',
copyLink: 'Copy link',
orPasteBunker: 'or paste a bunker URL',
bunkerTitle: 'Bunker (NIP-46)',
bunkerPasteBefore: 'Paste your',
bunkerPasteAfter: 'URI.',
amberInstructions: 'Settings → NIP-46 Bunker → Create → Copy',
nsecAppInstructions: 'Connect app → Copy bunker URI',
secretKeyTitle: 'Secret Key',
secretKeyRawDesc: 'Paste your Nostr secret key directly.',
secretKeyEncDesc: 'Open a password-protected key backup.',
rawNsec: 'Raw nsec',
encryptedNip49: 'Encrypted (NIP-49)',
nsecWarning: '⚠️ Only use this on a trusted device. Never share your nsec.',
login: 'Login',
encSaferDesc: 'Safer: the key stays encrypted until you enter your password, so the backup is safe to keep in cloud storage or a password manager.',
password: 'Password',
openEncryptedKey: 'Open encrypted key',
browserExtensionTitle: 'Browser Extension (NIP-07)',
browserExtensionDesc: 'Best for desktop. Works with Alby, nos2x, Flamingo.',
loginWithExtension: 'Login with Extension',
},
signupDialog: {
download: 'Download',
stepChoose: 'Choose',
stepProfile: 'Profile',
stepSaveKey: 'Save key',
somethingWrong: 'Something went wrong',
createFailed: 'Couldn\'t create your account. Please try again.',
keySavedTitle: 'Secret Key Saved!',
keySavedDesc: 'Your key has been safely stored.',
downloadFailedTitle: 'Download failed',
downloadFailedDesc: 'Could not download the key file. Please copy it manually.',
encSavedTitle: 'Encrypted backup saved 🔐',
encSavedDesc: 'Keep the file AND the password — you need both to log back in.',
encFailed: 'Could not encrypt that key.',
invalidFileTitle: 'Invalid file type',
invalidFileDesc: 'Please select an image file for your avatar.',
fileTooLargeTitle: 'File too large',
fileTooLargeDesc: 'Avatar image must be smaller than 5MB.',
avatarUploadedTitle: 'Avatar uploaded!',
avatarUploadedDesc: 'Your avatar has been uploaded successfully.',
uploadFailedTitle: 'Upload failed',
uploadFailedDesc: 'Failed to upload avatar. Please try again.',
nameRequiredTitle: 'Display name required',
nameRequiredDesc: 'Pick a display name so other skaters know who you are. You can change it anytime.',
reserveFailedTitle: 'Couldn\'t reserve that name',
reserveFailedDesc: '{{error}} You can claim one later in Settings.',
profileCreatedTitle: 'Profile Created!',
profileCreatedDesc: 'Your profile has been set up.',
profileFailedTitle: 'Profile Setup Failed',
profileFailedDesc: 'Your account was created but profile setup failed. You can update it later.',
welcomeToastTitle: 'Welcome!',
welcomeToastDesc: 'Your account is ready.',
titleWelcome: 'Join SkateSpots',
titleProfile: 'Set Up Your Profile',
titleDone: 'Welcome!',
createFreeAccount: 'Create your free account',
createFreeAccountDesc: 'No email, no phone, no password. Your account is a Nostr key — choose where it lives.',
recommended: 'Recommended',
signUpWithAmber: 'Sign up with Amber',
signUpWithAmberDesc: 'Your key is created and stored inside the Amber app — SkateSpots never touches it. Most secure. One tap to approve.',
continueWithAmber: 'Continue with Amber',
orQuickWay: 'or the quick way',
quickAccount: 'Quick account on this device',
quickAccountDesc: 'We generate a key that lives on this device. Fastest start — we\'ll help you save a backup right after.',
createQuickAccount: 'Create quick account',
quickReassurance: 'Takes 5 seconds · lives on this device · we\'ll help you back it up next',
preferAnotherSigner: 'Prefer another signer?',
iosDesktopNudge: 'On iOS or desktop? A signer app or browser extension is the most secure option —',
seeAllOptions: 'See all options',
youreInPickName: 'You\'re in! 🛹 Pick a display name so skaters know who you are',
changeAnytime: 'You can change it anytime later. Bio and avatar are optional.',
publishingProfile: 'Publishing your profile...',
displayName: 'Display Name',
displayNamePlaceholder: 'e.g. Tony, gnarlyGrind, or your crew name',
displayNameHint: 'Required — how you\'ll show up across SkateSpots. Change it anytime.',
skateName: 'Your SkateSpots name',
skateNameHint: 'Free and optional — a verified handle that shows a ✓ on your profile.',
bio: 'Bio',
bioPlaceholder: 'Tell others about yourself...',
avatar: 'Avatar',
uploadAvatar: 'Upload avatar image',
saving: 'Saving...',
saveAndStart: 'Save & Start Skating',
addNameToContinue: 'Add a display name to continue.',
savingPublishesNote: 'Saving publishes your profile to Nostr — your signer may ask you to approve it once.',
youreIn: 'You\'re in! 🛹',
oneQuickThing: 'One quick thing before you drop in…',
saveLoginKey: 'Save your login key',
saveLoginKeyBefore: 'This is the',
only: 'only',
saveLoginKeyAfter: 'way back into your account — no email, no password reset. Save it now and you can log in on any device, anywhere you skate.',
revealSaveKey: 'Reveal & save my key',
recoveryCopied: 'Recovery code copied. Keep it somewhere safe.',
saved: 'Saved',
treatLikeBoardKeys: 'Treat it like the keys to your board — anyone with this code controls your account. Never share it.',
hideEncBackup: 'Hide encrypted backup',
saveEncBackup: 'Save an encrypted backup',
encBackupBefore: 'A password-protected copy — safe to keep in cloud storage or a password manager. You\'ll need the file',
and: 'and',
encBackupAfter: 'the password.',
choosePassword: 'Choose a password',
saveEncBackupCta: 'Save encrypted backup',
savedCodeAria: 'I\'ve saved my recovery code',
savedCodeLabel: 'I\'ve saved my recovery code somewhere safe — a password manager, or written down offline.',
letsSkate: 'Let\'s skate',
saveLater: 'I\'ll save it later from Settings',
advancedSigner: 'Advanced: use a signer app instead',
},
mnemonic: {
generatedTitle: 'Recovery Phrase Generated! 🎉',
generatedDesc: 'Write down these 12 words in order. They\'re the key to your account!',
savedTitle: 'Recovery Phrase Saved!',
savedDesc: 'Keep this file in a secure location.',
downloadFailedTitle: 'Download failed',
downloadFailedDesc: 'Could not download the file. Please copy manually.',
copiedTitle: 'Copied to clipboard!',
copiedDesc: 'Your recovery phrase has been copied.',
qrWindowTitle: 'Recovery Phrase QR Code',
qrHeading: 'Your Recovery Phrase QR Code',
qrAlt: 'Recovery Phrase QR Code',
qrWarning: 'Keep this QR code private and secure!',
qrPrint: 'Print QR Code',
qrFailedTitle: 'QR Code failed',
qrFailedDesc: 'Could not generate QR code.',
accountCreatedTitle: 'Account Created! 🎉',
accountCreatedDesc: 'You\'re all set! Your recovery phrase is secure.',
loginFailedTitle: 'Login Failed',
loginFailedDesc: 'Failed to create account. Please try again.',
verifyFailedTitle: 'Verification Failed',
verifyFailedDesc: 'The words don\'t match. Please check and try again.',
titlePrefix: 'Create Account with',
titleSuffix: 'Recovery Phrase',
twelveWordTitle: '12-Word Recovery Phrase',
twelveWordDesc: 'Your recovery phrase is like a master password. Write it down on paper and keep it safe. You\'ll need it to access your account from other devices or if you clear your browser data.',
generate: 'Generate Recovery Phrase',
useNsecInstead: 'Use advanced nsec key instead',
yourPhrase: 'Your Recovery Phrase',
copy: 'Copy',
download: 'Download',
qrCode: 'QR Code',
criticalWarning: 'Critical Security Warning',
warnWriteStrong: 'Write it down',
warnWrite: 'on paper and store it safely',
warnNeverShareStrong: 'Never share',
warnNeverShare: 'your recovery phrase with anyone',
warnBackupsStrong: 'Keep multiple backups',
warnBackups: 'in different secure locations',
warnAnyoneStrong: 'Anyone with this phrase',
warnAnyone: 'controls your account forever',
warnLoseStrong: 'If you lose it',
warnLose: 'you lose your account permanently',
savedSecurely: 'I\'ve Saved It Securely',
verifyTitle: 'Verify Your Recovery Phrase',
verifyDesc: 'To make sure you\'ve saved it correctly, please enter these words:',
wordLabel: 'Word #{{n}}',
wordPlaceholder: 'Enter word #{{n}}',
goBack: 'Go Back',
verifyCreate: 'Verify & Create Account',
completeTitle: 'Account Created!',
completeDesc: 'Your Nostr account is ready to go!',
signerTitle: 'Recommended: Set Up a Signer App',
signerSubtitle: 'Keep your keys extra secure',
signerDesc: 'For better security, use a dedicated signer app instead of pasting your recovery phrase everywhere. Signer apps keep your keys safe while letting you use any Nostr app.',
signerCta: 'Learn About Signer Apps',
skip: 'Skip for now',
},
signupMethod: {
titlePrefix: 'Choose Your',
titleSuffix: 'Signup Method',
subtitle: 'Both methods are secure. Choose what works best for you!',
recommended: 'Recommended',
mnemonicTitle: 'Recovery Phrase',
mnemonicDesc: '12 easy-to-remember words. Industry standard used by Bitcoin, Ethereum, and most crypto wallets.',
mnemonicF1: 'Easy to write down on paper',
mnemonicF2: 'Works with other Nostr apps (NIP-06)',
mnemonicF3: 'Can generate multiple accounts',
mnemonicF4: 'Familiar to crypto users',
useMnemonic: 'Use Recovery Phrase',
advanced: 'Advanced',
nsecTitle: 'Secret Key (nsec)',
nsecDesc: 'Raw cryptographic key. More technical but gives you direct access to your Nostr identity.',
nsecF1: 'Direct cryptographic control',
nsecF2: 'Compatible with all Nostr apps',
nsecF3: 'Single string to backup',
nsecF4: 'No derivation layer',
useNsec: 'Use Secret Key',
footer: '🔒 Both methods are equally secure. Choose based on your preference!',
},
skateNip05: {
placeholder: 'yourname',
claimedSuffix: 'is yours — save your profile to publish it.',
checking: 'Checking availability…',
available: '{{name}}@{{domain}} is available',
claim: 'Claim it',
hint: 'Letters, numbers, dashes. Free, and shown as a ✓ badge on your profile.',
},
nip05Required: {
getFree: 'Get a free / cheap NIP-05',
paid: 'Paid',
requiredTitle: 'NIP-05 Verification Required',
bannerReason: 'To prevent spam and build trust, {{role}} need a verified Nostr address (NIP-05).',
gateReason: 'To prevent spam and build trust, {{role}} must have NIP-05 verification.',
addInSettings: 'Add NIP-05 in Settings',
explainer: 'NIP-05 proves your identity by linking your Nostr profile to a domain — like',
updateProfile: 'Update Your Profile',
},
editProfile: {
uploadSuccessTitle: 'Success',
uploadSuccessDesc: '{{field}} uploaded successfully',
uploadErrorDesc: 'Failed to upload {{field}}. Please try again.',
mustBeLoggedIn: 'You must be logged in to update your profile',
invalidClinkTitle: 'Invalid CLINK code',
invalidClinkDesc: 'That doesn\'t look like a valid noffer1… code. Clear it or paste a valid one.',
invalidLightningTitle: 'Invalid Lightning address',
invalidLightningDesc: 'Use an email-like address such as you@walletofsatoshi.com.',
invalidSpTitle: 'Invalid Silent Payment address',
invalidSpDesc: 'A Silent Payment address starts with "sp1" and is quite long. Clear it or paste a valid one.',
invalidCordnTitle: 'Invalid Cordn link',
invalidCordnDesc: 'Paste a full https://… Cordn contact link.',
updateSuccessTitle: 'Success',
updateSuccessDesc: 'Your profile has been updated',
updateErrorDesc: 'Failed to publish profile. Check your relay connections in Settings > Relays',
identityTitle: 'Identity',
identityDesc: 'How you show up across SkateSpots.',
displayName: 'Display name',
displayNamePlaceholder: 'Your name',
bio: 'Bio',
bioPlaceholder: 'A few words about you and your skating…',
profilePicture: 'Profile picture',
pictureDesc: 'Upload or paste a square image.',
bannerImage: 'Banner image',
bannerDesc: 'A wide image for the top of your profile.',
website: 'Website',
bitcoinTitle: 'Bitcoin & Payments',
bitcoinDesc: 'Let people zap, tip and pay you directly — no middleman.',
lightningAddress: 'Lightning address',
lud16Invalid: 'Use an email-like address, e.g. you@yourdomain.com.',
lud16Ok: 'Powers the “Zap” button on your profile.',
lud16Hint: 'Instant Bitcoin tips. Powers the “Zap” button on your profile.',
silentPaymentAddress: 'Silent Payment address',
spInvalid: 'Should start with “sp1” and be quite long.',
spOk: 'Valid — a reusable, private on-chain address.',
spHint: 'Reusable static on-chain address (BIP-352). Share once; every payment is unlinkable.',
clinkTipCode: 'CLINK tip code',
nofferInvalid: 'That doesn’t look like a valid noffer1… code.',
nofferOk: 'Valid — a “Tip in Bitcoin” button will show on your profile.',
nofferHint: 'Self-custodial CLINK code so anyone can tip you over Nostr — no middleman.',
skateProfileTitle: 'Skate Profile',
skateProfileDesc: 'Reppin’ your sponsors and how the crew reaches you.',
badges: 'Badges',
badgesHint: '(label + optional link)',
badgeExample: 'e.g. Independent Trucks',
badgeLabelPlaceholder: 'Badge label…',
badgeUrlPlaceholder: 'https://… (optional)',
removeBadge: 'Remove badge',
addBadge: 'Add badge',
badgesDescBefore: 'Sponsors, shops or people who back your skating — and anything else you want to show off. Each becomes a clickable chip on your profile. Your',
badgesDescAfter: 'member badge is added automatically.',
coordinator: 'Coordinator',
cordnInvalid: 'That doesn\'t look like a coordinator. Paste an nprofile, a cordn.net link, or an https:// URL.',
cordnOk: 'Shown on your profile — others can add it in one tap.',
cordnHint: 'The Cordn coordinator others use to message you. Private, encrypted, yours.',
useMyCoordinator: 'Use my coordinator',
accountTitle: 'Account',
accountDesc: 'Extra account settings.',
botAccount: 'Bot account',
botAccountDesc: 'Mark this account as automated or a bot.',
saving: 'Saving…',
saveProfile: 'Save Profile',
nip05VerifiedHandle: 'NIP-05 verified handle',
nip05How: 'How?',
extraHandles: 'Extra handles',
extraHandlesHint: 'optional — add up to 5',
nip05HintEmpty: 'A verified handle like you@yourdomain.com — Nostr’s version of a blue check. Shows a ✓ badge on your profile and unlocks the “Verified” filter in the Skaters directory.',
nip05Invalid: 'Use an email-like handle, e.g. you@yourdomain.com — no “https://” or spaces.',
nip05Checking: 'Checking your domain…',
nip05Verified: 'Verified — your domain confirms this handle belongs to you. ✓',
nip05Mismatch: 'Your domain maps this handle to a DIFFERENT key. Fix your .well-known/nostr.json so it points to this account.',
nip05Unreachable: 'Couldn’t reach your domain’s /.well-known/nostr.json yet. Saved handles still work once your domain is set up correctly.',
nip05Enter: 'Enter your NIP-05 handle.',
freeHandleBefore: 'No domain of your own? Claim a free',
freeHandleAfter: 'handle from SkateSpots.',
uploadLabel: 'Upload {{label}}',
previewLabel: '{{label}} preview',
},
security: {
recoveryCopied: 'Recovery code copied. Store it somewhere safe.',
savedTitle: 'Saved!',
recoveryDownloaded: 'Recovery code downloaded. Keep it somewhere safe.',
downloadFailedTitle: 'Download failed',
downloadFailedDesc: 'Please copy it manually instead.',
loginRequired: 'Login Required',
loginRequiredDesc: 'Log in to access security settings.',
backupConfirmedTitle: 'Backup Confirmed! ✅',
backupConfirmedDesc: 'Great! Your keys are safe.',
yourRecoveryCode: 'Your Recovery Code',
recoveryCodeDesc: 'Didn\'t save it when you signed up? You can grab it here anytime.',
recoveryCodeBefore: 'This code is the',
only: 'only',
recoveryCodeAfter: 'way to log back in on a new device or browser. Save it somewhere safe (a password manager, or written on paper). Anyone who has it controls your account — never share it.',
revealRecovery: 'Reveal my recovery code',
qrHint: 'Scan with another Nostr app to log in. Keep this screen private.',
neverShareHide: '⚠️ Never share this. Hide it when you\'re done.',
hide: 'Hide',
amberNoteBefore: 'You\'re logged in with',
amberNoteAfter: '. Your recovery code lives inside Amber — open the Amber app to back it up. SkateSpots never sees your key.',
bunkerNoteBefore: 'You\'re logged in with a',
remoteSigner: 'remote signer (bunker)',
bunkerNoteAfter: '. Your recovery code is held by your signer service, not SkateSpots. Back it up there.',
extensionNoteBefore: 'You\'re logged in with a',
browserExtension: 'browser extension',
extensionNoteAfter: '. Your recovery code lives in the extension — use its own backup/export option. SkateSpots never sees your key.',
otherNote: 'Your recovery code is managed by your signer app, not SkateSpots. Back it up there.',
backupWarningTitle: '⚠️ Have you backed up your recovery phrase or secret key?',
backupWarningBody: 'Your keys are the ONLY way to access your account. If you lose them, you lose everything - there\'s no password reset!',
iveBackedUp: 'I\'ve Backed Up My Keys',
backupConfirmed: '✅ Backup Confirmed',
backupNotConfirmed: '⚠️ Backup Not Confirmed',
backupConfirmedStatusDesc: 'You\'ve confirmed that your keys are safely backed up.',
backupNotConfirmedDesc: 'Make sure to backup your recovery phrase or secret key!',
notSavedYetBefore: 'Haven\'t saved your recovery code yet? No problem — grab it from the',
notSavedYetAfter: 'section above, store it somewhere safe, then confirm below.',
yesBackedUp: 'Yes, I\'ve Backed Up My Keys',
bestPracticesTitle: 'Key Security Best Practices',
bestPracticesDesc: 'How to keep your Nostr account safe',
bp1Title: 'Write it down on paper',
bp1Desc: 'Physical backup is the safest. Store in a secure location like a safe.',
bp2Title: 'Multiple backups',
bp2Desc: 'Keep copies in different secure locations (home safe, bank deposit box, etc.)',
bp3Title: 'Never share your keys',
bp3Desc: 'Real Nostr apps NEVER ask for your nsec or recovery phrase after initial login',
bp4Title: 'Don\'t store in cloud unencrypted',
bp4Desc: 'Avoid Google Docs, email drafts, or plain text cloud storage',
bp5Title: 'Consider using a browser extension',
bp5Desc: 'Alby, nos2x, or Flamingo keep your keys secure and never expose them to websites',
importantReminders: '⚠️ Important Reminders',
rem1: 'If you lose your keys, you lose your account permanently',
rem2: 'No company or person can reset your password - not even us',
rem3: 'Anyone with your keys has full control of your account',
rem4: 'Your keys control all your Nostr data across ALL apps',
recommendedSigners: '🔌 Recommended Nostr Signers',
recommendedSignersDesc: 'Use a Nostr signer for better security and convenience',
recommended: 'Recommended',
amberDesc: 'Secure Nostr signer for Android. Keeps your keys safe on your phone and signs events without exposing them.',
albyDesc: 'Full-featured extension with Lightning wallet, WebLN, and NWC support.',
flamingoDesc: 'Simple, clean interface. Focuses on Nostr signing without wallet features.',
nos2xDesc: 'Minimal, lightweight extension for advanced users.',
nostrConnectTitle: '☁️ Nostr Connect (Remote Signers)',
nostrConnectDesc: 'Use a remote signer to access your account from any device',
nostrConnectBody: 'Nostr Connect (also called "bunkers") lets you store your keys on a secure server and sign in from any device. Your keys never leave the server, but you can use your account anywhere!',
nsecAppDesc: 'Free, secure remote signer. Create bunker:// URI and paste when logging in.',
nostrumDesc: 'Another remote signer option with mobile support.',
howToUse: '💡 How to use:',
howToUseDesc: 'Create an account on one of these services, get your bunker:// connection string, then paste it in the "Nostr Connect" tab when logging in.',
},
relayList: {
connected: 'Connected',
connecting: 'Connecting…',
offline: 'Offline',
removeUrl: 'Remove {{url}}',
add: 'Add',
publishedTitle: 'Relay list published',
publishedDesc: 'Synced to Nostr (kind 10002).',
publishFailedTitle: 'Failed to publish relay list',
invalidUrlTitle: 'Invalid relay URL',
invalidUrlDesc: 'Try something like wss://relay.example.com',
alreadyInList: 'Relay already in your list',
keepReadWriteTitle: 'Keep read or write on',
keepReadWriteDesc: 'A relay needs at least one of read/write.',
alreadyInThisList: 'Relay already in this list',
dmPublishedTitle: 'DM relays published',
dmPublishedDesc: 'NIP-17 inbox updated.',
dmPublishFailedTitle: 'Failed to publish DM relays',
nip65Title: 'Relays (NIP-65)',
nip65DescUser: 'Tap Read / Write to toggle. Published to Nostr as your kind 10002 list.',
nip65DescLoggedOut: 'Tap Read / Write to toggle. Log in to publish your list to Nostr.',
noRelays: 'No relays yet — add one below.',
local: 'Local',
read: 'Read',
readHint: 'Fetch content from this relay',
write: 'Write',
writeHint: 'Publish your events to this relay',
dmTitle: 'DM inbox relays',
dmDesc: 'Where you receive NIP-17 direct messages (kind 10050). Falls back to your write relays when empty.',
localTitle: 'Local relays',
localDesc: 'App-only (e.g. a LAN node). Used for reads and writes; never published.',
blockedTitle: 'Blocked relays',
blockedDesc: 'Never connect to these — excluded from every read and write.',
},
relatr: {
searchingTitle: 'Searching for Relatr service...',
searchingDesc: 'Querying Nostr for Relatr profiles',
foundTitle: 'Found Relatr service!',
foundDesc: 'Service: {{name}}',
unknown: 'Unknown',
notFoundTitle: 'No Relatr service found',
notFoundDesc: 'Try entering the pubkey manually or check https://www.relatr.xyz/',
searchFailedTitle: 'Search failed',
searchFailedDesc: 'Could not query Nostr for Relatr profiles',
invalidPubkeyTitle: 'Invalid pubkey',
invalidPubkeyDesc: 'Please enter a valid 64-character hex pubkey',
enabledTitle: 'Relatr enabled!',
enabledDesc: 'Global trust scores are now active.',
disabledTitle: 'Relatr disabled',
disabledDesc: 'Trust scores are no longer being fetched.',
title: 'Relatr Trust Scores',
desc: 'Global trust scores help identify reliable users and reduce spam',
status: 'Status',
active: 'Active',
disabled: 'Disabled',
activeStrong: 'Relatr is active.',
activeDesc: 'Trust scores from ContextVM\'s global network analysis help identify reliable skaters and reduce spam.',
disabledStrong: 'Relatr is disabled.',
disabledHint: 'Enable it to get trust scores from the global Nostr network.',
disable: 'Disable Relatr',
enable: 'Enable Relatr',
advancedSettings: 'Advanced Settings',
servicePubkey: 'Relatr Service Pubkey',
pubkeyPlaceholder: 'Enter 64-character hex pubkey...',
searching: 'Searching...',
autoDiscover: 'Auto-Discover',
pubkeyDefault: 'Default: ContextVM\'s official Relatr service (npub1dvmc...)',
relayLabel: 'Relay (where Relatr publishes scores)',
relayDefault: 'Default: wss://relay.contextvm.org (ContextVM\'s official relay)',
applyChanges: 'Apply Changes',
learnMore: 'Learn More:',
website: 'Relatr Website',
github: 'Relatr GitHub',
nip85Spec: 'NIP-85 Specification',
},
followList: {
unfollow: 'Unfollow',
title: 'Your Nostr Network',
desc: 'Manage who you follow. Following users helps build your Web of Trust and determines which spots show trust badges.',
following: 'Following ({{count}})',
followers: 'Followers ({{count}})',
follow: 'Follow',
followHint: '💡 Paste a Nostr pubkey (npub1...), NIP-05 address (user@domain.com), or hex pubkey',
notFollowing: 'You\'re not following anyone yet',
notFollowingHint: 'Follow skaters to see their spots with ✅ VERIFIED badges',
trustActiveStrong: '✅ Trust Badges Active:',
trustActive: 'Spots created by people you follow will now show VERIFIED badges. Spots by their friends will show TRUSTED badges.',
noFollowers: 'No followers yet',
noFollowersHint: 'Share your Nostr profile to gain followers!',
howTitle: '💡 How Following Affects Trust Badges',
verifiedStrong: '✅ VERIFIED (Green):',
verifiedDesc: 'Spots created by people you directly follow',
trustedStrong: '🟡 TRUSTED (Yellow):',
trustedDesc: 'Spots created by people your friends follow (2 hops)',
unverifiedStrong: '⚪ Unverified:',
unverifiedDesc: 'Spots by users with no connection to your network',
tip: '💡 Tip: Follow active skateboarders to see more verified spots and build your trust network!',
},
notify: {
enableTitle: 'Enable notifications',
enableDesc: 'Buzz this device when something needs you.',
checkingPermission: 'Checking permission…',
unsupported: 'This device doesn\'t support notifications here. Rebuild the Android app to enable them on your phone.',
denied: 'Notifications are blocked for this site. Allow them in your browser/OS settings, then try again.',
backgroundTitle: 'Background notifications',
backgroundDesc: 'Get pinged for messages and trades even when SkateSpots is closed.',
pushUnsupported: 'This browser doesn\'t support background push. On mobile, add SkateSpots to your home screen.',
pushPrivacy: 'To reach you while closed, a SkateSpots notification server gets a ping from your relay when something arrives for your key and passes it to your phone\'s push service. It sees only that your key has something — never who sent it or what it says, and it can\'t decrypt your messages. Opt in per device.',
bgDeliveryTitle: 'Background delivery (Android)',
bgDeliveryDesc: 'Check for new messages, offers and trades while the app is closed — no Google services.',
off: 'Off',
standard: 'Standard (~15 min)',
fast: 'Fast (~5 min)',
bgDeliveryNote: 'Standard is best-effort — Android may defer it under Doze. Fast shows a persistent notification and uses more battery. Nothing is decrypted on our server.',
notifyMeAbout: 'Notify me about',
couldNotEnable: 'Could not enable',
sendTest: 'Send a test notification',
testTitle: 'SkateSpots notifications are on 🛹',
testBody: 'This is how a DM, zap or crew invite will look.',
footerBefore: 'Alerts are built on your device from data the app already has, and direct-message alerts never decrypt your messages — so Amber is never asked to open one. With',
footerAfter: 'off, a fully closed app can\'t be reached, so alerts arrive next time you open SkateSpots.',
cat_dms_label: 'Direct messages',
cat_dms_hint: 'Someone sends you a private message',
cat_crew_label: 'Crews',
cat_crew_hint: 'Invites, join requests and membership confirmations',
cat_social_label: 'Zaps, replies & mentions',
cat_social_hint: 'Reactions, reposts, zaps, replies and event updates',
cat_newsletter_label: 'Newsletter',
cat_newsletter_hint: 'New SkateSpots drops (subscribers only)',
cat_trades_label: 'Offers & trades',
cat_trades_hint: 'Offers, counter-offers and trade confirmations',
cat_reminders_label: 'Event reminders',
cat_reminders_hint: 'A nudge an hour before events you\'re going to (Android app)',
},
regionBackfill: {
taggedTitle: 'Tagged {{count}} location{{s}}',
taggedSkipped: '{{count}} skipped (no coordinates or no geocoder match).',
taggedOk: 'Region tags written to the community relay.',
nothingTitle: 'Nothing to tag',
nothingDesc: 'No pins could be geocoded. Try again later.',
failedTitle: 'Backfill failed',
title: 'Backfill region tags',
admin: 'Admin',
descBefore: 'Pins made before geocoding existed have coordinates but no city/country tag, so the map draws them while Community Stats can only say “Uncategorised”. This re-derives the tags from each pin and republishes',
descStrong: 'your own',
descAfter: 'untagged spots & shops.',
scanning: 'Scanning your spots…',
allTagged: 'Every spot & shop you own already has a region tag. 🎉 Any remaining “Uncategorised” pins belong to other mappers — only their own keys can tag them.',
countLine: '{{count}} of your spot{{s}}/shops have no region tag. Your signer may ask to approve each one.',
tagging: 'Tagging {{done}}/{{total}}…',
run: 'Backfill region tags',
summary: '{{updated}} updated · {{skipped}} skipped · {{failed}} failed',
},
adminReview: {
title: 'Review queue',
flagged: '{{count}} spot{{s}} trip the junk signals — hide or dismiss.',
clean: 'Spots that look like test entries or off-topic pins.',
},
grantPro: {
invalidNpub: 'Enter a valid npub (or hex pubkey)',
invalidDays: 'Days must be a positive number',
grantedTitle: 'Pro granted ⚡',
grantedDesc: '{{npub}} until {{date}}',
failedTitle: 'Grant failed',
title: 'Grant Sasquatch Pro',
owner: 'Owner',
desc: 'Manual fallback for a verified payment the zap receipt missed. Publishes an owner-signed grant; the user can\'t forge it.',
targetPlaceholder: 'npub1… (or hex)',
grant: 'Grant',
},
myData: {
reactionTitle: '{{emoji}} reaction',
deleting: 'Deleting...',
requestDeletion: 'Request Deletion',
selectContentTitle: 'Select Content to Delete',
selectContentDesc: 'Select the specific items you want to request deletion for.',
all: 'All',
spots: 'Spots',
events: 'Events',
shops: 'Shops',
listings: 'Listings',
collections: 'Collections',
lab: 'Lab',
posts: 'Posts',
reactions: 'Reactions',
selectAll: 'Select All',
selectAllVisible: 'Select All Visible',
selectedCount: '{{count}} selected',
noItemsCategory: 'No items in this category',
decentralizedStrong: 'Important:',
decentralizedNote: 'Nostr is decentralized. Relays should honor deletion requests, but some may not. This is not a guaranteed permanent deletion.',
deleteCount: 'Delete {{count}} Item{{s}}',
noDataTitle: 'No data to export',
noDataDesc: 'You haven\'t published any events yet.',
exportedTitle: 'Data exported!',
exportedDesc: 'Downloaded {{count}} events as JSON.',
deletionRequestedTitle: 'Deletion requested',
deletionRequestedDesc: 'The deletion request has been published to relays.',
deletionRequestedMany: 'Requested deletion of {{count}} items. Note: Some relays may not honor this request.',
deletionFailedTitle: 'Deletion failed',
deletionFailedDesc: 'Could not publish deletion request. Please try again.',
loginRequired: 'Login Required',
loginRequiredDesc: 'Log in to view and manage your data.',
title: 'My Data',
desc: 'Your content on the Nostr network. You own this data - export it anytime or request deletion.',
coachFilmer: 'Coach/Filmer',
zaps: 'Zaps',
received: 'Received',
exportAll: 'Export All Data',
recentActivity: 'Recent Activity',
viewAll: 'View All ({{count}})',
noDataPublished: 'No data published yet',
startBy: 'Start by',
addingASpot: 'adding a spot',
aboutYourData: 'About Your Data',
ownDataStrong: 'You own your data.',
ownData: 'All content is signed with your cryptographic keys and stored on Nostr relays. No company controls it.',
exportAnytimeStrong: 'Export anytime',
exportAnytime: '- Download all your events as JSON',
portableStrong: 'Portable',
portable: '- Use your keys in any Nostr app',
selectiveStrong: 'Selective deletion',
selective: '- Choose exactly what to delete',
caveatStrong: 'Decentralized caveat',
caveat: '- Some relays may retain data despite deletion requests',
},
settingsPage: {
seoTitle: 'Settings - SkateSpots',
seoDescription: 'Manage your SkateSpots account settings.',
heading: 'SETTINGS',
subtitle: 'Account and app preferences',
editProfile: 'Edit Profile',
profileRefreshed: 'Profile refreshed!',
found: 'Found: {{name}}',
unnamed: 'unnamed',
profileNotFound: 'Profile not found',
notFoundOnRelay: 'Not found on any relay.',
refreshFailed: 'Refresh failed',
refreshFailedDesc: 'Could not connect to relays.',
loginToEdit: 'Log in to edit your profile',
noProfileRelays: 'We couldn\'t find a profile on the relays. If you\'ve set one before, try refreshing.',
checkDisplayName: 'a display name',
checkPhoto: 'a profile photo',
checkBio: 'a short bio',
checkLightning: 'a Lightning address so people can zap you',
checkHandle: 'a free @skate-spots.org handle',
finishProfile: 'Finish your profile',
addItem: 'Add {{item}}',
nip05Title: 'NIP-05 Verification',
nip05Desc: 'Get a verified Nostr address (like name@domain.com) to build trust. Required for creating events, coaching profiles, and shops.',
freeFromSkateSpots: 'Free from SkateSpots',
claimHandleBefore: 'Claim a handle like',
claimHandleAfter: '— no signup, no cost, no domain to run. Claim it in the Profile tab, then save.',
claimInProfile: 'Claim in Profile',
nip05Footer: 'After getting your NIP-05, add it in the Profile field and save.',
localRelayOn: 'Local relay on',
localRelayOff: 'Local relay off',
lookingForCitrine: 'Looking for Citrine on this device…',
noLongerUsing: 'No longer using the on-device relay.',
localRelay: 'Local Relay',
citrineOff: 'Faster reads & offline backup on this device',
citrineSearching: 'Searching for Citrine on this device…',
citrineLive: 'Connected — reading & backing up locally',
citrineWaiting: 'Enabled — start Citrine to connect',
toggleCitrine: 'Toggle local Citrine relay',
citrineDetected: 'Citrine relay detected',
citrineLooking: 'Looking for Citrine…',
citrineNotDetected: 'Citrine not detected',
recheckCitrine: 'Re-check for the Citrine relay',
checking: 'Checking',
recheck: 'Recheck',
chipInstantReads: 'Instant reads',
chipWorksOffline: 'Works offline',
chipOnDeviceBackup: 'On-device backup',
cantReachStrong: 'Can\'t reach Citrine.',
cantReachBefore: 'Make sure the free Citrine app is installed and its relay is',
started: 'started',
cantReachAfter: ', then tap Recheck. It runs a private Nostr relay on your phone for instant reads and an offline backup.',
citrineStep1Before: 'Open Citrine → tap',
start: 'Start',
citrineStep2Before: 'Come back here → tap',
getCitrine: 'Get Citrine',
citrinePrivacy: 'Your Stash and messages stay end-to-end encrypted here, exactly like everywhere else. The local relay only speeds things up and keeps a copy on your device — it never replaces encryption.',
communityRelayDisabled: 'Community relay disabled',
communityRelayEnabled: 'Community relay enabled',
usingOnlyPersonal: 'Using only your configured relays.',
skateRelayIncluded: 'SkateSpots relay included.',
relays: 'Relays',
relayPrivacyBefore: 'Relays can see your',
ipAddress: 'IP address',
relayPrivacyAfter: 'when you connect. Tor hides it — and treat a "free" VPN as a warning sign; they usually monetise your traffic.',
communityRelay: 'Community Relay',
syncingGlobal: 'Syncing with global skate community',
usingOnlyPersonalShort: 'Using only personal relays',
appearance: 'Appearance',
language: 'Language',
darkModeOnly: 'Dark Mode Only',
darkModeDesc: 'SkateSpots runs a dark neon aesthetic inspired by skate culture. Black background, neon pink, lime, and orange.',
navigation: 'Navigation',
sasquatchExpanded: 'The Sasquatch has been expanded! 🦧 Refresh the page to see him.',
advanced: 'Advanced',
sasquatchGuide: '🦧 Sasquatch Guide',
sasquatchResetDesc: 'Reset the onboarding guide to expanded view',
expandSasquatch: 'Expand The Sasquatch',
wallet: 'Wallet',
loginToWallet: 'Log in to manage your wallet',
openWallet: 'Open Wallet',
balanceSendReceive: 'Balance, send, receive',
connectNwc: 'Connect NWC Wallet',
account: 'Account',
security: 'Security',
securitySub: 'Keys, signing, Amber',
nip05Sub: 'Get a verified Nostr address',
app: 'App',
myStash: 'My Stash',
myStashSub: 'Private gear inventory',
relaysSub: 'Nostr relay connections',
appearanceSub: 'Language and theme',
assistant: 'Assistant',
showSasquatch: 'Show Sasquatch assistant',
showSasquatchDesc: 'Show the floating Sasquatch button on the map and other screens.',
showMapFooter: 'Show map links bar',
showMapFooterDesc: 'Show the thin links strip at the bottom of the map.',
notifications: 'Notifications',
notificationsSub: 'Alerts for DMs, crews and zaps',
coordinator: 'Coordinator',
coordinatorSub: 'Your Cordn group server',
data: 'Data',
myData: 'My Data',
myDataSub: 'Export, import, manage',
advancedSub: 'Relatr, Sasquatch, tools',
notLoaded: 'Not loaded',
notLoggedIn: 'Not logged in',
},
onboarding: {
seoTitle: 'Get Started — SkateSpots',
seoDescription: 'Join the decentralized skateboarding community. Find spots, connect with coaches, send zaps. Free forever.',
homeSetTitle: 'Home set 🛹',
homeSetDesc: 'The map will now open on your area.',
locationFailedTitle: 'Couldn\'t get your location',
locationFailedDesc: 'Check location permissions, or pick your area from the map.',
niceTitle: '🎉 Nice!',
niceDesc: 'You now understand how Nostr works. Let\'s go skate!',
npubCopied: 'npub copied to clipboard.',
saveLoginNow: 'Save your login — do this now',
saveLoginNowDesc: 'Your recovery code is the only way back into your account. No email, no password reset.',
betaBadge: 'Beta — Open to skaters worldwide',
headline1: 'SKATE',
headline2: 'YOUR WORLD',
heroBefore: 'A',
decentralized: 'decentralized',
heroAfter: 'skate community. No company owns it. No algorithm buries your spots. No ban hammer. Just skaters.',
pillMap: 'Spot map',
pillCoaches: 'Coaches & filmers',
pillEvents: 'Events',
pillGear: 'Buy/sell gear',
pillZaps: 'Bitcoin zaps',
pillStash: 'Gear stash',
pillCrews: 'Crews',
createFreeAccount: 'Create your free account',
noEmailNoPassword: 'No email. No password. Own your identity forever.',
findSpots: 'Find Spots Near You',
addFirstSpot: 'Add Your First Spot',
accountCreated: 'You\'re in! Account created ✓',
identityYours: 'Your identity is yours — stored in your key, not on our servers.',
whereSkate: 'Where do you skate?',
whereSkateDesc: 'Set your home area once and the map opens on your spots every time. Your GPS never leaves your device.',
useMyLocation: 'Use my location',
pickOnMap: 'Pick on the map',
loginSaved: 'Login saved — your account is safe to recover.',
whyBother: 'Why bother?',
why1Title: 'Every spot, one map',
why1Body: 'Ledges, rails, DIY, parks — mapped by skaters who actually ride them. Check the status before you roll out so you don’t waste a session on a bust.',
why2Title: 'Built by skaters, not suits',
why2Body: 'No ads, no algorithm shoving junk in your face, no data harvesting. Open source. The map belongs to the community — including you.',
why3Title: 'Get paid to put on',
why3Body: 'Map a gem, film a banger, run a session — and get zapped real Bitcoin straight to your wallet. No platform cut, no middleman.',
why4Title: 'Your account is actually yours',
why4Body: 'One key logs you into every Nostr app. No company can lock you out or delete your account on a whim. Take your identity anywhere.',
everythingFree: 'Everything you can do — free',
featSpots: 'Discover Spots',
featSpotsDesc: 'Interactive map with thousands of spots worldwide',
featLab: 'The Lab',
featLabDesc: 'Find coaches, filmers & video editors near you',
featEvents: 'Events',
featEventsDesc: 'Competitions, sessions, meetups',
featGear: 'Buy / Sell Gear',
featGearDesc: 'Peer-to-peer marketplace for used skate gear',
featStash: 'My Stash',
featStashDesc: 'Private encrypted inventory of everything you own',
featCrews: 'Crews',
featCrewsDesc: 'Roll with your crew — shared spots, chat & sessions',
whatIsNostr: 'What is Nostr?',
whatIsNostrSub: 'The protocol that powers SkateSpots',
nostrBefore: 'Nostr is an open protocol — like email, but for social apps. Your account is a',
cryptoKeyPair: 'cryptographic key pair',
nostrAfter: 'that you own. No single company controls your account or your data — you choose which relays store it, and you can even run your own. Your identity works across every Nostr app.',
nostrPoint1: 'You own your data & keys',
nostrPoint2: 'No company can ban you',
nostrPoint3: 'Identity works everywhere',
nostrPoint4: 'Direct Bitcoin tips',
deepDive: 'Deep dive: How Nostr works',
privacyTip: 'Privacy tip: your GPS never leaves your device. Servers you connect to (like map tiles) can still see your IP — if you want to hide that too, a VPN or Tor works. Totally optional.',
readPrivacy: 'Read our privacy page',
zapsWallet: '⚡ Zaps & Wallet',
optional: 'Optional',
walletDesc: 'Zaps are Bitcoin tips you send directly to a skater who dropped a sick spot, filmed a banger clip, or ran a great session. No platform fee. No middleman. 100% goes to them.',
builtInWallet: 'Built-in Cashu wallet',
builtInWalletDesc: 'Receive Lightning payments right in the app. No setup.',
balance: 'Balance',
connectExternal: 'Connect external wallet',
connectExternalDesc: 'Have Alby, Mutiny, or another wallet? Connect it via NWC.',
connectViaNwc: 'Connect via NWC',
loginToConnect: 'Log in to connect your own wallet.',
fiveDollars: '💡 $5 of Bitcoin = hundreds of zaps to spread across the community',
commonQuestions: 'Common questions',
faq1Q: 'Is SkateSpots free?',
faq1A: '100% free. Browse spots, add locations, find coaches, create events, join sessions — all free, forever. Bitcoin (zaps) is optional if you want to tip creators.',
faq2Q: 'What is Nostr and why does SkateSpots use it?',
faq2Before: 'Nostr is an open protocol for decentralized apps — think of it like the internet itself, not owned by any company. Your SkateSpots account is a cryptographic key pair that',
faq2Strong: 'you own',
faq2After: '. No single company can ban your account or hold your data hostage — you choose which relays store your data, and you can even run your own. Your identity also works on other Nostr apps like Primal, Damus, or Amethyst.',
faq3Q: 'What\'s an nsec and why is it important?',
faq3Before: 'Your',
faq3Mid: 'is your private key — it\'s the only proof you own your account. Unlike passwords, there is',
faq3Strong: 'no "forgot my password"',
faq3After: '. If you lose your nsec, your account is gone forever. When you create an account, download your backup immediately and store it somewhere safe (password manager, written down offline).',
faq4Q: 'What signers / apps work with SkateSpots?',
faq4Desktop: 'On desktop:',
faq4Extensions: 'browser extensions.',
faq4Android: 'On Android:',
faq4AndroidAfter: '(best experience with the native app).',
faq4Ios: 'On iOS:',
faq4IosAfter: 'or paste your nsec directly (less secure).',
faq4Note: 'You can also log in by pasting your nsec directly — but using a signer extension is safer.',
faq5Q: 'Can I use SkateSpots on my phone?',
faq5Before: 'Yes! SkateSpots is a PWA (Progressive Web App). Visit',
faq5Mid: 'in your phone\'s browser and tap',
faq5Em: '"Add to Home Screen"',
faq5After: 'for a full app experience. Or download it from',
faq5End: '— a decentralized app store.',
faq6Q: 'What is a zap?',
faq6A: 'A zap is an instant Bitcoin tip sent over the Lightning Network. When you zap a skater, the sats land in their wallet within seconds — no PayPal, no Venmo, no cut taken. The person who filmed that 4-stair gets paid directly by fans.',
faq7Q: 'Will my spots be deleted if SkateSpots shuts down?',
faq7A: 'No. Your spots are published to Nostr relays — distributed servers that anyone can run. Even if SkateSpots as a product disappeared tomorrow, your data would still exist on relays and any Nostr client could display it.',
faq8Q: 'Can I add a spot that\'s on private property?',
faq8A: 'You can, but use common sense. Spots on private property should be marked accordingly so skaters know the deal. SkateSpots is not responsible for trespassing — always be respectful of owners and locals.',
faq9Q: 'How do I report a bug or give feedback?',
faq9Before: 'DM the SkateSpots team directly on Nostr, or reach out to our beta coordinator:',
faq9Link: 'Beta feedback → cordn.net',
readyToShred: 'READY TO SHRED?',
stopScrolling: 'Stop scrolling. Get the app. Find a spot. Skate it.',
exploreSpots: 'Explore Spots',
betaFeedbackTitle: 'Beta — We want your feedback',
betaFeedbackDesc: 'SkateSpots is under active development. You may hit rough edges — that\'s on us, not you. Report bugs, suggest features, tell us what sucks.',
betaCoordinator: 'Beta Coordinator',
betaCoordinatorDesc: 'cordn.net → message us',
dmOnNostr: 'DM on Nostr',
fixSameDay: 'Found a bug? We fix it same day. Your reports make the app better for every skater.',
relaySetup: 'Relay setup',
},
relayEdu: {
understandingNostr: 'Understanding Nostr',
stepOf: 'Step {{current}} of {{total}}',
step1Title: 'Understanding Relays',
step1Desc: 'Think of relays like skate shops that share news about spots',
you: 'You',
relays: 'Relays',
skaters: 'Skaters',
s1Good1: 'No company owns your account',
s1Good1Desc: 'Your identity lives on multiple relay servers',
s1Good2: 'You control where your data lives',
s1Good2Desc: 'Your posts sync across relays you choose — pick your own, or run one yourself',
s1Warn: 'Relay operators can see public posts',
s1WarnDesc: 'Think of it like posting on a bulletin board',
step2Title: 'Public vs Private',
step2Desc: 'Know what\'s visible and what\'s encrypted',
publicTitle: 'Public (Anyone Can See)',
pub1: 'Spots you add to the map',
pub2: 'Reviews and comments',
pub3: 'Your profile info (name, picture)',
pub4: 'Who you follow',
privateTitle: 'Private (Encrypted)',
priv1: 'Direct messages',
priv2: 'Wallet communications',
noteStrong: 'Note:',
note: 'Posts may be stored permanently on relays. Think before you post - just like real life!',
step3Title: 'Your Keys = Your Account',
step3Desc: 'This is the most important thing to understand',
secretKeyTitle: 'Your Secret Key (nsec)',
secretKeyDesc: 'Think of it like your skateboard - never lend it to anyone. It proves you own your account.',
s3Bad1: 'If lost, your account can\'t be recovered',
s3Bad2: 'No "forgot password" - nobody can reset it',
s3Good: 'Back it up and store it safely',
publicKeyTitle: 'Your Public Key (npub)',
publicKeyDesc: 'This is like your username - share it freely so others can find you!',
inControl: '🔐 You\'re in control - no company can lock you out!',
skip: 'Skip',
gotIt: 'Got It!',
},
about: {
seoTitle: 'About - SkateSpots',
seoDescription: 'Learn about SkateSpots and our mission to build a decentralized skateboarding community.',
npubCopied: 'npub copied to clipboard. Paste it in your favorite Nostr client to send a DM.',
bugReport: '🐛 Bug Report',
bugWhatHappened: 'What happened:',
bugExpected: 'What I expected:',
bugSteps: 'Steps to reproduce:',
aboutWord: 'ABOUT',
heroBefore: 'Connect with local skate scenes worldwide.',
skaterOwned: 'Skater-owned data',
heroAfter: ', community-run relays.',
viewStats: 'View Community Stats',
missionTitle: 'Our Mission',
mission1: 'SkateSpots helps skateboarders discover spots, connect with pros at The Lab, and organize sessions - all without corporate control. The real skating happens offline, this tool just connects you to it.',
mission2: 'Skateboarding knowledge should live with the community, not locked in corporate apps. That\'s why SkateSpots runs on Nostr - a relay-based system where skaters control their own data, choose which relays store it, and can even run their own. No single company owns the map.',
whyNostr: 'Why Nostr?',
why1Title: 'Community-Controlled',
why1Desc: 'Your spots live on community relays. Skaters run the servers, not corporations.',
why2Title: 'You Own Your Data',
why2Desc: 'Your Nostr identity is yours. Take it to any Nostr app - no lock-in.',
why3Title: 'Bitcoin Integration',
why3Desc: 'Zap creators directly with Bitcoin. No platform taking 30%.',
why4Title: 'Open Source',
why4Desc: 'Built in the open. Anyone can contribute or fork the code.',
getInvolved: 'Get Involved',
getInvolvedDesc: 'SkateSpots is open source and community-driven. Contribute code, share spots, or help others get started.',
betaTitle: 'Beta Testing',
betaDesc: 'Dear User! Thank you for beta testing SkateSpots. Please note that the app is still in beta, and there may be bugs or security issues. If you encounter any problems or want to report a bug/issue, please reach out!',
giveFeedback: 'Give us feedback:',
copyNpub: 'Copy npub',
reportBug: 'Report Bug',
viewProfile: 'View Profile',
downloadApp: 'Download the App',
downloadAppDesc: 'Install SkateSpots on Android via Zapstore — the open-source, Bitcoin-native app store.',
getOnZapstore: 'Get on Zapstore',
credits: 'Credits',
creditPoweredBy: 'Powered by',
creditNostr: 'Decentralized protocol',
creditCrewChat: 'Crew chat via',
creditCordn: 'End-to-end encrypted group chat',
creditEcash: 'Ecash via',
creditCashu: 'Chaumian ecash for Lightning',
creditWalletConnect: 'Wallet connect via',
creditNwc: 'Nostr Wallet Connect (NIP-47)',
creditSigning: 'Signing on Android with',
creditAmber: 'Nostr signer for Android',
creditMapData: 'Bitcoin map data from',
creditBtcmap: 'Places that accept Bitcoin',
creditCrowdfunding: 'Crowdfunding',
creditAngor: 'Decentralized Bitcoin crowdfunding',
creditDistributed: 'Distributed on',
creditZapstore: 'Open-source app store',
creditTrust: 'Trust & crew chat via',
creditContextvm: 'Relatr trust scores & encrypted crew-chat coordination',
stillQuestions: 'Still have questions?',
stillQuestionsDesc: 'The Sasquatch is our AI guide — powered by live SkateSpots data. Ask him about spots, events, coaches, gear, or how anything works.',
askSasquatch: 'Ask The Sasquatch',
poweredBy: 'Powered by',
decentralized: 'Decentralized',
communityOwned: 'Community-Owned',
},
faq: {
seoTitle: 'FAQ - SkateSpots',
seoDescription: 'Ask The Sasquatch anything about SkateSpots.',
greeting: 'Hey! I\'m The Sasquatch 🦧 — ask me anything about SkateSpots. Spots, events, gear, wallets, privacy, Nostr — I\'ve got you. What do you want to know?',
gotQuestions: 'Got Questions?',
heroDesc: 'Ask The Sasquatch — he knows everything about SkateSpots, Nostr, Bitcoin, and skateboarding.',
askTitle: 'Ask The Sasquatch',
askDesc: 'The Sasquatch is open in the bottom-left corner of your screen — just click it and type your question. He has live access to spots, events, coaches, and weather.',
askQuestion: 'What can you help me with on SkateSpots?',
openSasquatch: 'Open The Sasquatch',
commonQuestions: 'Common questions — tap to ask',
quickAnswers: 'Quick answers',
footerHint: 'The Sasquatch uses live Nostr data from the SkateSpots relay + AI to answer your questions. 🛹',
chip1Label: 'What is SkateSpots?',
chip1Q: 'What is SkateSpots and how does it work?',
chip2Label: 'How do I add a spot?',
chip2Q: 'How do I add a skate spot to the map?',
chip3Label: 'What are zaps?',
chip3Q: 'What are zaps?',
chip4Label: 'Is it free?',
chip4Q: 'Is SkateSpots free to use?',
chip5Label: 'What is My Stash?',
chip5Q: 'What is My Stash?',
chip6Label: 'How do I login?',
chip6Q: 'How do I log in or create an account?',
chip7Label: 'What is Nostr?',
chip7Q: 'What is Nostr?',
chip8Label: 'Privacy & Data',
chip8Q: 'What data is public and what data is private on SkateSpots?',
chip9Label: 'How to sell gear?',
chip9Q: 'How do I sell gear on SkateSpots?',
chip10Label: 'What is The Lab?',
chip10Q: 'What is The Lab?',
fact1Label: 'Skate spots worldwide',
fact1Value: 'Discover & add',
fact2Label: 'Bitcoin Lightning zaps',
fact2Value: '100% free',
fact3Label: 'Decentralized on Nostr',
fact3Value: 'You own your data',
fact4Label: 'Private gear inventory',
fact4Value: 'Encrypted Stash',
s1Q: 'What is SkateSpots?',
s1A: 'A decentralized map and community for skateboarding, built on Nostr. Discover spots, find coaches and filmers, join crews, list events, and buy/sell gear — with no company in the middle.',
s2Q: 'How do I add a spot?',
s2A: 'Tap the map, or use the + / "Add Spot" button, then drop a pin and fill in the details. You need a free Nostr account (tap Log In → Create Account — no email required).',
s3Q: 'What are zaps?',
s3A: 'Zaps are Bitcoin Lightning tips. You can zap spots, events, crews and people you appreciate. They go straight to the recipient over Lightning — SkateSpots never holds your funds.',
s4Q: 'Is it free?',
s4A: 'Yes. Creating an account, adding spots and browsing are free. Optional paid extras (like featured listings) and tips (zaps) are paid directly between people.',
s5Q: 'What data is public?',
s5A: 'Anything you publish — spots, events, your profile — is public on Nostr relays. Your private messages and your Stash are end-to-end encrypted. See the Privacy page for details.',
s6Q: 'What is The Lab?',
s6A: 'The Lab is where you find people to hire or learn from: coaches, trainers, filmers, video editors, photographers, builders, artists and more.',
},
privacy: {
seoTitle: 'Privacy — SkateSpots',
seoDescription: 'How SkateSpots handles your data: your GPS stays on your device, spots you publish are public, and how to hide your IP with a VPN or Tor if you want to.',
heroBefore: 'Your',
heroHighlight: 'Privacy',
heroDesc: 'No tracking, no ads, no data selling. Here\'s exactly what stays on your device, what\'s public, and how to lock things down further if you want to.',
locationTitle: 'Your location stays on your device',
locationBefore: 'When you tap',
nearMe: '"Near Me,"',
locationAfter: 'your GPS location is read on your device and used only to center the map and sort nearby spots.',
locationGood1: 'It is never published to Nostr.',
locationGood2: 'It is never sent to SkateSpots — there is no SkateSpots server collecting it.',
locationGood3: 'It is never shared with any third party.',
locationGood4: 'On the native app we use your phone\'s own location system directly and skip Google Play Services — so it works on de-Googled phones (GrapheneOS, /e/OS) and Google never sees it.',
weatherBefore: 'Weather is off by default. When you turn it on, it looks up conditions for the',
weatherEm: 'spot\'s',
weatherAfter: 'public location — not yours.',
publicTitle: 'What\'s public by design',
publicDesc: 'SkateSpots is a shared map, so some things are meant to be seen by everyone. When you choose to publish them, these are public on the Nostr network:',
public1Strong: 'Spots you add',
public1: '— including their coordinates. If you add a spot at your exact location, that location becomes public. Think before you drop a pin on your own driveway.',
public2Strong: 'Reviews, comments, and your public profile',
public2: '(name, picture).',
public3: 'Who you follow, and public reactions.',
public4Strong: 'Zaps',
public4: 'are public by default: a zap ties your account — often your lightning address — to the post you paid and to the tip itself. Your wallet can send it anonymously if you\'d rather it didn\'t.',
publicNoteBefore: 'A VPN or Tor does',
notWord: 'not',
publicNoteAfter: 'hide the content of what you publish — a spot\'s coordinates are public regardless. Privacy tools hide your network identity (your IP), not the things you choose to post.',
encryptedTitle: 'What\'s private & encrypted',
enc1Strong: 'Direct messages',
enc1Mid: 'and',
enc1Strong2: 'crew chats',
enc1: 'are end-to-end encrypted (NIP-17). Only the participants can read them.',
enc2Strong: 'My Stash',
enc2: '— your private gear inventory — is encrypted to your own key.',
enc3Strong: 'Crew-only members',
enc3: 'can choose to be visible only to their crew-mates.',
ipTitle: 'What leaves your device: your IP',
ipBefore: 'Like every website, the servers SkateSpots talks to can see your',
ipAddress: 'IP address',
ipAfter: '(a rough indicator of your region). This is normal for any web or map app and isn\'t unique to us. Those servers are:',
ipTileStrong: 'Map tiles',
ipTile: '(OpenStreetMap) — see your IP and which area of the map you look at.',
ipGeoStrong: 'Spot/shop geocoding',
ipGeo: '— when you create or edit a spot or shop, its coordinates go to OpenStreetMap Nominatim to fill in the city/country tag. When you open Community Stats, the coordinates of pins that have no location tag go to BigDataCloud so they can be grouped by region (cached on your device — at most once per pin). Both see your IP. These are the pin you placed / public pin coordinates, never your device\'s live GPS.',
ipPushStrong: 'Background notifications',
ipPush: '(opt-in) — if you enable them, a SkateSpots push server sees that your key received something and when (never who sent it or what it says — messages stay encrypted), and your OS push service (Google/Mozilla/Apple) delivers the alert.',
ipBtcStrong: 'Bitcoin places',
ipBtc: '(BTCMap, when you switch that layer on) — see your IP and the area you\'re viewing.',
ipRelayStrong: 'Nostr relays',
ipRelay: '— see your IP when the app reads or publishes.',
ipMintStrong: 'Cashu mints',
ipMint: '— see your IP whenever your wallet sends, receives or melts.',
ipRouteStrong: 'Directions',
ipRoute: '(OSRM, and Transitous for transit) — receive the start and end of any route you ask for, not only your IP. Routing stays in the app: your start and destination are never handed off to Google or Apple Maps.',
ipWeatherStrong: 'Weather',
ipWeatherMid: '(Open-Meteo, opt-in) and',
ipUploadStrong: 'image uploads',
ipWeather: '(Blossom), when you use them.',
ipAiStrong: 'Sasquatch AI',
ipAi: '— your message goes to our own server, then to the model provider.',
ipLink1: 'Because you send your public key (npub) on the same connection, a relay can link your IP to you — not just "someone connected", but "this person, from this address".',
ipShieldBefore: 'One thing we do shield: the',
ipMintsStrong: 'mints directory',
ipShieldAfter: 'is fetched through our server, so MintRadar never sees your IP.',
vpnTitle: 'Want more privacy? VPN or Tor (optional)',
vpnDesc: 'For most skaters this isn\'t necessary — but if you want to hide your IP and rough location from the servers above, you have two free options:',
vpnReputable: '🛡️ A reputable VPN',
vpnReputableDesc: 'Hides your IP behind the VPN\'s servers. Easy and fast. Use one you pay for and trust; free VPNs usually monetise your traffic, so treat "free" as a warning sign.',
torBrowser: '🧅 Tor Browser',
torDesc: 'Free, maximum IP privacy, routes through multiple relays. Slower — maps and some Nostr relays may lag or block Tor exit nodes. Great for the extra-cautious.',
readThis: 'Read this so you\'re not fooled',
caveat1Before: 'A VPN/Tor hides your',
ipWord: 'IP',
caveat1: '— not the spots you publish. A pin at your home is public whether or not you use a VPN.',
caveat2Before: 'Don\'t enable',
nearMeGps: '"Near Me" / browser GPS',
caveat2: 'if your goal is to stay anonymous — precise GPS overrides any IP hiding.',
caveat3: 'No tool makes you fully "anonymous." These reduce what servers can infer about your network location — that\'s it.',
keysTitle: 'Your keys, your account',
keysBefore: 'Your account is a cryptographic key pair that you own. The safest way to log in is a',
signer: 'signer',
keysAfter: '(Alby, nos2x, or Amber on Android) so your secret key never gets pasted into the app.',
keysCaveatBefore: 'If you paste your',
keysCaveatAfter: 'directly, it\'s stored in your browser. Use a signer extension instead when you can, and keep your nsec backup somewhere safe offline.',
footerBefore: 'Questions? Ask',
sasquatchName: 'The Sasquatch',
footerMid: 'in-app, or read the',
gettingStarted: 'getting started guide',
about: 'About',
},
license: {
seoTitle: 'License - SkateSpots',
seoDescription: 'SkateSpots license. You are free to skateboard.',
tagline: 'Skate, earn, have fun and spend Sats wherever you are.',
copyright: 'SkateSpots contributors & the community',
licenseLabel: 'License',
freeLine1: 'YOU ARE FREE',
freeLine2: 'TO SKATEBOARD',
body: 'This project is open. Share it, fork it, build on it, skate it. The streets belong to everyone. So does this code. Keep skating, have fun, learn tricks, support the builders and keep skating again.',
builtBefore: 'Built with ❤️ on',
builtMid: '· Powered by',
lightning: 'Bitcoin Lightning ⚡',
viewSource: 'View source',
aboutSkateSpots: 'About SkateSpots',
supporters: 'Supporters',
},
glossary: {
seoTitle: 'Trick Glossary · SkateSpots',
seoDescription: 'The complete skateboarding glossary — {{count}} entries covering flips, grinds, slides, grabs, airs, lip tricks, handplants, freestyle, transition terms, stance & hardware, with alternate names.',
heading: 'TRICK GLOSSARY',
subheading: '{{count}} entries · tricks, transition, freestyle, stance & hardware',
searchPlaceholder: 'Search tricks or nicknames — tre flip, crooks, method…',
clearSearch: 'Clear search',
all: 'All',
results: '{{count}} result{{s}} for',
noMatch: 'No tricks match that. Try a nickname like “tre flip”, “crooks”, or “melon”.',
tricksCount: '{{count}} tricks',
footerBefore: 'Know a trick or nickname we missed? Tricks pick up different names in every scene — hit up',
sasquatch: 'The Sasquatch 🦧',
footerAfter: 'and let us know.',
},
signers: {
seoTitle: 'Signer Apps - Secure Your Nostr Keys | SkateSpots',
seoDescription: 'Learn about signer apps that keep your Nostr keys secure. Find the best option for Android, iOS, or desktop browsers.',
securityGuide: 'Security Guide',
protect: 'Protect Your',
nostrKeys: 'Nostr Keys',
heroDesc: 'Signer apps keep your private keys secure by signing events without exposing them. Instead of pasting your key into every app, let a signer handle it safely.',
whySignerStrong: 'Why use a signer?',
whySigner: 'Your Nostr private key is like a password that can never be changed. A signer app keeps it safe while letting you use any Nostr app.',
androidTitle: 'Android Signers',
androidDesc: 'Native signer apps for Android devices, including GrapheneOS.',
iosTitle: 'iOS Signers',
iosDesc: 'Signer options for iPhone and iPad users.',
browserTitle: 'Browser Extensions',
browserDesc: 'Desktop browser extensions for signing on web apps.',
recommended: 'Recommended',
yourPlatform: 'Your Platform',
get: 'Get {{name}}',
protocolsTitle: 'Understanding Protocols',
nip07Desc: 'Browser extension standard. The extension injects signing capabilities into web apps. Used by Alby, nos2x, Flamingo, and similar extensions.',
nip46Desc: 'Remote signing protocol ("bunker"). Your signer app approves requests from other apps without sharing your keys. Used by Nsec.app, Nostria, and Nowser.',
nip55Desc: 'Android-specific signer protocol. Native apps can request signatures from Amber without exposing keys. The most seamless Android experience.',
whichChoose: 'Which to choose?',
chooseAndroid: 'Android:',
chooseAndroidDesc: 'Amber (NIP-55) for native apps',
chooseIos: 'iOS:',
chooseIosDesc: 'Nsec.app (NIP-46) for best compatibility',
chooseDesktop: 'Desktop:',
chooseDesktopDesc: 'Alby, nos2x, or Flamingo (NIP-07)',
amberDesc: 'Native Android signer focused on security and ease of use. The best choice for Android users.',
nsecAppDesc: 'Cross-platform remote signer that acts as a secure vault for your keys.',
nostriaDesc: 'Remote signer app with keys stored in secure device storage.',
nowserDesc: 'Multi-auth signer supporting both browser extension and remote signing protocols.',
nsecAppIosDesc: 'The best option for iOS. Use it as a remote signer with any Nostr client.',
nowserTfDesc: 'iOS signer available via TestFlight with signing capabilities similar to Android.',
nostriaIosDesc: 'Remote signer with secure key storage on your device.',
nos2xDesc: 'Simple and reliable browser extension for signing Nostr events.',
albyDesc: 'Bitcoin Lightning wallet with built-in Nostr key management and WebLN.',
flamingoDesc: 'Nostr-focused browser extension with clean interface.',
kiwiDesc: 'On Android/GrapheneOS, use Kiwi Browser to load nos2x or Alby extensions.',
fNativeApp: 'Native app',
fMultiKey: 'Multiple key support',
fEventSigning: 'Event signing',
fSecureStorage: 'Secure key storage',
fRemoteSigning: 'Remote signing',
fAnyClient: 'Works with any client',
fCrossDevice: 'Cross-device sync',
fMultiKeySupport: 'Multi-key support',
fNip07: 'NIP-07 support',
fNip46: 'NIP-46 support',
fFlexibleSigning: 'Flexible signing',
fNativeIos: 'Native iOS app',
fLightweight: 'Lightweight',
fEasyToUse: 'Easy to use',
fChromeSupport: 'Chrome/Brave/Edge support',
fLightningPayments: 'Lightning payments',
fNostrSigning: 'Nostr signing',
fWebln: 'WebLN support',
fNwc: 'NWC support',
fCleanUi: 'Clean UI',
fPrivacyFocused: 'Privacy-focused',
fExtensionMobile: 'Extension support on mobile',
fFullBrowser: 'Full browser experience',
fGraphene: 'Works on GrapheneOS',
},
betaDisclaimer: {
title: 'Heads up — this is beta',
subtitle: 'A quick word before you drop in.',
ossTitle: 'Open-source & experimental',
ossBody: 'Built in the open by the community. Poke around, fork it, break it.',
betaTitle: 'In beta — not audited',
betaBody: 'The code has not undergone a formal security audit yet. Bugs happen.',
moneyTitle: 'Handles real Bitcoin & Lightning',
moneyBody: 'Use money on it at your own risk. Only load what you can afford to lose.',
finePrint: 'No warranty of any kind. You are solely responsible for your own funds and keys.',
viewCode: 'View the code',
accept: 'Got it, let me in',
},
},
walletUi: {
page: {
title: 'Wallet',
subtitle: 'Bitcoin Lightning + Cashu',
manage: 'Manage',
getAWallet: 'Get a Wallet',
learnPlanB: 'Learn more at Plan B Academy',
walletMinibits: 'Mobile Cashu · NWC',
walletAlby: 'Browser extension · WebLN + NWC',
walletZeus: 'Self-custodial Lightning',
walletCoinos: 'Web-based · works everywhere',
whatAreZaps: 'What are Zaps?',
whatAreZapsShort: 'What are zaps?',
zapsDesc: 'Instant Bitcoin Lightning payments. Send sats to any skater, spot creator, or coach — no middleman, no platform cut.',
instant: 'Instant',
instantDesc: 'Settles in seconds',
flexible: 'Flexible',
flexibleDesc: 'Your wallet, your keys',
private: 'Private',
privateDesc: 'Cashu hides who pays',
direct: 'Direct',
directDesc: 'Value goes to creator',
loginRequired: 'Login Required',
loginRequiredDesc: 'Log in with your Nostr account to connect your Lightning wallet and start zapping.',
browseMints: 'Browse Cashu mints',
},
modal: {
walletName: 'Wallet Name (optional)',
walletNamePlaceholder: 'My Lightning Wallet',
connectionUri: 'Connection URI',
spendLimitTitle: 'Set a spending limit first',
spendLimitBody1: 'This string lets SkateSpots pay Lightning invoices from your wallet. In your wallet app, give this connection a small',
dailyBudget: 'daily budget',
spendLimitBody2: '(e.g. a few thousand sats) and enable only',
spendLimitBody3: 'Never paste a connection with an unlimited budget. You can revoke it in your wallet any time.',
addWallet: 'Add Wallet',
nwcConnection: 'NWC Connection',
lightningWallet: 'Lightning Wallet',
pasteNwc: 'Paste your NWC connection string to send zaps',
connectWallet: 'Connect Wallet',
browserExtension: 'Browser Extension (WebLN)',
autoDetected: 'Auto-detected if installed',
ready: 'Ready',
notFound: 'Not Found',
getNwcFrom: 'Get NWC from:',
connectionRequired: 'Connection URI required',
connectionRequiredDesc: 'Please enter a valid NWC connection URI.',
activeChanged: 'Active wallet changed',
activeChangedDesc: 'The selected wallet is now active for zaps.',
connectNwc: 'Connect NWC Wallet',
connectNwcDesc: 'Enter your connection string from a compatible wallet.',
connect: 'Connect',
connecting: 'Connecting...',
walletSettings: 'Wallet Settings',
connectDesc: 'Connect your lightning wallet to send zaps instantly.',
},
cashu: {
title: 'Cashu Wallet',
desc: 'Built-in ecash wallet — no external setup required',
initializing: 'Initializing wallet...',
walletError: 'Wallet Error',
balance: 'Balance',
balanceAria: 'Wallet balance {{amount}} sats',
activeMint: 'Active Mint',
selectMint: 'Select a mint',
receive: 'Receive',
send: 'Send',
receiveViaLightning: '⚡ Receive via Lightning',
amountSats: 'Amount in sats',
generateInvoice: 'Generate Invoice',
generating: 'Generating…',
or: 'Or',
receiveCashu: '🥜 Receive Cashu Token',
scanQr: 'Scan QR',
pasteCashu: 'Paste Cashu token (cashu...)',
receiveToken: 'Receive Token',
receiving: 'Receiving…',
createCashu: '🥜 Create Cashu Token',
createToken: 'Create Token',
creating: 'Creating…',
payInvoice: '⚡ Pay Lightning Invoice',
pasteInvoice: 'Paste Lightning invoice (lnbc...)',
payInvoiceBtn: 'Pay Invoice',
processing: 'Processing...',
payAddress: '📧 Pay Lightning Address',
sendToAddress: 'Send to Address',
sending: 'Sending…',
recentActivity: 'Recent Activity',
receivedLightning: 'Received ⚡ Lightning',
sentLightning: 'Sent ⚡ Lightning',
sentToken: 'Sent 🥜 Token',
receivedToken: 'Received 🥜 Token',
whatIsCashu: 'What is Cashu?',
whatIsCashuDesc: 'Cashu is digital cash backed by Bitcoin Lightning. Think of it as Bitcoin tokens you hold in your browser.',
benefitOffline: 'Works offline at skate spots',
benefitInstant: 'Instant peer-to-peer payments',
benefitPrivacy: "Privacy-focused (mints can't track you)",
benefitRedeemable: 'Always redeemable for Lightning sats',
findMints: 'Find mints at mintradar.pedani.eu',
backupTitle: 'Backup Your Wallet',
backupDesc: "Your wallet is secured with a 12-word recovery phrase. Save it safely — you'll need it to restore your balance if you clear browser data.",
viewRecovery: 'View Recovery Phrase',
backupNote: 'This is like your Nostr nsec — keep it secret and backed up!',
tokenReceived: 'Token received',
tokenReceivedDesc: 'Cashu token redeemed successfully!',
receiveFailed: 'Receive failed',
unknownError: 'Unknown error',
requestUnsupported: 'Cashu request not supported yet',
requestUnsupportedDesc: 'This wallet can’t pay a Cashu (creq) request yet — ask for a Lightning invoice or address instead.',
onchainUnsupported: 'On-chain not supported here',
onchainUnsupportedDesc: 'That’s a Bitcoin address. This wallet only sends Cashu tokens and Lightning payments.',
nostrProfile: 'That’s a Nostr profile',
nostrProfileDesc: 'Scan a Cashu token, Lightning invoice, or Lightning address to pay.',
clinkUnsupported: 'Payment code not supported yet',
clinkUnsupportedDesc: 'This wallet can’t pay CLINK codes yet. Use a Lightning invoice or address instead.',
unrecognizedQr: 'Unrecognized QR code',
unrecognizedQrDesc: 'Scan a Cashu token, Lightning invoice, or Lightning address.',
invalidAmount: 'Invalid amount',
invalidAmountDesc: 'Please enter a valid amount',
paymentSent: 'Payment sent',
paymentSentDesc: 'Sent {{amount}} sats to {{address}}',
paymentFailed: 'Payment failed',
copied: 'Copied',
copiedDesc: '{{label}} copied to clipboard',
receiveSats: 'Receive {{amount}} sats',
payInvoiceHint: 'Pay this invoice from any Lightning wallet',
invoiceQrAlt: 'Invoice QR Code',
lightningInvoice: 'Lightning Invoice',
invoiceLabel: 'Invoice',
waiting: '⏳ Waiting for payment...',
tokenReady: 'Token Ready to Send',
shareToken: 'Share this token with the recipient',
tokenQrAlt: 'Token QR Code',
cashuToken: 'Cashu Token',
tokenLabel: 'Token',
copyToken: 'Copy Token',
tokenWarning: 'Anyone with this token can claim it - treat it like cash!',
browser: {
title: 'Built-in Ecash Wallet',
appOnly: 'App only',
desc: 'Self-custodial Cashu ecash, powered by Bitcoin Lightning',
unlockTitle: 'Unlock the full wallet in the SkateSpots app',
unlockDesc: 'The built-in ecash wallet lets you mint, send and redeem Cashu tokens right at the spot. It lives in the native app, where it can talk to mints directly — browsers sandbox those connections for your security.',
zapTipTitle: 'Zap & tip — works in your browser now',
zapTipDesc: 'Sending sats to spots, events, coaches and crews works right here. Just connect a Lightning wallet below — Nostr Wallet Connect or a browser extension like Alby. No Cashu mint needed.',
},
},
escrow: {
title: 'Escrow',
desc: "Sell safely: turn on escrow and a buyer can lock their sats to you for a trade. You release them when you deliver — if you don't, the buyer can reclaim them after the agreed time.",
active: 'Escrow is on',
publish: 'Turn on escrow',
published: 'Escrow is on',
publishedDesc: 'Buyers can now lock funds to you for a trade.',
failed: 'Could not turn on escrow',
},
escrowSettings: {
title: 'Escrow settings',
desc: 'Defaults for Cashu escrow trades. Device-local.',
lockWindow: 'Default lock window',
lockWindowHint: 'How long funds stay locked before the buyer can reclaim them. Longer is safer for the seller.',
days: 'days',
preferredMint: 'Mint for escrow',
activeMint: 'Active mint',
preferredMintHint: 'Which mint funds escrow. Use a trustworthy one.',
requireListings: 'Require escrow on new listings',
requireListingsHint: 'Pre-selects “Require escrow” when you list an item.',
},
privacy: {
title: 'Privacy Tips',
subtitle: 'Learn how to protect your financial privacy',
cashuRecommended: 'CASHU (Recommended)',
anon: 'Anonymous transactions',
blind: 'Blind signatures hide activity',
noIp: 'No IP exposure',
trustMint: 'Trust mint with funds',
lightningAddress: 'LIGHTNING ADDRESS',
linked: 'Can be linked to identity',
traceable: 'Payment history traceable',
ipVisible: 'IP visible when invoicing',
selfCustody: 'Self-custody possible',
bestPractices: 'Best Practices',
tip1Title: 'Use Cashu for receiving tips',
tip1Desc: 'Built-in Cashu wallet is the most private way to receive zaps',
tip2Title: 'Use a separate wallet for SkateSpots',
tip2Desc: "Don't use your main savings wallet - keep app funds separate",
tip3Title: 'Consider using VPN or Tor',
tip3Desc: 'Hides your IP address when making transactions',
tip4Title: 'Avoid identity-linked Lightning addresses',
tip4Desc: 'Use pseudonymous addresses not tied to your real name',
proTip: 'Pro tip:',
proTipDesc: 'Receive payments to your Cashu wallet first, then periodically move larger amounts to your personal wallet using a VPN for the transfer.',
warnTitle: 'Privacy Note',
warnDesc: 'Lightning addresses can potentially link your SkateSpots activity to your real identity.',
betterPrivacy: 'For better privacy:',
bullet1: 'Use a dedicated wallet for this app',
bullet2: 'Consider using Cashu wallet instead',
bullet3: 'Use VPN/Tor for transactions',
useCashu: 'Use Cashu Instead',
understand: 'I Understand',
},
mintPrivacy: {
title: 'Mint Privacy',
desc: 'Using multiple mints improves privacy by preventing any single mint from seeing all your transactions',
distribution: 'Fund Distribution',
singleWarning: 'Using a single mint means that mint can see all your transaction timing and amounts. Consider adding more mints for better privacy.',
yourMints: 'Your Mints ({{count}})',
default: 'Default',
addMint: 'Add Mint',
addMintDesc: 'Add a new Cashu mint to your wallet',
mintUrl: 'Mint URL',
add: 'Add',
adding: 'Adding...',
orChoose: 'Or choose from list',
recommended: 'Recommended Mints',
allMints: 'All mints (directory)',
searchMints: 'Search mints…',
noDirectoryMints: 'No mints match your search.',
directoryHint: 'From the community MintRadar directory — unofficial and may be stale.',
added: 'Added',
soon: 'Soon',
autoDistribute: 'Auto-distribute funds',
autoDesc: 'Spread received funds across multiple mints',
autoAria: 'Auto-distribute funds (coming soon)',
tip1: 'Use 2-3 different mints minimum for better privacy',
tip2: "Don't keep all funds in one mint - spread them out",
tip3: 'Consider mint jurisdiction (different countries = different legal access)',
tip4: 'Mints see timing of transactions but NOT who you pay (blind signatures)',
future: 'Future features: Mint rotation and privacy scoring - stay tuned!',
findMore: 'Find more mints at mintradar.pedani.eu',
trustWarning: 'Trust Warning:',
trustWarningDesc: 'Mints are custodial - they hold your funds. Only use mints you trust. If a mint disappears, your funds may be lost. Spreading funds across multiple mints reduces risk.',
removeTitle: 'Remove Mint?',
removeDesc: 'Are you sure you want to remove {{name}}?',
removeMint: 'Remove Mint',
warning: 'Warning:',
withdrawWarning: 'This mint has {{amount}} sats. Withdraw your funds before removing!',
invalidUrl: 'Invalid URL',
invalidUrlDesc: 'Mint URL must start with https://',
mintAdded: 'Mint Added',
mintAddedDesc: 'New mint added successfully',
mintAddedNamed: '{{name}} added successfully',
failedAdd: 'Failed to add mint',
failed: 'Failed',
noMints: 'No Mints',
singleMint: 'Single Mint',
concentrated: 'Concentrated',
moderate: 'Moderate',
wellDistributed: 'Well Distributed',
mintCoinosWebDesc: 'Web-friendly mint (recommended for browsers)',
mintMinibitsWebDesc: '⚠ Native app only — not reachable from web browsers',
mintMinibitsNativeDesc: 'SkateSpots mint — recommended',
mintCoinosDesc: 'Web-friendly mint',
mintLnbitsDesc: 'Community mint on LNbits',
},
nwc: {
title: 'Lightning Wallet',
noConnection: 'NWC wallet',
refresh: 'Refresh balance',
createInvoice: 'Create invoice',
copyInvoice: 'Copy invoice',
pasteInvoice: 'Paste a Lightning invoice (lnbc…)',
amount: 'Amount: {{amount}} sats',
pasteToSee: 'Paste an invoice to see the amount',
pay: 'Pay',
invoiceQrAlt: 'Lightning invoice QR code',
enterAmount: 'Enter an amount in sats',
readBalanceFailed: 'Could not read balance',
readBalanceFailedDesc: 'Your wallet did not respond.',
createInvoiceFailed: 'Could not create invoice',
createInvoiceFailedDesc: 'Try again in a moment.',
paymentSent: 'Payment sent',
paidAmount: 'Paid {{amount}} sats.',
paid: 'Paid.',
paymentRejected: 'The wallet rejected the payment.',
},
qr: {
title: 'Scan QR Code',
desc: 'Position the QR code within the camera view to scan',
paste: 'Paste from Clipboard',
hint: 'Scan a Cashu token, Lightning invoice, or Lightning address',
clipboardEmpty: 'Clipboard empty',
clipboardEmptyDesc: 'No text found in clipboard',
pasteFailed: 'Paste failed',
pasteFailedDesc: 'Could not read from clipboard',
},
recovery: {
title: 'Recovery Phrase',
desc: 'These 12 words are the only way to restore your ecash balance. Write them down in order and store them offline — never share them with anyone.',
tapReveal: 'Tap to reveal',
noWatching: "Make sure nobody's watching",
hide: 'Hide',
reveal: 'Reveal',
paperHint: 'Writing it on paper is safest — copying to your clipboard can leak to other apps.',
written: "I've written it down",
revealFirst: 'Reveal the words first',
copiedDesc: 'Recovery phrase copied — paste it somewhere safe, then clear your clipboard.',
copyFailed: 'Copy failed',
copyFailedDesc: 'Please write the words down instead.',
},
guide: {
quickGuide: 'Quick Guide',
fullGuide: 'Wallet Guide (Skater Edition)',
cashuSummary: 'Privacy + Convenience',
mintHoldsFunds: '(mint holds funds)',
lightningSummary: 'Control + Security',
youHoldKeys: '(you hold keys)',
useBoth: 'Use both: Cashu for tips, Lightning for savings',
cashuTitle: 'CASHU WALLET',
cashuTagline: 'Like cash in your pocket at the skatepark',
cashuBullet1: 'Quick to pull out for a tip',
cashuBullet2: "Don't carry your life savings here",
cashuBullet3: 'If the mint disappears, funds could be lost',
cashuBullet4: 'Nobody knows how much you have (privacy!)',
cashuPerfectFor: 'Session snacks, zapping clips, small tips',
lightningTitle: 'LIGHTNING WALLET',
lightningTagline: "Like your bank account - you're in control",
lightningBullet1: 'Secure, you control the keys',
lightningBullet2: 'Takes a moment to set up',
lightningBullet3: 'Full transaction history',
lightningBullet4: 'Better for bigger amounts',
lightningPerfectFor: 'Gear purchases, savings, big zaps',
perfectFor: 'Perfect for:',
proTips: 'PRO TIPS',
tip1Title: 'Use both wallets',
tip1Desc: 'Keep session money in Cashu, savings in Lightning',
tip2Title: 'Try different wallets',
tip2Desc: 'Experiment to find what works for you. Each has different features and trade-offs',
tip3Title: 'Better privacy',
tip3Desc: 'Using multiple wallets means no single app sees all your activity',
tip4Title: 'Gain experience',
tip4Desc: 'The more tools you know, the more options you have. Start small, learn as you go',
tldr: 'TL;DR:',
tldrDesc: 'There\'s no "best" wallet - it depends on your needs. Try both, use what feels right, and don\'t keep all your sats in one place!',
},
zap: {
count: '{{count}} zaps',
connectFirst: 'Connect Your Wallet First',
connectFirstDesc: 'To send zaps (Bitcoin tips), you need to connect a Lightning wallet.',
quickSetup: 'Quick Setup (30 seconds)',
quickSetupDesc: 'Connect your existing Lightning wallet with Nostr Wallet Connect (NWC). No extra accounts needed.',
connectNwc: 'Connect Wallet with NWC',
noWallet: "Don't have a wallet? Learn how",
skip: 'Skip for now (manual invoice)',
note: 'ℹ️ Note:',
noteDesc: "You can still create an invoice without connecting a wallet, but you'll need to copy it manually to pay.",
payWithWebln: 'Pay with WebLN',
processing: 'Processing...',
openInWallet: 'Open in Lightning Wallet',
scanHint: 'Scan the QR code or copy the invoice to pay with any Lightning wallet.',
cashuInstant: 'Pay instantly with your built-in wallet',
satsAvailable: 'sats available',
or: 'OR',
customAmount: 'Custom amount',
addComment: 'Add a comment (optional)',
insufficientBanner: '⚠️ Insufficient Cashu balance. You need {{amount}} more sats. Switch to Lightning or top up your Cashu wallet.',
creatingInvoice: 'Creating invoice...',
zapAmount: 'Zap {{amount}} sats',
zapAmountCashu: 'Zap {{amount}} sats with Cashu',
lightningInvoice: 'Lightning Invoice',
qrAlt: 'Lightning Invoice QR Code',
enterAmount: 'Enter an amount',
enterAmountDesc: 'Choose a preset or type a valid number of sats.',
cashuUnavailable: 'Cashu wallet unavailable',
cashuUnavailableDesc: 'Your Cashu wallet is not ready. Switch to Lightning or open your wallet first.',
insufficientCashu: 'Insufficient Cashu balance',
insufficientCashuDesc: 'You need {{amount}} more sats. Switch to Lightning or top up.',
zapSentCashu: 'Zap sent! 🥜',
zapSentCashuDesc: 'Zapped {{amount}} sats from your Cashu wallet.',
cashuFailed: 'Cashu payment failed',
cashuFailedDesc: 'Failed to pay with your Cashu wallet.',
invoiceCopied: 'Invoice copied',
invoiceCopiedDesc: 'Lightning invoice copied to clipboard',
defaultComment: 'Zapped on SkateSpots! 🛹',
titleConnect: 'Connect Wallet',
titlePayment: 'Lightning Payment',
titleZap: 'Send a Zap',
descConnect: 'Connect your Lightning wallet to send zaps',
descPayment: 'Pay with Bitcoin Lightning Network',
descZap: 'Zaps are small Bitcoin payments that support the creator of this item. If you enjoyed this, consider sending a zap!',
descZapShort: 'Zaps are small Bitcoin payments that support creators. Consider sending a zap!',
},
zapCrew: {
title: 'Zap the Crew',
desc: 'Split a zap evenly across {{count}} crew members with a Lightning address.',
noMembers: 'No other crew members to zap right now.',
totalAmount: 'Total amount',
customTotal: 'Custom total',
each: 'sats each',
rounded: '(rounded)',
noWallet: "No wallet connected — you'll get an invoice per member to pay manually. Connect a wallet for one-tap splitting.",
zapAcross: 'Zap {{amount}} sats across {{count}}',
sentTo: '⚡ Sent to {{done}}/{{total}}',
none: 'No auto-payments completed',
copyPending: 'Copy any pending invoices to finish paying manually.',
thanks: 'Thanks for supporting the crew!',
payingCrew: 'Paying the crew…',
defaultComment: 'Zapped the {{crew}} crew! 🤘',
sent: 'Sent',
paying: 'Paying',
resolving: 'Resolving',
skipped: 'Skipped',
failed: 'Failed',
waiting: 'Waiting',
copyInvoice: 'Copy invoice',
invoiceCopied: 'Invoice copied',
invoiceCopiedDesc: 'Paste it into any Lightning wallet',
},
zapBadges: {
title: 'Zap Badges',
zaps: 'zaps',
satsSent: 'sats sent',
rank: 'rank',
footnote: 'Based on the last 30 days of zaps.',
first: 'First Zap',
firstHint: 'Send your first zap',
regular: 'Regular',
regularHint: 'Send 10 zaps',
generous: 'Generous',
generousHint: 'Send 10,000 sats',
patron: 'Patron',
patronHint: 'Send 100,000 sats',
top10: 'Top 10',
top10Hint: 'Reach the top 10 supporters',
},
trust: {
unavailable: 'Trust score unavailable',
yourContent: 'This is your content',
following: 'You follow this user',
friendOfFriend: 'Friend of a friend (2 hops)',
nip05: 'NIP-05 verified user',
unknown: 'Unknown user',
help: 'Trust scores help identify reliable contributors in the skateboarding community.',
},
share: {
title: 'Share profile',
desc: "Point a phone camera at the code to open {{name}}'s profile.",
qrAlt: 'Profile QR code',
copyLink: 'Copy link',
copyFailed: 'Copy failed',
copyFailedDesc: 'Could not copy the link.',
shareEllipsis: 'Share…',
},
addToCollection: {
title: 'Save to Collection',
desc: 'Save "{{spot}}" to one of your collections',
loginRequired: 'Please log in to save spots to collections',
noCollections: "You don't have any collections yet",
createFirst: 'Create your first one below!',
spotCount: '{{count}} spots',
alreadySavedTag: 'Already saved',
alreadySaved: 'Already saved',
alreadySavedDesc: '“{{spot}}” is already in “{{collection}}”',
saved: 'Spot saved!',
savedDesc: 'Added "{{spot}}" to "{{collection}}"',
saveFailed: 'Failed to save',
somethingWrong: 'Something went wrong',
created: 'Collection created!',
createdDesc: 'Added "{{spot}}" to your new collection "{{collection}}"',
createFailed: 'Failed to create collection',
name: 'Collection Name',
descriptionOptional: 'Description (optional)',
creating: 'Creating...',
createAndSave: 'Create & Save',
createNew: 'Create New Collection',
},
collection: {
back: 'Back to Collections',
created: 'Created {{date}}',
deleteTitle: 'Delete Collection?',
deleteDesc: 'This will permanently delete "{{title}}". The spots themselves won\'t be affected.',
deleting: 'Deleting...',
deleteFailed: 'Failed to delete collection',
deleted: 'Collection deleted',
deletedDesc: '"{{title}}" has been removed',
empty: 'No spots in this collection',
emptyOwner: 'Add spots to your collection by clicking "Save to Collection" on any spot',
emptyOther: 'This collection is empty',
browseSpots: 'Browse Spots',
linkCopied: 'Link copied!',
linkCopiedDesc: 'Collection link has been copied to clipboard',
map: 'Collection Map',
removeFailed: 'Failed to remove spot',
shareText: 'Check out this spot collection: {{title}}',
spotRemoved: 'Spot removed',
spotRemovedDesc: '"{{title}}" has been removed from this collection',
},
mints: {
title: 'Mints',
subtitle: 'Browse Cashu mints and their health. Cashu wallet operations run in the SkateSpots app.',
searchPlaceholder: 'Search mints by name or URL…',
onlineOnly: 'Online only',
count: '{{filtered}} of {{total}} mints',
directoryError: 'Could not load the mint directory.',
directoryHint: 'The directory is community-run and rate-limited — it may be briefly unavailable.',
goToWallet: 'Go to Wallet',
appOnly: 'Adding a mint happens in the SkateSpots app (Cashu is app-only).',
noMatch: 'No mints match “{{query}}”.',
footer: "Health data from the community MintRadar project — unofficial and may be stale. Always review a mint's terms before trusting it with funds.",
archived: 'Archived',
degraded: 'Degraded',
offline: 'Offline',
online: 'Online',
terms: 'Terms',
reliability: 'reliability',
add: 'Add',
addToWallet: 'Add to wallet',
copyUrl: 'Copy URL',
openMint: 'Open mint',
inWallet: 'In your wallet',
addedBadge: 'Added',
urlCopied: 'Mint URL copied',
added: 'Mint added',
addedDesc: '{{host}} is now in your wallet.',
couldNotAdd: 'Could not add mint',
communityRating: 'Community rating',
reliabilityLabel: 'Reliability',
uptime: 'Uptime 24h / 7d',
location: 'Location',
latency: 'Latency',
nuts: 'NUTs supported',
units: 'Units',
version: 'Version',
ratedBy: 'Rated {{rating}}/5 by {{count}} people on MintRadar.',
noRating: 'No community rating yet on MintRadar.',
reviewsNote1: "Written reviews aren't part of the directory —",
openMintRadar: 'open MintRadar',
reviewsNote2: 'to read or leave one.',
liveness: 'Liveness audit: {{mints}} mints and {{melts}} melts tested',
livenessErrors: ' · {{errors}}/{{total}} recent errors',
whatsAMint: "What's a mint?",
whatsAMintDesc: "A Cashu mint issues and redeems the ecash in your wallet. It holds the Bitcoin backing your tokens — so a mint you don't run yourself is one you have to trust.",
mintWarning: 'Mints can go offline, change terms, or disappear with funds. Keep only small balances unless you trust the operator.',
healthDisclaimer: 'Health data is community-run (MintRadar) — unofficial and possibly stale.',
},
appShop: {
title: 'App Shop',
subtitle: 'SkateSpots merch for the people who keep the scene rolling.',
comingSoon: 'Coming soon',
wearThe: 'Wear the',
scene: 'Scene',
heroDesc1: 'Merch for the people who keep this thing rolling — sold',
locally: 'locally, on request',
heroDesc2: '. You message me, we sort the details, and you pay in',
heroDesc3: '— Lightning or Cashu — when you collect it. No cards, no accounts, no checkout robot, no middleman skimming the drop.',
requestItem: 'Request an item',
dmHint: 'Opens a private, encrypted DM with me.',
lightning: 'Lightning',
cashu: 'Cashu',
localHandover: 'Local handover',
inTheWorks: 'In the works',
notForSale: 'not for sale yet',
soon: 'soon',
tees: 'Tees & hoodies',
decks: 'Decks',
shopTools: 'Shop tools',
stickerPacks: 'Sticker packs',
stickerPacksDesc: 'The packs are coming. The designs are not — print these two right now, free.',
sticker: 'Sticker',
stickerTip: 'Black die-cut. Print on sticker paper, cut along the black edge.',
poster: 'Poster',
posterTip: 'Prints on A3; shrink to A4 (50%) and it still scans fine.',
printableAlt: 'SkateSpots {{title}} — printable',
print: 'Print',
printNote1: 'Print at',
actualSize: '100% / “Actual size”',
printNote2: 'so the size is exact and the QR stays sharp — it points at skate-spots.org. Vector files, so they scale without going fuzzy.',
howBuying: 'How buying works',
dmNotCart: "it's a DM, not a cart",
step1Title: 'Message me',
step1Desc: 'Send a DM with what you want — item, size, colour, whatever you are after.',
step2Title: 'We sort it out',
step2Desc: 'Price in sats, and where and when to meet. It is a conversation, not a checkout.',
step3Title: 'Pay in sats',
step3Desc: 'Lightning or Cashu at handover, then it is yours. No shipping, no waiting rooms.',
},
newsletter: {
newIssue: 'New issue',
composerTitle: 'Publish a newsletter issue',
composerDesc: 'Published as a NIP-23 article (kind 30023) on the community relay. Subscribers get it in their notification bell. Body supports Markdown.',
fieldTitle: 'Title',
identifier: 'Identifier (slug)',
leaveBlank: 'leave blank to auto-generate',
reusingSlug: 'Re-using an existing slug edits that issue instead of creating a new one.',
summary: 'Summary',
optional: 'optional',
coverImage: 'Cover image URL',
bodyMarkdown: 'Body (Markdown)',
characters: '{{count}} characters',
publishIssue: 'Publish issue',
issuePublished: 'Issue published! 🛹',
issuePublishedDesc: 'Subscribers will see it in their notification bell.',
publishFailed: 'Publish failed',
couldNotReachRelay: 'Could not reach any relay — check your connection and try again.',
tryAgain: 'Please try again.',
publishedToNostr: 'Published to Nostr',
publishedToast: 'Published!',
postedToNostr: 'Drop {{number}} posted to Nostr.',
postToNostr: 'Post to Nostr',
previewLabel: 'Preview',
editFirst: 'Edit first',
previewNoteDesc: 'This is how the note will appear on most Nostr clients. Formatting varies by client.',
editNoteDesc: 'Edit the note text below. Changes only affect this publish — the source file stays unchanged.',
dropNumber: 'Drop {{number}}',
publish: 'Publish',
publishing: 'Publishing...',
dropIsOut: 'Drop {{number}} is out',
daysAgo: '{{count}}d ago',
dismiss: 'Dismiss',
itsMonday: "It's Monday",
mondayDesc: 'Time to drop the weekly newsletter. One click and the AI writes it from the git log.',
writeDrop: 'Write Drop {{number}}',
dismissReminder: 'Dismiss reminder',
loginPrompt: 'Log in with Nostr to get new drops in your mailbox.',
subscribedToast: "You're subscribed! 🛹",
subscribedToastDesc: 'New drops will land in your notification bell under "News".',
unsubscribed: 'Unsubscribed',
unsubscribedDesc: 'You can still read every issue here anytime.',
checking: 'Checking…',
subscribed: 'Subscribed',
unsubscribe: 'Unsubscribe',
subscribeCta: 'Subscribe to the newsletter',
archiveSynced: 'Archive synced to Nostr',
missingCount: '{{count}} archive issues not on Nostr yet',
backfillDesc: 'Publish {{list}} as NIP-23 articles so subscribers and long-form readers can see the full archive. The page keeps its rich layout regardless.',
backfill: 'Backfill {{count}} to Nostr',
publishing2: 'Publishing…',
publishingLabel: 'Publishing…',
backfilled: 'Backfilled {{count}} issues 🛹',
backfilledDesc: 'The archive now lives on Nostr for subscribers and readers.',
partialBackfill: 'Published {{published}}, {{failed}} failed',
retryFinish: 'Retry to finish: {{list}}.',
notLoggedIn: 'Not logged in',
allIssues: 'All issues',
brand: 'SkateSpots Newsletter',
digTheDrop: 'Dig the drop? Zap it and never miss the next one.',
sectionStats: 'Community by the Numbers',
sectionWhatsNew: "What's New",
sectionSpotOfWeek: 'Spot of the Week',
sectionCommunity: 'Community',
sectionSupport: 'Support the Project',
sectionLatestDrops: 'Latest Drops · Live from Nostr',
mappedBy: 'Mapped by',
viewOnMap: 'View on map',
noSpotPicked: 'No spot picked this week. Submit yours and it might make the next drop.',
browseMap: 'Browse the map',
spotMap: 'Spot Map',
statsBadge: 'Stats',
supportDesc: "100% open source, community funded, no ads, no investors. If it's useful zap it or back it on Angor.",
readFull: 'Read the full issue',
joinSkaters: 'Join {{count}} skaters getting the drops',
weeklyDrops: 'Weekly Drops',
tagline: 'A weekly newsletter · shipped whenever the f*** we want',
freeNote: 'Free. Powered by Nostr — your subscription lives on your keys and follows you across devices. New drops land in your notification bell.',
live: 'Live',
builtOn: 'Built on',
openSource: 'Open source',
supportUs: 'Support us',
freeToSkate: 'You are free to skateboard',
},
stats: {
subtitle: 'Live metrics pulled directly from the Nostr network. Transparent, decentralized, verifiable by anyone.',
dataSource: 'Data source:',
updated: 'updated {{age}}',
refresh: 'Refresh',
partial: "Couldn't reach every relay — these numbers may be incomplete.",
error: 'Could not reach the relay right now. Stats will load once the connection is restored.',
atAGlance: 'Community at a Glance',
skateSpots: 'Skate Spots',
listedShops: 'Listed Shops',
events: 'Events',
mappers: 'Mappers',
uniqueContributors: 'unique contributors',
labHeader: 'The Lab · Creators & Services',
total: '{{count}} total',
coaches: 'Coaches',
filmers: 'Filmers',
editors: 'Editors',
photographers: 'Photographers',
builders: 'Builders',
artists: 'Artists',
graffiti: 'Graffiti',
tattoo: 'Tattoo',
trainers: 'Trainers',
musicians: 'Musicians',
judges: 'Judges',
organizers: 'Organizers',
barbers: 'Barbers',
showFewer: 'Show fewer',
showAllCrafts: 'Show all 13 crafts',
community: 'Community',
tours: 'Tours',
crews: 'Crews',
marketplace: 'Marketplace',
liveListings: 'Live Listings',
tradesRecorded: 'Trades Recorded',
satsTraded: 'Sats Traded',
zapsToMappers: 'Zaps to Mappers',
tradesNote1: 'mutual trades across',
tradesNote2: "people. Sats are self-reported by the traders — the app can't observe an off-app payment — so the headline counts only",
mutual: 'mutual',
tradesNote3: 'trades with a per-pair cap; the raw claim is',
tradesNote4: 'sats. "Zaps to mappers" counts only receipts addressed to our top contributors — it is',
not: 'not',
tradesNote5: 'global zap volume.',
byRegion: 'Spots & Shops by Region',
resolving: 'Resolving {{count}} untagged locations from their coordinates… one-time, then cached on this device.',
topCountries: 'Top Countries',
topCities: 'Top Cities',
other: 'Other',
otherCountriesTitle: 'Countries outside the top 6',
otherCitiesTitle: 'Cities outside the top 6',
uncategorised: 'Uncategorised',
uncategorisedTitle: 'No city/country tag',
regionNote1: 'Counts',
spotsAndShops: 'spots & shops',
regionNote2: ", not skaters — from each one's own city/country tag, or derived from its pin's coordinates when it has none (cached on your device; countries grouped by ISO code when present), community relay only, with moderation-hidden content excluded.",
ofTotal: '{{count}} of {{total}}',
regionNote3: 'locations have no location tag and show as "Uncategorised".',
growth: 'Growth Over Time',
chartCumulativeTitle: 'Spots Mapped (Cumulative)',
chartCumulativeDesc: 'Total spots added to the map over time',
chartMonthlyTitle: 'Monthly Activity',
chartMonthlyDesc: 'New content published each month',
chartMappersTitle: 'Active Mappers / Month',
chartMappersDesc: 'Unique contributors publishing each month',
chartMixTitle: 'Content Mix',
chartMixDesc: 'Breakdown by content type (all time)',
noData: 'No data yet',
topMappers: 'Top Mappers',
mostSpots: '— most spots added',
noMapperData: 'No mapper data yet',
topShops: 'Top Shops',
mostShops: '— most shops added',
shopCount: '{{count}} shops',
noShopData: 'No shop data yet',
supporters: 'Community Supporters',
fundedTitle: 'Funded by the Community',
fundedDesc1: 'Decentralized Bitcoin crowdfunding via Angor.',
fundedDesc2: 'No ads, no investors, no data selling.',
backAngor: 'Back us on Angor',
communityLink: 'Community →',
footer1: 'All data is pulled live from',
footer2: '— a public Nostr relay.',
footer3: 'Every event is signed by its creator and cryptographically verifiable.',
footer4: 'No numbers can be faked.',
spots: 'Spots',
shops: 'Shops',
lab: 'Lab',
thisMonth: '+{{count}} this month',
fromActive: '{{count}} from active accounts',
viewAll: 'View all',
spotCount: '{{count}} spots',
},
leaderboard: {
mapEarn: 'Map & Earn',
helpMap: 'HELP MAP THE WORLD',
intro: 'Add spots and shops, get zapped by grateful skaters. Top contributors may receive bonus rewards!',
howItWorks: 'How It Works',
step1Title: '1. Map',
step1Desc: 'Add spots & shops with photos and details',
step2Title: '2. Earn Zaps',
step2Desc: 'Skaters who find your spots useful will tip you',
step3Title: '3. Bonus Rewards',
step3Desc: 'Top mappers may get bonus rewards from our fund',
proTips: '💡 Pro Tips for More Zaps:',
tipPhotos: 'Add 3+ photos',
tipDescriptions: 'Write detailed descriptions',
tipLocation: 'Accurate location',
mapShop: 'Map a Shop',
mapShopDesc: 'Skate shops, board stores',
mapSpot: 'Map a Spot',
mapSpotDesc: 'Parks, street spots, DIY',
shops: 'Shops',
spotsLower: 'spots',
shopsLower: 'shops',
contributions: 'contributions',
yourRanking: 'Your Ranking',
spotsShops: '{{spots}} spots • {{shops}} shops',
totalLower: 'total',
overall: 'Overall',
noContributions: 'No contributions yet. Be the first!',
addFirstSpot: 'Add First Spot',
noShops: 'No shops mapped yet',
addFirstShop: 'Add First Shop',
noSpots: 'No spots mapped yet',
poweredBy: 'Powered by Community Zaps',
poweredByDesc: "When you find a spot helpful, zap the mapper! It's the best way to say thanks and encourage more contributions.",
you: 'You',
zapTitle: 'Zap Leaderboards',
zapSubtitle: 'The spots getting the most love — and the skaters giving it.',
thisWeek: 'This week',
thisMonth: 'This month',
allTime: 'All time',
topZappedSpots: 'Top zapped spots',
noZaps: 'No zaps yet in this period. Be the first — hit the ⚡ on a spot you love.',
topSupporters: 'Top supporters',
noZapsSent: 'No zaps sent yet in this period.',
fromTop10: '⚡ {{amount}} sats from the top 10',
supporter: 'Supporter',
skateShop: 'Skate shop',
skateSpot: 'Skate spot',
},
supporters: {
our: 'OUR',
community: 'COMMUNITY',
heroDesc: 'SkateSpots is built by and for the community. Supporters, collaborators, partners and friends keeping this free and decentralized.',
backAngor: 'Back us on Angor',
support: 'Support',
supportDesc: 'Fund SkateSpots on Angor — decentralized, on-chain Bitcoin crowdfunding. No middleman, no KYC.',
collaborators: 'Collaborators',
collaboratorsDesc: 'People actively building SkateSpots — developers, designers, relay operators.',
partners: 'Partners',
partnersDesc: 'Organizations and projects we work closely with.',
friends: 'Friends',
friendsDesc: 'People we vibe with.',
backTitle: 'Back the project on Angor',
backDesc: 'Angor is a decentralized Bitcoin crowdfunding platform built on Nostr — your funds stay in your control, released in stages, with no custodian holding the pot. Every sat goes to development, relay hosting, and keeping SkateSpots free for every skater on earth.',
openAngor: 'Open Angor',
orZap: 'or ⚡ zap any spot, crew or creator directly in the app',
wantCollaborate: 'Want to collaborate?',
wantCollaborateDesc: 'Developers, designers, skaters with skills — reach out.',
viewNostrHub: 'View on NostrHub',
wantPartner: 'Want to partner with SkateSpots? Reach out on Nostr.',
comingSoon: 'Coming soon',
wantListed: 'Want to be listed here?',
wantListedDesc: 'Send a logo (PNG/WebP, transparent background, min 200×200px) and your URL via DM on Nostr, only if you have the vibes.',
whyTitle1: 'Why We Need',
whyTitle2: 'Support',
whyDesc: 'SkateSpots is 100% free, open-source, and built on Nostr — a decentralized protocol with no central server to shut it down. No ads, no data selling, no investors. Every sat goes directly toward development, relay hosting, and keeping this free for every skater on earth.',
why1Title: 'Nostr-native',
why1Desc: 'No central server. Your data lives on relays you choose.',
why2Title: 'Bitcoin powered',
why2Desc: 'Earn sats for mapping spots. Spend them at local shops.',
why3Title: 'Free forever',
why3Desc: 'Open source, no paywalls, no tracking, no VC funding.',
thankYou: 'Thank you. Every sat keeps the spots on the map. 🛹⚡',
license: 'License',
faq: 'FAQ',
},
admin: {
confirmed: 'Confirmed',
title: 'Review queue',
subtitle: 'Spots, shops, events, tours and gear that trip the junk signals. Hide and Confirm are soft, reversible labels; reports rank first. Nothing is deleted.',
tabReports: 'Reports',
tabSpots: 'Spots',
tabShops: 'Shops',
tabEvents: 'Events',
tabTours: 'Tours',
tabGear: 'Gear',
tabCrews: 'Crews',
tabLab: 'Lab',
tabClaims: 'Claims',
nothingToReview: 'Nothing to review — nothing of this kind trips the junk signals.',
hideActioned: 'Hide actioned',
showActioned: 'Show actioned ({{count}})',
undoAll: 'Undo all {{count}}',
undoAllTitle: 'Undo every verdict in this tab?',
undoAllDesc: "This sets each item back to neutral — not hidden, not vouched. It publishes one public label per item, so with Amber you'll get {{count}} approval screens.",
undoCount: 'Undo {{count}}',
undo: 'Undo',
confirm: 'Confirm',
hide: 'Hide',
hideIt: 'Hide it',
hideForEveryone: 'Hide this for everyone?',
hideForEveryoneDesc: 'This removes the item from browse and search for every SkateSpots user. It is a public, reversible label — you can Undo it from this queue.',
hidden: 'Hidden',
hiddenDesc: 'Removed from the app via a public label. Reversible.',
couldNotHide: 'Could not hide',
notASpot: 'Not a skate spot',
lookAtIt: 'Look at it',
},
profile: {
nip05Verified: 'NIP-05 verified',
nip05NotVerified: 'NIP-05 not verified against this key',
message: 'Message',
zapsReceived: 'zaps received',
memberSince: 'Member since',
badges: 'Badges',
sponsorsSupport: 'Sponsors & support',
skater: 'Skater',
stance: 'Stance',
skating: 'Skating',
homeSpot: 'Home spot',
basedIn: 'Based in',
addSkateDetails: 'Add your skate details',
addSkateDetailsDesc: 'Set your stance, styles & level to appear in the Skaters directory.',
spotsAdded: 'Spots Added',
shopsAdded: 'Shops Added',
noSpots: "You haven't added any spots yet",
noSpotsDesc: 'Put your local spot on the map →',
stash: 'Stash',
gear: 'Gear',
crews: 'Crews',
info: 'Info',
lightning: 'Lightning',
silentPayment: 'Silent Payment',
coordinator: 'Coordinator',
coordinatorAdded: 'Coordinator added',
add: 'Add',
messagePrivately: 'Message privately',
founder: 'Founder',
member: 'Member',
supportSkater: 'Support this skater',
supportSkaterDesc: 'Send a tip to thank them for their contributions',
},
index: {
joinCommunity: 'Join the community to add spots, create events & more',
topZapped: 'TOP ZAPPED',
topZappedDesc: 'Most supported spots this month',
leaderboard: 'Leaderboard',
newHere: 'NEW HERE?',
newHereDesc: 'Create a free account to add spots, join events, connect with skaters, and become part of the community. No email required.',
followFounder: 'Follow the Founder',
followFounderDesc: 'Hold the avatar for 3 seconds to follow and stay updated on new features and events!',
createAccount: 'Create Account',
accountWorks: 'Your account works across all apps in the network',
footerTagline: 'Open Source • Community Owned • No Ads',
},
stash: {
title: 'My Stash',
loginDesc: 'Your private gear inventory. Track what you own, set prices, and selectively share with traders.',
encrypted: 'Encrypted',
syncsEverywhere: 'Syncs Everywhere',
p2pTrading: 'P2P Trading',
encryptionRequired: 'Encryption Required',
encryptionRequiredDesc: 'Your Stash is encrypted for privacy. Please use a signer that supports NIP-44 encryption (like Alby, nos2x, or Amber).',
learnSigners: 'Learn About Signers',
select: 'Select',
stash: 'Stash',
wishlist: 'Wishlist',
allCategories: 'All categories',
sortNewest: 'Newest',
sortValue: 'Value',
sortCondition: 'Condition',
sortAz: 'A–Z',
friendsOnly: 'Friends only',
addToWishlist: 'Add to Wishlist',
addItemToStash: 'Add Item to Stash',
addItem: 'Add Item',
editItem: 'Edit Item',
publicStash: 'Public stash',
listView: 'List view',
gridView: 'Grid view',
uploading: 'Uploading...',
addPhotos: 'Add Photos',
wishlistReqPlaceholder: 'Any specific requirements...',
itemDescPlaceholder: 'Describe the item, any damage...',
itemsCount: '{{count}} item(s)',
wishlistEmpty: 'Wishlist Empty',
wishlistEmptyDesc: "Add gear you're hunting for — we'll match you with friends' stashes.",
stashEmpty: 'Your Stash is Empty',
stashEmptyDesc: 'Start tracking your gear. Decks, trucks, shoes — add it once, access it everywhere via Nostr.',
noMatch: 'No items match',
noMatchDesc: 'Try adjusting your filters.',
backupTransfer: 'Backup & transfer',
backupDesc: "Your stash is encrypted on Nostr. Keep a copy you control — a dead relay or a new account can't lose your gear list.",
exportTitle: 'Encrypted backup',
exportDialogDesc: 'Save {{count}} item(s) to a file. Encrypt it so a leaked file is not a leak.',
modePassword: 'Password',
modeKey: 'My key',
password: 'Password',
confirmPassword: 'Confirm password',
passwordHint: "Use at least 8 characters. There is no recovery — don't forget it.",
keyModeHint: 'Encrypted to your Nostr key. Only that key can open it — nothing to remember.',
exportPlaintext: 'Export without encryption',
pwTooShort: 'Use at least 8 characters.',
pwMismatch: "Passwords don't match.",
exportFailed: 'Could not create the backup.',
unlockTitle: 'Unlock backup',
unlockDesc: 'This backup is password-protected.',
unlock: 'Unlock',
unlockFailed: 'Could not unlock the backup.',
show: 'Show',
hide: 'Hide',
strength: 'Password strength',
strengthWeak: 'Weak',
strengthFair: 'Fair',
strengthGood: 'Good',
strengthStrong: 'Strong',
filenameWillSave: 'Will save as {{name}}',
export: 'Export',
import: 'Import',
exported: 'Stash exported',
exportedDesc: '{{count}} items saved to a file.',
noItemsInFile: 'No items found in that file.',
noValidItems: 'No valid items found in that file.',
imported: 'Imported {{count}} items',
importSkipped: '{{count}} skipped (already in your stash).',
importAdded: 'Added to your stash.',
importFailed: 'Import failed',
couldNotRead: 'Could not read that file.',
updateFailed: 'Could not update your stash',
updatedCount: 'Updated {{count}} items',
markedForSale: 'Marked {{count}} for sale',
unmarkedCount: 'Unmarked {{count}}',
removedCount: 'Removed {{count}} items',
selectedCount: '{{count}} selected',
selectAll: 'Select all',
visibilityPublic: 'Public',
visibilityPrivate: 'Private',
forSale: 'For sale',
remove: 'Remove',
bulkDeleteTitle: 'Remove {{count}} items?',
bulkDeleteDesc: "This rewrites your encrypted stash in one signature and can't be undone here. Export a backup first if you're not sure.",
deleteTitle: 'Remove from your stash?',
deleteDesc: '“{{title}}” will be removed from your stash',
deleteDescPublic: ' and unlisted from your public showcase',
deleteUndone: '. This can’t be undone.',
deleteDescGeneric: 'This item will be removed from your stash. This can’t be undone.',
},
},
cardUi: {
save: 'Save card to Nostr',
saved: 'Card saved',
savedDesc: 'Your card is now signed and verifiable.',
saveFailed: 'Could not save the card',
notPublished: 'Not published',
titleNeon: 'Rider',
titleRest: 'Card',
saveFrontPng: 'Save front (PNG)',
expired: 'EXPIRED',
logoUploaded: 'Logo uploaded',
logoUploadFailed: 'Could not upload the logo',
venue: 'Linked venue (optional)',
venueNone: 'No venue',
venueHint: 'Pick one of your own spots to bind this pass to a venue. A verifier will confirm you own it.',
venuePick: 'Choose your venue',
parkNoVenue: 'A Park pass can only be issued by the venue’s owner — map a spot you own first.',
parkAddVenue: 'Add a spot',
parkNeedsVenue: 'Pick a venue you own',
parkIssued: 'Issued passes',
parkRevoke: 'Revoke',
parkRevoked: 'Revoked',
parkRevokeFailed: 'Could not revoke',
v: {
valid: 'Valid',
validDesc: 'Signature valid and issued by the venue.',
expired: 'Expired',
expiredDesc: 'Past its valid-thru date.',
revoked: 'Revoked',
revokedDesc: 'The issuer cancelled this pass — do not accept it.',
invalid: 'Invalid',
invalidDesc: 'The signature does not verify. Do not trust it.',
notFound: 'Not found',
notFoundDesc: 'No matching record on the relays.',
unverifiedIssuer: 'Unverified issuer',
unverifiedIssuerDesc: 'This pass names a venue not owned by the issuer’s key.',
notBound: 'Not bound',
notBoundDesc: 'Not bound to a venue — proves nothing about access.',
noRsvp: 'No RSVP',
noRsvpDesc: 'The holder has no RSVP on record for this event.',
admitted: 'Admitted',
admittedDesc: 'Organiser’s event + the holder RSVP’d.',
},
passes: {
tab: 'My passes',
pass: 'Access pass',
none: 'No passes yet. When a venue or organiser issues you one, it shows up here.',
},
facets: {
title: 'Show identity facets',
crews: 'Crews',
local: 'Local',
roles: 'Lab roles',
},
roles: {
coach: 'Coach',
filmer: 'Filmer',
editor: 'Editor',
photographer: 'Photographer',
builder: 'Builder',
artist: 'Artist',
graffiti: 'Graffiti',
tattoo: 'Tattoo',
trainer: 'Trainer',
musician: 'Musician',
judge: 'Judge',
organizer: 'Organizer',
barber: 'Barber',
},
crew: {
crewCard: 'CREW CARD',
crewSub: 'Your crew, on a card',
pick: 'Choose a crew',
none: 'You are not in a crew yet — join or start one first.',
hint: 'The card is built from the crew’s own signed data. Edit the crew on its page to change it.',
members: 'MEMBERS',
spots: 'SPOTS',
est: 'Est. {{year}}',
openCall: 'Open call',
verifiedCrew: 'Verified crew',
viewCrew: 'View the crew',
},
event: {
pass: 'EVENT PASS',
passSub: 'A pass for an event you’re going to',
pick: 'Choose an event',
none: 'No events yet — create one, or RSVP to one, first.',
hint: 'The pass is built from the event’s own signed data. Only the organiser can change it.',
admits: 'ADMITS',
going: '{{count}} going',
verifiedEvent: 'Verified event',
viewEvent: 'View the event',
issuePass: 'Issue a check-in pass',
issuePassBtn: 'Issue pass',
issuePassHint: 'A signed pass for a specific attendee — they show it at the door.',
notOrganiser: 'Only the organiser can issue passes',
passIssued: 'Pass issued',
issueFailed: 'Could not issue the pass',
issuedPasses: 'Issued check-in passes',
},
studio: {
unsaved: 'Unsaved changes',
saved: 'Saved',
saveChanges: 'Save changes',
allSaved: 'Saved — up to date',
showOnCard: 'Show on card',
derived: 'From your Skater profile',
noSkaterProfile: 'No Skater profile yet —',
editSkaterProfile: 'add your styles & level',
noPhoto: 'No profile photo yet —',
addPhoto: 'add one in your profile',
finishTitle: 'Finish your card',
finishDesc: 'A photo and a Skater profile make it look real.',
checkPhoto: 'Profile photo',
checkName: 'Display name',
checkProfile: 'Styles & level',
checkCrew: 'Join a crew',
moreTypes: 'More card types unlock as you map spots you own, join crews, or RSVP to events.',
accent: 'Accent colour',
accentHint: 'Overrides the theme accent',
accentClear: 'Use theme colour',
logo: 'Custom logo / crest',
logoAdd: 'Upload',
logoChange: 'Change',
logoClear: 'Remove',
},
back: {
title: 'Printable back side',
desc: 'Crews, roles, sponsors & links on the reverse',
showBack: 'Show back',
showFront: 'Show front',
empty: 'Nothing here yet — add crews, roles or sponsors.',
},
verify: {
checking: 'Checking the card…',
notFound: 'Card not found',
notFoundDesc: 'No matching event was found on the relays. It may have been deleted, or the link is wrong.',
invalid: 'Signature invalid',
invalidDesc: 'This event exists but its signature does not verify. Do not trust it.',
authentic: 'Authentic',
authenticDesc: 'The issuer’s signature is valid.',
kind: 'Type',
kindPark: 'Park Access Card',
kindRider: 'Rider Card',
kindCrew: 'Crew Card',
kindEvent: 'Event pass',
organiser: 'Organiser',
expired: 'Expired',
valid: 'Valid',
holder: 'Holder',
ticketHolder: 'Ticket holder',
rsvp: 'RSVP',
rsvpGoing: 'Going',
rsvpInterested: 'Interested',
rsvpNone: 'No RSVP found',
revoked: 'Pass revoked',
revokedDesc: 'The issuer cancelled this pass — do not accept it.',
issuedBy: 'Issued by',
accessLevel: 'Access level',
validThru: 'Valid thru',
selfIssued: 'Status',
selfIssuedDesc: 'Self-issued identity card',
venue: 'Venue',
venueVerified: 'Verified — this venue’s owner key matches the issuer.',
venueMismatch: 'This pass names a venue that is not owned by the issuer’s key.',
venueNone: 'Not bound to a venue.',
viewCard: 'View the card',
disclaimer: 'A valid signature proves who signed this card — it is not proof of skill or of access rights beyond what the venue issued.',
details: 'Details',
invalidLink: 'Invalid link',
invalidLinkDesc: 'That doesn’t look like a valid card link.',
},
public: {
invalid: 'That doesn’t look like a valid card link.',
createYourOwn: 'Create your own card',
editYourCard: 'Edit your card',
backToEditor: 'Back to the editor',
backHome: 'Back home',
notCustomised: 'This skater hasn’t customised their card yet — this is the default look.',
verifyNote: 'Scan the QR on the card to verify it.',
},
},
role: {
owner: 'Owner',
admin: 'Admin',
organiser: 'Organiser',
verifiedOwner: 'Verified owner',
},
nfc: {
write: 'Write to NFC tag',
written: 'Tag written',
writtenDesc: 'Tap the tag to a phone to open the verify page.',
failed: 'Could not write the tag',
},
venueAccess: {
title: 'Access passes',
desc: 'Issue a signed pass to a rider so they can prove access at the door — they show their card and anyone can scan the QR to verify it.',
active: '{{count}} active',
revoked: '{{count}} revoked',
issue: 'Issue a pass',
verifyHint: 'Verified by scanning — no app or account needed to check.',
publicTitle: 'Access passes',
publicDesc: 'This venue issues access passes. Scan a rider’s card at the door to verify it.',
badge: 'Passes',
sellerHint: 'This seller’s venue issues access passes',
},
};
export default { ...en, ...batch3En, ...batch4En };