mirror of
https://github.com/polaroi8d/cactoide.git
synced 2026-03-22 22:25:29 +00:00
feat: Add translation support
This commit is contained in:
55
src/lib/i18n/i18n.ts
Normal file
55
src/lib/i18n/i18n.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import messages from './messages.json';
|
||||
|
||||
// Simple i18n utility for English-only text management
|
||||
// Get message by key with optional interpolation
|
||||
export function t(key: string, params?: Record<string, string | number>): string {
|
||||
// Navigate through nested keys (e.g., 'common.cancel')
|
||||
const keys = key.split('.');
|
||||
let value: unknown = messages;
|
||||
|
||||
for (const k of keys) {
|
||||
if (value && typeof value === 'object' && k in value) {
|
||||
value = (value as Record<string, unknown>)[k];
|
||||
} else {
|
||||
console.warn(`Translation key not found: ${key}`);
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof value !== 'string') {
|
||||
console.warn(`Translation value is not a string: ${key}`);
|
||||
return key;
|
||||
}
|
||||
|
||||
// Interpolate parameters
|
||||
if (params) {
|
||||
return value.replace(/\{(\w+)\}/g, (match, paramKey) => {
|
||||
return params[paramKey]?.toString() || match;
|
||||
});
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Format plural forms (basic implementation)
|
||||
export function tp(key: string, count: number, params?: Record<string, string | number>): string {
|
||||
const baseKey = key;
|
||||
const pluralKey = `${key}_plural`;
|
||||
|
||||
// Try to get plural form first
|
||||
let message = t(pluralKey, { ...params, count });
|
||||
|
||||
// If plural form doesn't exist, use singular
|
||||
if (message === pluralKey) {
|
||||
message = t(baseKey, { ...params, count });
|
||||
}
|
||||
|
||||
// Replace {plural} with 's' if count > 1
|
||||
if (count !== 1) {
|
||||
message = message.replace('{plural}', 's');
|
||||
} else {
|
||||
message = message.replace('{plural}', '');
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
246
src/lib/i18n/messages.json
Normal file
246
src/lib/i18n/messages.json
Normal file
@@ -0,0 +1,246 @@
|
||||
{
|
||||
"common": {
|
||||
"required": "*",
|
||||
"cancel": "Cancel",
|
||||
"create": "Create",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"view": "View",
|
||||
"home": "Home",
|
||||
"loading": "Loading...",
|
||||
"error": "Error",
|
||||
"success": "Success",
|
||||
"name": "Name",
|
||||
"date": "Date",
|
||||
"time": "Time",
|
||||
"location": "Location",
|
||||
"type": "Type",
|
||||
"visibility": "Visibility",
|
||||
"public": "Public",
|
||||
"private": "Private",
|
||||
"limited": "Limited",
|
||||
"unlimited": "Unlimited",
|
||||
"capacity": "Capacity",
|
||||
"attendees": "Attendees",
|
||||
"attendeeLimit": "Attendee Limit",
|
||||
"enterLimit": "Enter limit",
|
||||
"enterEventName": "Enter event name",
|
||||
"enterLocation": "Enter location",
|
||||
"enterYourName": "Enter your name",
|
||||
"enterNumberOfGuests": "Enter number of guests",
|
||||
"yourName": "Your Name",
|
||||
"numberOfGuests": "Number of Guests",
|
||||
"addGuests": "Add guest users",
|
||||
"joinEvent": "Join Event",
|
||||
"copyLink": "Copy Link",
|
||||
"addToCalendar": "Add to Calendar",
|
||||
"close": "Close",
|
||||
"closeModal": "Close modal",
|
||||
"removeRSVP": "Remove RSVP",
|
||||
"updating": "Updating...",
|
||||
"creating": "Creating...",
|
||||
"adding": "Adding...",
|
||||
"updateEvent": "Update Event",
|
||||
"createEvent": "Create Event",
|
||||
"createNewEvent": "Create New Event",
|
||||
"createYourFirstEvent": "Create Your First Event",
|
||||
"editEvent": "Edit Event",
|
||||
"deleteEvent": "Delete Event",
|
||||
"myEvents": "My Events",
|
||||
"discover": "Discover",
|
||||
"noEventsYet": "No Events Yet",
|
||||
"noPublicEventsYet": "No Public Events Yet",
|
||||
"noAttendeesYet": "No attendees yet",
|
||||
"beFirstToJoin": "Be the first to join!",
|
||||
"eventNotFound": "Event Not Found",
|
||||
"eventIsFull": "Event is Full!",
|
||||
"maximumCapacityReached": "Maximum capacity reached",
|
||||
"eventLinkCopied": "Event link copied to clipboard!",
|
||||
"rsvpAddedSuccessfully": "RSVP added successfully!",
|
||||
"removedRsvpSuccessfully": "Removed RSVP successfully.",
|
||||
"anUnexpectedErrorOccurred": "An unexpected error occurred.",
|
||||
"somethingWentWrong": "Something went wrong. Please try again.",
|
||||
"failedToAddRsvp": "Failed to add RSVP",
|
||||
"failedToRemoveRsvp": "Failed to remove RSVP",
|
||||
"failedToDeleteEvent": "Failed to delete event",
|
||||
"youMayNotHavePermission": "You may not have permission to delete this event.",
|
||||
"anErrorOccurredWhileDeleting": "An error occurred while deleting the event:",
|
||||
"databaseUnreachable": "Database unreachable.",
|
||||
"eventIdNotFound": "EventId not found",
|
||||
"eventNotExists": "Event not found",
|
||||
"failedToLoadEvent": "Failed to load event",
|
||||
"nameAndUserIdRequired": "Name and user ID are required",
|
||||
"eventCapacityExceeded": "Event capacity exceeded. You're trying to add {guests} attendees (including yourself), but only {remaining} spots remain.",
|
||||
"nameAlreadyExists": "Name already exists for this event",
|
||||
"missingOrEmptyFields": "Missing or empty fields: {fields}",
|
||||
"dateCannotBeInPast": "Date cannot be in the past.",
|
||||
"limitMustBeAtLeast2": "Limit must be at least 2 for limited events.",
|
||||
"unauthorized": "Unauthorized",
|
||||
"youCanOnlyEditYourOwnEvents": "You can only edit your own events",
|
||||
"youDoNotHavePermissionToDelete": "You do not have permission to delete this event",
|
||||
"eventIdAndUserIdRequired": "Event ID and User ID are required",
|
||||
"guestsWillBeAddedAs": "Guests will be added as \"{name}'s Guest #1\", \"{name}'s Guest #2\", etc.",
|
||||
"yourNamePlaceholder": "Your Name",
|
||||
"atTime": "at"
|
||||
},
|
||||
"navigation": {
|
||||
"home": "Home",
|
||||
"discover": "Discover",
|
||||
"create": "Create",
|
||||
"myEvents": "My Events"
|
||||
},
|
||||
"home": {
|
||||
"title": "Cactoide - The RSVP site",
|
||||
"description": "Create and manage event RSVPs. No registration required, instant sharing.",
|
||||
"mainTitle": "Cactoide(ea)",
|
||||
"subtitle": "The Ultimate RSVP Platform",
|
||||
"tagline": "Create, share, and manage events with zero friction.",
|
||||
"whyCactoideTitle": "Why Cactoide(ae)?🌵",
|
||||
"whyCactoideDescription": "Like the cactus, great events bloom under any condition when managed with care. Cactoide(ae) helps you streamline RSVPs, simplify coordination, and keep every detail efficient—so your gatherings are resilient, vibrant, and unforgettable.",
|
||||
"createEventNow": "Create Event Now",
|
||||
"discoverPublicEventsTitle": "Discover Public Events",
|
||||
"discoverPublicEventsDescription": "See what others are planning and get inspired",
|
||||
"browseAllPublicEvents": "Browse All Public Events",
|
||||
"whyCactoideFeatureTitle": "Why Cactoide?",
|
||||
"instantEventCreationTitle": "Instant Event Creation",
|
||||
"instantEventCreationDescription": "Create events in seconds with our streamlined form. No accounts, no waiting, just pure efficiency.",
|
||||
"oneClickSharingTitle": "One-Click Sharing",
|
||||
"oneClickSharingDescription": "Each event gets a unique, memorable URL. Share instantly via any platform or messaging app.",
|
||||
"allInOneClarityTitle": "All-in-One Clarity",
|
||||
"allInOneClarityDescription": "No more scrolling through endless chats and reactions. See everyone's availability and responses neatly in one place.",
|
||||
"noHassleNoSignUpsTitle": "No Hassle, No Sign-Ups",
|
||||
"noHassleNoSignUpsDescription": "Skip registrations and endless forms. Unlike other event platforms, you create and share instantly — no accounts, no barriers.",
|
||||
"smartLimitsTitle": "Smart Limits",
|
||||
"smartLimitsDescription": "Choose between unlimited RSVPs or set a limited capacity. Perfect for any event size.",
|
||||
"effortlessSimplicityTitle": "Effortless Simplicity",
|
||||
"effortlessSimplicityDescription": "Designed to be instantly clear and easy. No learning curve — just open, create, and go.",
|
||||
"howItWorksTitle": "How It Works",
|
||||
"step1Title": "Create Event",
|
||||
"step1Description": "Fill out a simple form with event details. Choose between limited or unlimited capacity.",
|
||||
"step2Title": "Get Unique URL",
|
||||
"step2Description": "Receive a random, memorable URL for your event. Perfect for sharing anywhere.",
|
||||
"step3Title": "Collect RSVPs",
|
||||
"step3Description": "People visit your link and join with just their name. No accounts needed.",
|
||||
"ctaTitle": "Ready to Create Your First Event?",
|
||||
"ctaDescription": "Join thousands of event organizers who trust Cactoide",
|
||||
"ctaButton": "Create"
|
||||
},
|
||||
"create": {
|
||||
"title": "Create Event - Cactoide",
|
||||
"formTitle": "Create New Event",
|
||||
"eventNameLabel": "Name",
|
||||
"eventNamePlaceholder": "Enter event name",
|
||||
"dateLabel": "Date",
|
||||
"timeLabel": "Time",
|
||||
"locationLabel": "Location",
|
||||
"locationPlaceholder": "Enter location",
|
||||
"typeLabel": "Type",
|
||||
"unlimitedOption": "Unlimited",
|
||||
"limitedOption": "Limited",
|
||||
"attendeeLimitLabel": "Attendee Limit",
|
||||
"attendeeLimitPlaceholder": "Enter limit",
|
||||
"visibilityLabel": "Visibility",
|
||||
"publicOption": "🌍 Public",
|
||||
"privateOption": "🔒 Private",
|
||||
"publicDescription": "Public events are visible to everyone and can be discovered by others",
|
||||
"privateDescription": "Private events are only visible to you and people you share the link with",
|
||||
"creatingEvent": "Creating Event...",
|
||||
"createEventButton": "Create Event"
|
||||
},
|
||||
"event": {
|
||||
"title": "{eventName} - Cactoide",
|
||||
"eventTitle": "Event - Cactoide",
|
||||
"editTitle": "Edit Event - {eventName} - Cactoide",
|
||||
"myEventsTitle": "My Events - Cactoide",
|
||||
"eventNotFoundTitle": "Event Not Found",
|
||||
"eventNotFoundDescription": "The event you're looking for doesn't exist or has been removed.",
|
||||
"joinThisEvent": "Join This Event",
|
||||
"eventIsFull": "Event is Full!",
|
||||
"maximumCapacityReached": "Maximum capacity reached",
|
||||
"yourNameLabel": "Your Name",
|
||||
"yourNamePlaceholder": "Enter your name",
|
||||
"addGuestsLabel": "Add guest users",
|
||||
"numberOfGuestsLabel": "Number of Guests",
|
||||
"numberOfGuestsPlaceholder": "Enter number of guests",
|
||||
"guestsWillBeAddedAs": "Guests will be added as \"{name}'s Guest #1\", \"{name}'s Guest #2\", etc.",
|
||||
"joinEventButton": "Join Event",
|
||||
"joinEventWithGuests": "Join Event + {count} Guest{plural}",
|
||||
"adding": "Adding...",
|
||||
"attendeesTitle": "Attendees",
|
||||
"noAttendeesYet": "No attendees yet",
|
||||
"beFirstToJoin": "Be the first to join!",
|
||||
"copyLinkButton": "Copy Link",
|
||||
"addToCalendarButton": "Add to Calendar",
|
||||
"eventLinkCopied": "Event link copied to clipboard!",
|
||||
"rsvpAddedSuccessfully": "RSVP added successfully!",
|
||||
"removedRsvpSuccessfully": "Removed RSVP successfully.",
|
||||
"failedToAddRsvp": "Failed to add RSVP",
|
||||
"failedToRemoveRsvp": "Failed to remove RSVP",
|
||||
"editEventTitle": "Edit Event",
|
||||
"editEventDescription": "Update your event details",
|
||||
"updatingEvent": "Updating...",
|
||||
"updateEventButton": "Update Event",
|
||||
"myEventsDescription": "Manage your created events",
|
||||
"noEventsYetTitle": "No Events Yet",
|
||||
"noEventsYetDescription": "You haven't created any events yet. Start by creating your first event!",
|
||||
"createYourFirstEventButton": "Create Your First Event",
|
||||
"deleteEventTitle": "Delete Event",
|
||||
"deleteEventDescription": "Are you sure you want to delete \"{eventName}\"? This action cannot be undone and will remove all RSVPs.",
|
||||
"deleteButton": "Delete",
|
||||
"viewEventAriaLabel": "View event",
|
||||
"editEventAriaLabel": "Edit event",
|
||||
"deleteEventAriaLabel": "Delete event",
|
||||
"removeRsvpAriaLabel": "Remove RSVP"
|
||||
},
|
||||
"discover": {
|
||||
"title": "Discover Events - Cactoide",
|
||||
"noPublicEventsTitle": "No Public Events Yet",
|
||||
"noPublicEventsDescription": "There are no public events available at the moment. Be the first to create one!",
|
||||
"createButton": "Create",
|
||||
"publicEventsTitle": "Public Events ({count})",
|
||||
"publicEventsDescription": "Discover events created by the community",
|
||||
"searchPlaceholder": "Search events by name, location...",
|
||||
"searchInputAriaLabel": "Search input",
|
||||
"toggleFiltersAriaLabel": "Toggle filters",
|
||||
"typeFilterLabel": "Type:",
|
||||
"typeFilterAll": "All",
|
||||
"typeFilterLimited": "Limited",
|
||||
"typeFilterUnlimited": "Unlimited",
|
||||
"statusFilterLabel": "Status:",
|
||||
"statusFilterAll": "All events",
|
||||
"statusFilterUpcoming": "Upcoming events",
|
||||
"statusFilterPast": "Past events",
|
||||
"timeFilterLabel": "Time:",
|
||||
"timeFilterAny": "Any time",
|
||||
"timeFilterNextWeek": "Next week",
|
||||
"timeFilterNextMonth": "Next month",
|
||||
"sortOrderLabel": "Sort:",
|
||||
"sortOrderEarliest": "Earliest first",
|
||||
"sortOrderLatest": "Latest first",
|
||||
"viewButton": "View",
|
||||
"noEventsFoundTitle": "No events found",
|
||||
"noEventsFoundDescription": "Try adjusting your search terms or browse all events"
|
||||
},
|
||||
"calendar": {
|
||||
"addToCalendarTitle": "Add to Calendar",
|
||||
"googleCalendarTitle": "Google Calendar",
|
||||
"googleCalendarDescription": "Add to Google Calendar",
|
||||
"microsoftOutlookTitle": "Microsoft Outlook",
|
||||
"microsoftOutlookDescription": "Add to Outlook Calendar",
|
||||
"downloadICalTitle": "Download iCal File",
|
||||
"downloadICalDescription": "Download .ics file for any calendar app"
|
||||
},
|
||||
"errors": {
|
||||
"title": "Error - Cactoide",
|
||||
"errorTitle": "Error",
|
||||
"anUnexpectedErrorOccurred": "An unexpected error occurred.",
|
||||
"homeButton": "Home"
|
||||
},
|
||||
"layout": {
|
||||
"defaultTitle": "Cactoide -",
|
||||
"defaultDescription": "Create and manage event RSVPs",
|
||||
"userIdCookieText": "Your UserID storated as a cookie:",
|
||||
"firstTimeVisiting": "First time visiting. Generating new UserID...",
|
||||
"copyright": "© 2025 Cactoide"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user