966 lines
42 KiB
TypeScript
966 lines
42 KiB
TypeScript
export type Locale = "en" | "fi" | "sv";
|
||
|
||
const baseMessages = {
|
||
en: {
|
||
brand: "lomavuokraus.fi",
|
||
navProfile: "Profile",
|
||
navMyListings: "My listings",
|
||
navNewListing: "New listing",
|
||
navAdmin: "Admin",
|
||
navApprovals: "Approvals",
|
||
navUsers: "Users",
|
||
navMonitoring: "Monitoring",
|
||
navSettings: "Settings",
|
||
navLogout: "Logout",
|
||
navLogin: "Login",
|
||
navSignup: "Sign up",
|
||
navBrowse: "Browse listings",
|
||
navLanguage: "Language",
|
||
approvalsBadge: "{count}",
|
||
heroEyebrow: "lomavuokraus.fi",
|
||
heroTitle: "Find your next Finnish getaway",
|
||
heroBody:
|
||
"Discover Finnish cabins, apartments and villas posted directly by their owners. Listings are verified before publishing and you contact hosts directly — simple and transparent.",
|
||
ctaViewSample: "View a sample listing",
|
||
ctaHealth: "Check health endpoint",
|
||
ctaBrowse: "Browse listings",
|
||
highlightQualityTitle: "Quality stays",
|
||
highlightQualityBody:
|
||
"Curated cabins and villas with clear availability and simple booking.",
|
||
highlightLocalTitle: "Local-first",
|
||
highlightLocalBody:
|
||
"Built for Finnish seasons with fast content delivery in the Nordics.",
|
||
highlightApiTitle: "API-friendly",
|
||
highlightApiBody:
|
||
"Structured data so you can surface listings wherever you need them.",
|
||
runtimeConfigTitle: "Runtime configuration",
|
||
runtimeConfigLead: "Build-time and runtime values used by the service.",
|
||
runtimeAppEnv: "APP_ENV",
|
||
runtimeSiteUrl: "NEXT_PUBLIC_SITE_URL",
|
||
runtimeApiBase: "NEXT_PUBLIC_API_BASE",
|
||
aboutTitle: "About lomavuokraus.fi",
|
||
aboutLead:
|
||
"A focused marketplace for Finnish holiday rentals with fast browsing, clear moderation, and a simple host experience.",
|
||
pricingTitle: "Pricing",
|
||
pricingLead: "Straightforward pricing for hosts with no surprises.",
|
||
pricingMonthly: "Monthly",
|
||
pricingAnnual: "Annual",
|
||
pricingPerMonth: "per month",
|
||
pricingPerYear: "per year (save 20%)",
|
||
pricingMonthlyBody: "Start with a monthly plan at 10€ per listing.",
|
||
pricingAnnualBody:
|
||
"Pay annually for 100€ per listing and keep your costs predictable.",
|
||
pricingPerListing: "Pricing is per active listing. Cancel anytime.",
|
||
pricingNotesTitle: "Notes",
|
||
pricingNotesBody:
|
||
"We keep pricing simple while we build out hosting tools, messaging, and integrations. All current features are included.",
|
||
footerAbout: "About",
|
||
footerPricing: "Pricing",
|
||
footerPrivacy: "Privacy & cookies",
|
||
footerCookieNotice:
|
||
"We use only essential cookies for login and security. By using this site you consent; if you do not accept, please do not use the site.",
|
||
loginTitle: "Login",
|
||
emailLabel: "Email",
|
||
passwordLabel: "Password",
|
||
loginButton: "Login",
|
||
loggingIn: "Logging in…",
|
||
loginSuccess: "Login successful.",
|
||
registerTitle: "Create an account",
|
||
registerLead:
|
||
"Verify email is required, and an admin will approve your account.",
|
||
nameOptional: "Name (optional)",
|
||
passwordHint: "Password (min 8 chars)",
|
||
registerButton: "Register",
|
||
registering: "Submitting…",
|
||
registerSuccess:
|
||
"Registration successful. Check your email for a verification link.",
|
||
forgotTitle: "Forgot your password?",
|
||
forgotLead: "Enter your email and we will send a reset link.",
|
||
forgotSubmit: "Send reset link",
|
||
forgotSuccess: "If that email exists, a reset link has been sent.",
|
||
forgotError: "Could not send reset link right now.",
|
||
resetTitle: "Reset password",
|
||
resetLead: "Set a new password for your account.",
|
||
resetSubmit: "Set new password",
|
||
resetSuccess: "Password updated. You can log in now.",
|
||
resetError: "Failed to reset password. The link may be invalid or expired.",
|
||
resetMissingToken:
|
||
"Reset token missing. Please use the link from your email.",
|
||
forgotCta: "Forgot password?",
|
||
pendingAdminTitle: "Admin: pending items",
|
||
pendingUsersTitle: "Pending users",
|
||
pendingListingsTitle: "Pending listings",
|
||
noPendingUsers: "No pending users.",
|
||
noPendingListings: "No pending listings.",
|
||
statusLabel: "status",
|
||
slugsLabel: "Slugs",
|
||
verifiedLabel: "verified",
|
||
approve: "Approve",
|
||
approveAdmin: "Approve + make admin",
|
||
reject: "Reject",
|
||
remove: "Remove",
|
||
publish: "Publish",
|
||
approvalsMessage: "Listing updated",
|
||
userUpdated: "User updated",
|
||
adminRequired: "Admin access required",
|
||
adminUsersTitle: "Admin: users",
|
||
adminUsersLead: "Manage user roles and approvals.",
|
||
monitorTitle: "Admin: monitoring",
|
||
monitorLead:
|
||
"Live status for Hetzner nodes, Kubernetes pods, and PostgreSQL.",
|
||
monitorRefresh: "Refresh now",
|
||
monitorLastUpdated: "Last updated",
|
||
monitorNoData: "No data yet.",
|
||
monitorHetznerTitle: "Hetzner nodes",
|
||
monitorHetznerMissingToken: "Set HCLOUD_TOKEN to show Hetzner nodes.",
|
||
monitorHetznerEmpty: "No Hetzner servers returned.",
|
||
monitorK8sTitle: "Kubernetes",
|
||
monitorNodesTitle: "Nodes",
|
||
monitorPodsTitle: "Pods",
|
||
monitorNoPods: "No pods in lomavuokraus namespaces.",
|
||
monitorRestarts: "Restarts",
|
||
monitorAge: "Age",
|
||
monitorDbTitle: "PostgreSQL",
|
||
monitorServerTime: "Server time",
|
||
monitorDbSize: "Database size",
|
||
monitorDbRecovery: "Recovery mode",
|
||
monitorConnections: "Connections",
|
||
monitorHealthy: "Healthy",
|
||
monitorAttention: "Needs attention",
|
||
monitorLoadFailed: "Failed to load monitoring data.",
|
||
monitorCreated: "Created",
|
||
monitorLastReady: "Last Ready transition",
|
||
adminSettingsTitle: "Admin: settings",
|
||
adminSettingsLead: "Site-wide feature toggles and policies.",
|
||
tableEmail: "Email",
|
||
tableRole: "Role",
|
||
tableStatus: "Status",
|
||
tableVerified: "Verified",
|
||
tableApproved: "Approved",
|
||
myListingsTitle: "My listings",
|
||
createNewListing: "Create new listing",
|
||
noListings: "No listings yet.",
|
||
createOne: "Create one",
|
||
loading: "Loading…",
|
||
edit: "Edit",
|
||
view: "View",
|
||
removing: "Removing…",
|
||
removed: "Listing removed",
|
||
removeConfirm: "Remove this listing? It will be hidden from others.",
|
||
myProfileTitle: "My profile",
|
||
notLoggedIn: "Not logged in",
|
||
profileEmail: "Email",
|
||
profileName: "Name",
|
||
profilePhone: "Phone",
|
||
profileRole: "Role",
|
||
profileStatus: "Status",
|
||
profileEmailVerified: "Email verified",
|
||
profileApproved: "Approved",
|
||
profileUpdated: "Profile updated",
|
||
billingSettingsTitle: "Billing assistant",
|
||
billingSettingsLead:
|
||
"Opt into automated billing emails via the n8n agent. Set defaults and per-listing overrides.",
|
||
billingEnableLabel: "Enable billing assistant for my listings",
|
||
billingAccountNameLabel: "Billing account owner name",
|
||
billingAccountPlaceholder: "Use profile default",
|
||
billingIbanLabel: "IBAN for payouts",
|
||
billingIbanPlaceholder: "Use profile default",
|
||
billingIncludeVat: "Include a VAT line on invoices",
|
||
billingListingsTitle: "Per-listing billing",
|
||
billingListingsLead:
|
||
"Override billing details per listing; blank fields inherit your profile defaults.",
|
||
billingNoListings: "No listings available yet.",
|
||
billingVatChoice: "VAT line preference",
|
||
billingVatInherit: "Use profile choice",
|
||
billingVatYes: "Include VAT line",
|
||
billingVatNo: "Do not include VAT line",
|
||
billingDisabledHint:
|
||
"Enable the billing assistant to manage invoice details and VAT preferences.",
|
||
billingLoadFailed: "Failed to load billing settings.",
|
||
billingSaveFailed: "Could not save billing settings.",
|
||
billingSaved: "Billing settings saved.",
|
||
settingsSaved: "Settings saved.",
|
||
emailLocked: "Email cannot be changed",
|
||
save: "Save",
|
||
saving: "Saving…",
|
||
yes: "yes",
|
||
no: "no",
|
||
verifyTitle: "Email verification",
|
||
verifyOk:
|
||
"Your email is verified. An admin will approve your account shortly.",
|
||
verifyFail: "Verification failed or token invalid.",
|
||
listingLocation: "Location",
|
||
listingAddress: "Address",
|
||
listingCapacity: "Capacity",
|
||
listingPrices: "Price (starting from)",
|
||
listingAmenities: "Amenities",
|
||
listingNoAmenities: "No amenities listed yet.",
|
||
listingContact: "Contact",
|
||
contactLoginToView: "Log in to view contact details for this listing.",
|
||
listingMoreInfo: "More info",
|
||
availabilityTitle: "Availability calendar",
|
||
availabilityLegendBooked: "Booked",
|
||
availabilityMissing: "Calendar not connected yet.",
|
||
localeLabel: "Locale",
|
||
homeCrumb: "Home",
|
||
createListingTitle: "Create listing",
|
||
languageTabsLabel: "Listing languages",
|
||
languageTabsHint: "Add translations for each supported language",
|
||
localeSectionTitle: "Listing content",
|
||
localeReady: "Ready",
|
||
localePartial: "In progress",
|
||
localeMissing: "Missing",
|
||
aiHelperTitle: "AI translation helper",
|
||
aiHelperLead: "Let AI fill the other languages for you.",
|
||
aiOptionalHint: "Optional: AI helper can fill other languages.",
|
||
aiAutoExplain:
|
||
"Enter the texts in any one language and click the button; AI will fill the remaining locales.",
|
||
aiAutoTranslate: "Auto-translate missing languages",
|
||
aiAutoTranslating: "Translating…",
|
||
aiAutoSuccess: "Translations updated with AI.",
|
||
aiAutoError:
|
||
"AI translation failed. Please try again or use manual mode below.",
|
||
aiHelperNote: "Uses OpenAI to translate missing texts.",
|
||
aiManualLead:
|
||
"If auto-translate fails, copy this prompt to your AI assistant and paste the JSON reply below.",
|
||
aiPromptLabel: "Prompt to send to AI",
|
||
aiCopyPrompt: "Copy prompt",
|
||
aiPromptCopied: "Copied",
|
||
aiCopyError: "Copy failed",
|
||
aiResponseLabel: "Paste AI response (JSON)",
|
||
aiApply: "Apply AI response",
|
||
aiApplyError:
|
||
"Could not read AI response. Please ensure it is valid JSON with a locales object.",
|
||
aiApplySuccess: "Translations updated from AI response.",
|
||
settingContactVisibilityTitle: "Listing contact visibility",
|
||
settingContactVisibilityHelp:
|
||
"Hide host contact details from anonymous visitors so only logged-in users can see them.",
|
||
settingRequireLoginForContact: "Require login to see contact details",
|
||
translationMissing:
|
||
"Add at least one language with a title and description.",
|
||
saveDraft: "Save draft",
|
||
missingFields: "Missing: {fields}",
|
||
loginToCreate: "Please log in first to create a listing.",
|
||
slugLabel: "Slug",
|
||
slugHelp:
|
||
"Short, easy-to-type link name; use lowercase and hyphens (e.g. lake-cabin).",
|
||
slugPreview: "Your listing link will be: {url}",
|
||
slugChecking: "Checking availability…",
|
||
slugAvailable: "Slug is available",
|
||
slugTaken: "Slug already in use",
|
||
slugCheckError: "Could not check slug right now",
|
||
localeInput: "Locale",
|
||
titleLabel: "Title",
|
||
descriptionLabel: "Description",
|
||
teaserLabel: "Teaser",
|
||
teaserHelp: "Short intro shown on cards",
|
||
countryLabel: "Country",
|
||
regionLabel: "Region",
|
||
cityLabel: "City",
|
||
contactNameLabel: "Contact name",
|
||
contactEmailLabel: "Contact email",
|
||
streetAddressLabel: "Street address",
|
||
addressNoteLabel: "Address note (optional)",
|
||
addressNotePlaceholder: "Directions, parking, or arrival details",
|
||
latitudeLabel: "Latitude (optional)",
|
||
longitudeLabel: "Longitude (optional)",
|
||
maxGuestsLabel: "Max guests",
|
||
bedroomsLabel: "Bedrooms",
|
||
bedsLabel: "Beds",
|
||
bathroomsLabel: "Bathrooms",
|
||
priceWeekdayLabel: "Price starting from (weeknight, € / night)",
|
||
priceWeekendLabel: "Price starting from (weekend, € / night)",
|
||
priceHintHelp:
|
||
"These prices are indicative only (starting from), not a binding offer.",
|
||
priceStartingFromShort: "Starting from {price}€ / night",
|
||
priceWeekdayShort: "{price}€ weekday",
|
||
priceWeekendShort: "{price}€ weekend",
|
||
priceNotSet: "Not provided",
|
||
calendarUrlsLabel: "Availability calendars (iCal URLs, one per line)",
|
||
calendarUrlsHelp:
|
||
"Paste iCal links from other platforms. We will merge them to show availability.",
|
||
imagesLabel: "Images",
|
||
imagesHelp: "Upload up to {count} images (max {sizeMb}MB each).",
|
||
imagesTooMany: "Too many images (max {count}).",
|
||
imagesTooLarge: "Image is too large (max {sizeMb}MB).",
|
||
imagesReadFailed: "Could not read selected images.",
|
||
imagesRequired: "Please upload at least one image.",
|
||
coverImageLabel: "Cover image order",
|
||
coverImageHelp: "1-based index of the uploaded images (defaults to 1)",
|
||
coverChoice: "Cover position {index}",
|
||
makeCover: "Make cover",
|
||
existingImageLabel: "Existing image",
|
||
imageRemoveLastError: "Add another image before removing the last one.",
|
||
imageRemoveFailed: "Failed to remove image.",
|
||
submitListing: "Create listing",
|
||
submittingListing: "Submitting…",
|
||
createListingSuccess: "Listing created with id {id} (status: {status})",
|
||
approvalsCountLabel: "Approvals",
|
||
approvalsPending: "{count} pending",
|
||
amenitySauna: "Sauna",
|
||
amenityFireplace: "Fireplace",
|
||
amenityWifi: "Wi-Fi",
|
||
amenityPets: "Pets allowed",
|
||
amenityLake: "By the lake",
|
||
amenityAirConditioning: "Air conditioning",
|
||
amenityKitchen: "Kitchen",
|
||
amenityDishwasher: "Dishwasher",
|
||
amenityWashingMachine: "Washing machine",
|
||
amenityBarbecue: "Barbecue grill",
|
||
amenityMicrowave: "Microwave",
|
||
amenityFreeParking: "Free parking",
|
||
amenityEvAvailable: "EV charging nearby",
|
||
amenityEvNearby: "EV charging nearby",
|
||
amenityEvOnSite: "EV charging on-site",
|
||
amenityWheelchairAccessible: "Wheelchair accessible",
|
||
amenitySkiPass: "Ski pass included",
|
||
evChargingLabel: "EV charging",
|
||
evChargingYes: "EV charging available",
|
||
evChargingNo: "No EV charging",
|
||
evChargingAny: "Any",
|
||
evChargingExplain: "Is there EV charging available at the property?",
|
||
capacityGuests: "{count} guests",
|
||
capacityBedrooms: "{count} bedrooms",
|
||
capacityBeds: "{count} beds",
|
||
capacityBathrooms: "{count} bathrooms",
|
||
capacityUnknown: "Capacity not set",
|
||
browseListingsTitle: "Browse listings",
|
||
browseListingsLead:
|
||
"Search public listings, filter by location, and explore them on the map.",
|
||
sampleBadge: "Sample",
|
||
privacyTitle: "Privacy & cookies",
|
||
privacyUpdated: "Updated: {date}",
|
||
privacyCollectTitle: "What data we collect",
|
||
privacyCollectAccounts:
|
||
"Account data: email, password hash, name, phone (optional), role/status.",
|
||
privacyCollectListings:
|
||
"Listing data: location, contact details, amenities, photos, translations.",
|
||
privacyCollectLogs:
|
||
"Operational logs: minimal request metadata for diagnostics.",
|
||
privacyUseTitle: "How we use your data",
|
||
privacyUseAuth: "To authenticate users and manage sessions.",
|
||
privacyUseListings: "To publish and moderate listings.",
|
||
privacyUseMail:
|
||
"To send transactional email (verification, password reset).",
|
||
privacyUseLegal: "To comply with legal requests or protect the service.",
|
||
privacyStoreTitle: "Storage & retention",
|
||
privacyStoreDb: "Data is stored in our Postgres database hosted in the EU.",
|
||
privacyStoreBackups:
|
||
"Backups are retained for disaster recovery; removed accounts/listings may persist in backups for a limited time.",
|
||
privacyCookiesTitle: "Cookies",
|
||
privacyCookiesSession:
|
||
"We use essential cookies only: a session cookie for login/authentication.",
|
||
privacyCookiesNoTracking:
|
||
"No analytics, marketing, or tracking cookies are used.",
|
||
privacySharingTitle: "Sharing",
|
||
privacySharingAds:
|
||
"We do not sell or share personal data with advertisers.",
|
||
privacySharingOps:
|
||
"Data may be shared with service providers strictly for running the service (email delivery, hosting).",
|
||
privacyRightsTitle: "Your rights",
|
||
privacyRightsAccess:
|
||
"Request access, correction, or deletion of your data.",
|
||
privacyRightsConsent:
|
||
"Withdraw consent for non-essential processing (we currently process only essentials).",
|
||
privacyRightsContact: "Contact support for any privacy questions.",
|
||
searchLabel: "Search",
|
||
searchPlaceholder: "Search by name, description, or city",
|
||
cityFilter: "City",
|
||
regionFilter: "Region",
|
||
searchButton: "Search",
|
||
searchAmenities: "Amenities",
|
||
searchAvailability: "Availability",
|
||
startDate: "Start date",
|
||
endDate: "End date",
|
||
availabilityOnlyWithCalendar:
|
||
"Only listings with a connected calendar are shown when filtering by dates.",
|
||
availableForDates: "Available for selected dates",
|
||
notAvailableForDates: "Unavailable for selected dates",
|
||
calendarConnected: "Calendar connected",
|
||
clearFilters: "Clear filters",
|
||
addressSearchLabel: "Find listings near an address",
|
||
addressSearchPlaceholder: "Street, city, or place",
|
||
locateAddress: "Locate on map",
|
||
addressRadiusLabel: "Within {km} km",
|
||
listingsFound: "{count} listings",
|
||
openListing: "Open listing",
|
||
mapNoResults: "No listings match your filters.",
|
||
addressNotFound: "Address not found",
|
||
addressLookupFailed: "Failed to locate address",
|
||
loadingMap: "Loading map…",
|
||
latestListingsTitle: "Latest listings",
|
||
latestListingsLead: "Fresh rentals as they are published. Tap to browse.",
|
||
},
|
||
fi: {
|
||
brand: "lomavuokraus.fi",
|
||
navProfile: "Profiili",
|
||
navMyListings: "Omat kohteet",
|
||
navNewListing: "Luo kohde",
|
||
navAdmin: "Ylläpito",
|
||
navApprovals: "Tarkastettavat",
|
||
navUsers: "Käyttäjät",
|
||
navMonitoring: "Valvonta",
|
||
navSettings: "Asetukset",
|
||
navLogout: "Kirjaudu ulos",
|
||
navLogin: "Kirjaudu",
|
||
navSignup: "Rekisteröidy",
|
||
navBrowse: "Selaa kohteita",
|
||
navLanguage: "Kieli",
|
||
approvalsBadge: "{count}",
|
||
heroEyebrow: "lomavuokraus.fi",
|
||
heroTitle: "Löydä seuraava mökkilomasi",
|
||
heroBody:
|
||
"Selaa suomalaisten mökkien, huoneistojen ja villojen ilmoituksia suoraan omistajilta. Jokainen ilmoitus tarkistetaan ennen julkaisua, ja otat vuokranantajaan yhteyttä suoraan — yksinkertaista ja läpinäkyvää.",
|
||
languageTabsLabel: "Ilmoituksen kielet",
|
||
languageTabsHint: "Lisää käännökset kaikille tuetuille kielille",
|
||
localeSectionTitle: "Ilmoituksen sisältö",
|
||
localeReady: "Valmis",
|
||
localePartial: "Kesken",
|
||
localeMissing: "Puuttuu",
|
||
aiHelperTitle: "AI-käännösapu",
|
||
aiHelperLead: "Anna tekoälyn täydentää muut kielet puolestasi.",
|
||
aiOptionalHint: "Valinnainen: AI-avustaja voi täyttää muut kielet.",
|
||
aiAutoExplain:
|
||
"Täytä tekstit yhdellä kielellä ja paina nappia; AI täyttää loput kielet.",
|
||
aiAutoTranslate: "Käännä puuttuvat kielet",
|
||
aiAutoTranslating: "Käännetään…",
|
||
aiAutoSuccess: "Käännökset päivitetty AI:lla.",
|
||
aiAutoError:
|
||
"Käännös epäonnistui. Yritä uudelleen tai käytä manuaalitilaa alla.",
|
||
aiHelperNote: "Käyttää OpenAI:ta puuttuvien tekstien kääntämiseen.",
|
||
aiManualLead:
|
||
"Jos automaattinen käännös ei toimi, kopioi prompti tekoälylle ja liitä JSON-vastaus alle.",
|
||
aiPromptLabel: "Prompti tekoälylle",
|
||
aiCopyPrompt: "Kopioi prompti",
|
||
aiPromptCopied: "Kopioitu",
|
||
aiCopyError: "Kopiointi epäonnistui",
|
||
aiResponseLabel: "Liitä tekoälyn vastaus (JSON)",
|
||
aiApply: "Käytä AI-vastausta",
|
||
aiApplyError:
|
||
"Vastausta ei voitu lukea. Varmista, että se on kelvollista JSONia ja sisältää locales-avaimen.",
|
||
aiApplySuccess: "Käännökset päivitetty AI-vastauksesta.",
|
||
settingContactVisibilityTitle: "Ilmoitusten yhteystiedot",
|
||
settingContactVisibilityHelp:
|
||
"Piilota isännän yhteystiedot kirjautumattomilta, näytä ne vain kirjautuneille käyttäjille.",
|
||
settingRequireLoginForContact: "Vaadi kirjautuminen yhteystietoihin",
|
||
translationMissing: "Täytä vähintään yhden kielen otsikko ja kuvaus.",
|
||
saveDraft: "Tallenna luonnos",
|
||
missingFields: "Puuttuu: {fields}",
|
||
ctaViewSample: "Katso esimerkkikohde",
|
||
ctaHealth: "Tarkista health-päätepiste",
|
||
ctaBrowse: "Selaa kohteita",
|
||
highlightQualityTitle: "Laadukkaat kohteet",
|
||
highlightQualityBody:
|
||
"Kuratoidut mökit ja huvilat, selkeät saatavuudet ja helppo varaus.",
|
||
highlightLocalTitle: "Suomi edellä",
|
||
highlightLocalBody:
|
||
"Tehty Suomen olosuhteisiin, nopea sisällönjakelu Pohjoismaissa.",
|
||
highlightApiTitle: "API-ystävällinen",
|
||
highlightApiBody:
|
||
"Strukturoitu data, jotta löydät kohteet kaikissa kanavissa.",
|
||
runtimeConfigTitle: "Ajoaikainen konfiguraatio",
|
||
runtimeConfigLead: "Ajo- ja rakennusaikaiset arvot, joita palvelu käyttää.",
|
||
runtimeAppEnv: "APP_ENV",
|
||
runtimeSiteUrl: "NEXT_PUBLIC_SITE_URL",
|
||
runtimeApiBase: "NEXT_PUBLIC_API_BASE",
|
||
aboutTitle: "Tietoja lomavuokraus.fi:stä",
|
||
aboutLead:
|
||
"Keskittynyt suomalainen loma-asuntopalvelu: nopea selaus, selkeä moderointi ja mutkaton kokemus vuokraajalle.",
|
||
pricingTitle: "Hinnasto",
|
||
pricingLead: "Selkeä hinnoittelu vuokraajille ilman yllätyksiä.",
|
||
pricingMonthly: "Kuukausi",
|
||
pricingAnnual: "Vuosimaksu",
|
||
pricingPerMonth: "kuukaudessa",
|
||
pricingPerYear: "vuodessa (20 % säästö)",
|
||
pricingMonthlyBody: "Aloita kuukausihinnalla 10 € per kohde.",
|
||
pricingAnnualBody:
|
||
"Maksa vuodessa 100 € per kohde ja pidä kulut ennustettavina.",
|
||
pricingPerListing:
|
||
"Hinta on per aktiivinen kohde. Voit perua milloin vain.",
|
||
pricingNotesTitle: "Huomiot",
|
||
pricingNotesBody:
|
||
"Pidämme hinnoittelun yksinkertaisena samalla kun rakennamme uusia isäntätyökaluja, viestejä ja integraatioita. Kaikki nykyiset ominaisuudet sisältyvät.",
|
||
footerAbout: "Tietoa",
|
||
footerPricing: "Hinnasto",
|
||
footerPrivacy: "Tietosuoja ja evästeet",
|
||
footerCookieNotice:
|
||
"Käytämme vain välttämättömiä evästeitä kirjautumiseen ja turvallisuuteen. Käyttämällä sivustoa hyväksyt evästeet; jos et hyväksy, älä käytä sivustoa.",
|
||
loginTitle: "Kirjaudu sisään",
|
||
emailLabel: "Sähköposti",
|
||
passwordLabel: "Salasana",
|
||
loginButton: "Kirjaudu",
|
||
loggingIn: "Kirjaudutaan…",
|
||
loginSuccess: "Kirjautuminen onnistui.",
|
||
registerTitle: "Luo tili",
|
||
registerLead:
|
||
"Sähköpostin varmistus vaaditaan, ja ylläpitäjä hyväksyy tilin.",
|
||
nameOptional: "Nimi (valinnainen)",
|
||
passwordHint: "Salasana (väh. 8 merkkiä)",
|
||
registerButton: "Rekisteröidy",
|
||
registering: "Lähetetään…",
|
||
registerSuccess:
|
||
"Rekisteröinti onnistui. Tarkista sähköpostisi vahvistuslinkin vuoksi.",
|
||
forgotTitle: "Unohditko salasanasi?",
|
||
forgotLead: "Syötä sähköpostisi niin lähetämme palautuslinkin.",
|
||
forgotSubmit: "Lähetä palautuslinkki",
|
||
forgotSuccess: "Jos sähköposti löytyy, palautuslinkki on lähetetty.",
|
||
forgotError: "Linkin lähetys epäonnistui.",
|
||
resetTitle: "Vaihda salasana",
|
||
resetLead: "Aseta uusi salasana tilillesi.",
|
||
resetSubmit: "Aseta uusi salasana",
|
||
resetSuccess: "Salasana vaihdettu. Voit nyt kirjautua sisään.",
|
||
resetError: "Salasanan vaihto epäonnistui. Linkki voi olla vanhentunut.",
|
||
resetMissingToken:
|
||
"Palautustunniste puuttuu. Käytä sähköpostista saatua linkkiä.",
|
||
forgotCta: "Unohdit salasanan?",
|
||
pendingAdminTitle: "Ylläpito: tarkastettavat",
|
||
pendingUsersTitle: "Odottavat käyttäjät",
|
||
pendingListingsTitle: "Odottavat kohteet",
|
||
noPendingUsers: "Ei odottavia käyttäjiä.",
|
||
noPendingListings: "Ei odottavia kohteita.",
|
||
statusLabel: "tila",
|
||
slugsLabel: "Osoitepolut",
|
||
verifiedLabel: "vahvistettu",
|
||
approve: "Hyväksy",
|
||
approveAdmin: "Hyväksy + admin",
|
||
reject: "Hylkää",
|
||
remove: "Poista käytöstä",
|
||
publish: "Julkaise",
|
||
approvalsMessage: "Kohde päivitetty",
|
||
userUpdated: "Käyttäjä päivitetty",
|
||
adminRequired: "Ylläpitäjän oikeudet vaaditaan",
|
||
adminUsersTitle: "Ylläpito: käyttäjät",
|
||
adminUsersLead: "Hallinnoi rooleja ja hyväksyntöjä.",
|
||
monitorTitle: "Ylläpito: valvonta",
|
||
monitorLead:
|
||
"Hetzner-solmujen, Kubernetes-podien ja PostgreSQL:n tilanne yhdessä näkymässä.",
|
||
monitorRefresh: "Päivitä nyt",
|
||
monitorLastUpdated: "Päivitetty",
|
||
monitorNoData: "Ei dataa vielä.",
|
||
monitorHetznerTitle: "Hetzner-solmut",
|
||
monitorHetznerMissingToken:
|
||
"Aseta HCLOUD_TOKEN, jotta Hetzner-solmut saadaan näkyviin.",
|
||
monitorHetznerEmpty: "Hetzner ei palauttanut palvelimia.",
|
||
monitorK8sTitle: "Kubernetes",
|
||
monitorNodesTitle: "Solmut",
|
||
monitorPodsTitle: "Podit",
|
||
monitorNoPods: "Ei podeja lomavuokraus-namespacessa.",
|
||
monitorRestarts: "Restartit",
|
||
monitorAge: "Ikä",
|
||
monitorDbTitle: "PostgreSQL",
|
||
monitorServerTime: "Palvelimen aika",
|
||
monitorDbSize: "Tietokannan koko",
|
||
monitorDbRecovery: "Recovery-tila",
|
||
monitorConnections: "Yhteydet",
|
||
monitorHealthy: "Kunnossa",
|
||
monitorAttention: "Huomio",
|
||
monitorLoadFailed: "Valvontadatan haku epäonnistui.",
|
||
monitorCreated: "Luotu",
|
||
monitorLastReady: "Viimeisin Ready-muutos",
|
||
adminSettingsTitle: "Ylläpito: asetukset",
|
||
adminSettingsLead: "Sivuston laajuiset ominaisuusasetukset ja käytännöt.",
|
||
tableEmail: "Sähköposti",
|
||
tableRole: "Rooli",
|
||
tableStatus: "Tila",
|
||
tableVerified: "Vahvistettu",
|
||
tableApproved: "Hyväksytty",
|
||
myListingsTitle: "Omat kohteet",
|
||
createNewListing: "Luo uusi kohde",
|
||
noListings: "Ei kohteita vielä.",
|
||
createOne: "Luo kohde",
|
||
loading: "Ladataan…",
|
||
edit: "Muokkaa",
|
||
view: "Näytä",
|
||
removing: "Poistetaan…",
|
||
removed: "Kohde poistettu näkyvistä",
|
||
removeConfirm: "Poista kohde? Se piilotetaan muilta.",
|
||
myProfileTitle: "Oma profiili",
|
||
notLoggedIn: "Et ole kirjautunut",
|
||
profileEmail: "Sähköposti",
|
||
profileName: "Nimi",
|
||
profilePhone: "Puhelin",
|
||
profileRole: "Rooli",
|
||
profileStatus: "Tila",
|
||
profileEmailVerified: "Sähköposti vahvistettu",
|
||
profileApproved: "Hyväksytty",
|
||
profileUpdated: "Profiili päivitetty",
|
||
billingSettingsTitle: "Laskutusavustin",
|
||
billingSettingsLead:
|
||
"Ota n8n-laskutus käyttöön halutessasi ja määritä oletus- ja kohdekohtaiset tiedot.",
|
||
billingEnableLabel: "Ota laskutusavustin käyttöön kohteilleni",
|
||
billingAccountNameLabel: "Tilinomistajan nimi",
|
||
billingAccountPlaceholder: "Käytä profiilin oletusta",
|
||
billingIbanLabel: "IBAN-maksuissa",
|
||
billingIbanPlaceholder: "Käytä profiilin oletusta",
|
||
billingIncludeVat: "Lisää laskulle ALV-rivi",
|
||
billingListingsTitle: "Kohdekohtaiset asetukset",
|
||
billingListingsLead:
|
||
"Ylikirjoita tiedot kohteittain; tyhjä kenttä käyttää profiilin arvoa.",
|
||
billingNoListings: "Ei kohteita vielä.",
|
||
billingVatChoice: "ALV-rivin valinta",
|
||
billingVatInherit: "Käytä profiilin asetusta",
|
||
billingVatYes: "Lisää ALV-rivi",
|
||
billingVatNo: "Älä lisää ALV-riviä",
|
||
billingDisabledHint:
|
||
"Ota laskutusavustin käyttöön lisätäksesi laskutustiedot ja ALV-valinnat.",
|
||
billingLoadFailed: "Laskutusasetusten lataus epäonnistui.",
|
||
billingSaveFailed: "Laskutusasetusten tallennus epäonnistui.",
|
||
billingSaved: "Laskutusasetukset tallennettu.",
|
||
settingsSaved: "Asetukset tallennettu.",
|
||
emailLocked: "Sähköpostia ei voi vaihtaa",
|
||
save: "Tallenna",
|
||
saving: "Tallennetaan…",
|
||
yes: "kyllä",
|
||
no: "ei",
|
||
verifyTitle: "Sähköpostin vahvistus",
|
||
verifyOk: "Sähköposti vahvistettu. Ylläpitäjä hyväksyy tilin pian.",
|
||
verifyFail: "Vahvistus epäonnistui tai token on virheellinen.",
|
||
listingLocation: "Sijainti",
|
||
listingAddress: "Osoite",
|
||
listingCapacity: "Tilat",
|
||
listingPrices: "Hinta (alkaen)",
|
||
listingAmenities: "Varustelu",
|
||
listingNoAmenities: "Varustelua ei ole listattu.",
|
||
listingContact: "Yhteystiedot",
|
||
contactLoginToView:
|
||
"Kirjaudu sisään nähdäksesi tämän ilmoituksen yhteystiedot.",
|
||
listingMoreInfo: "Lisätietoja",
|
||
availabilityTitle: "Saatavuuskalenteri",
|
||
availabilityLegendBooked: "Varattu",
|
||
availabilityMissing: "Kalenteria ei ole vielä yhdistetty.",
|
||
localeLabel: "Kieli",
|
||
homeCrumb: "Etusivu",
|
||
createListingTitle: "Luo kohde",
|
||
loginToCreate: "Kirjaudu ensin luodaksesi kohteen.",
|
||
slugLabel: "Osoitepolku",
|
||
slugHelp:
|
||
"Keksi lyhyt ja helppo linkki; käytä pieniä kirjaimia ja väliviivoja (esim. saimaa-mokki).",
|
||
slugPreview: "Ilmoituksen linkki on: {url}",
|
||
slugChecking: "Tarkistetaan saatavuutta…",
|
||
slugAvailable: "Slug on vapaa",
|
||
slugTaken: "Slug on jo käytössä",
|
||
slugCheckError: "Slugia ei voitu tarkistaa nyt",
|
||
localeInput: "Kieli",
|
||
titleLabel: "Otsikko",
|
||
descriptionLabel: "Kuvaus",
|
||
teaserLabel: "Tiivistelmä",
|
||
teaserHelp: "Lyhyt ingressi kortteihin",
|
||
countryLabel: "Maa",
|
||
regionLabel: "Maakunta/alue",
|
||
cityLabel: "Kunta/kaupunki",
|
||
contactNameLabel: "Yhteyshenkilö",
|
||
contactEmailLabel: "Yhteyssähköposti",
|
||
streetAddressLabel: "Katuosoite",
|
||
addressNoteLabel: "Saapumisohje (valinnainen)",
|
||
addressNotePlaceholder: "Reittiohje, pysäköinti tai muut saapumisohjeet",
|
||
latitudeLabel: "Leveysaste (valinnainen)",
|
||
longitudeLabel: "Pituusaste (valinnainen)",
|
||
maxGuestsLabel: "Vieraita enintään",
|
||
bedroomsLabel: "Makuuhuoneita",
|
||
bedsLabel: "Vuoteita",
|
||
bathroomsLabel: "Kylpyhuoneita",
|
||
priceWeekdayLabel: "Hinta alkaen (arki, € / yö)",
|
||
priceWeekendLabel: "Hinta alkaen (viikonloppu, € / yö)",
|
||
priceHintHelp: "Hinnat ovat suuntaa-antavia (alkaen), eivät sitovia.",
|
||
priceStartingFromShort: "Alkaen {price}€ / yö",
|
||
priceWeekdayShort: "{price}€ arki",
|
||
priceWeekendShort: "{price}€ viikonloppu",
|
||
priceNotSet: "Ei ilmoitettu",
|
||
calendarUrlsLabel: "Saatavuuskalenterit (iCal-osoitteet, yksi per rivi)",
|
||
calendarUrlsHelp:
|
||
"Liitä iCal-linkit muilta alustoilta. Yhdistämme ne saatavuuden näyttämiseen.",
|
||
imagesLabel: "Kuvat",
|
||
imagesHelp: "Lataa enintään {count} kuvaa (max {sizeMb} Mt / kuva).",
|
||
imagesTooMany: "Liikaa kuvia (enintään {count}).",
|
||
imagesTooLarge: "Kuva on liian suuri (max {sizeMb} Mt).",
|
||
imagesReadFailed: "Kuvien luku epäonnistui.",
|
||
imagesRequired: "Lataa vähintään yksi kuva.",
|
||
coverImageLabel: "Kansikuvan järjestys",
|
||
coverImageHelp:
|
||
"Monettako ladatuista kuvista käytetään kansikuvana (oletus 1)",
|
||
coverChoice: "Kansipaikka {index}",
|
||
makeCover: "Aseta kansikuvaksi",
|
||
existingImageLabel: "Nykyinen kuva",
|
||
imageRemoveLastError: "Lisää toinen kuva ennen viimeisen poistamista.",
|
||
imageRemoveFailed: "Kuvan poistaminen epäonnistui.",
|
||
submitListing: "Luo kohde",
|
||
submittingListing: "Lähetetään…",
|
||
createListingSuccess: "Kohde luotu id:llä {id} (tila: {status})",
|
||
approvalsCountLabel: "Tarkastettavat",
|
||
approvalsPending: "{count} odottaa",
|
||
amenitySauna: "Sauna",
|
||
amenityFireplace: "Takka",
|
||
amenityWifi: "Wi-Fi",
|
||
amenityPets: "Lemmikit sallittu",
|
||
amenityLake: "Järven rannalla",
|
||
amenityAirConditioning: "Ilmastointi",
|
||
amenityKitchen: "Keittiö",
|
||
amenityDishwasher: "Astianpesukone",
|
||
amenityWashingMachine: "Pyykinpesukone",
|
||
amenityBarbecue: "Grilli",
|
||
amenityMicrowave: "Mikroaaltouuni",
|
||
amenityFreeParking: "Maksuton pysäköinti",
|
||
amenityEvAvailable: "Sähköauton lataus lähellä",
|
||
amenityEvNearby: "Sähköauton lataus lähellä",
|
||
amenityEvOnSite: "Sähköauton lataus kohteessa",
|
||
amenityWheelchairAccessible: "Esteetön / pyörätuolilla",
|
||
amenitySkiPass: "Hissilippu sisältyy",
|
||
evChargingLabel: "Sähköauton lataus",
|
||
evChargingYes: "Latausmahdollisuus",
|
||
evChargingNo: "Ei latausta",
|
||
evChargingAny: "Kaikki",
|
||
evChargingExplain: "Onko kohteessa sähköauton latausmahdollisuus?",
|
||
capacityGuests: "{count} vierasta",
|
||
capacityBedrooms: "{count} makuuhuonetta",
|
||
capacityBeds: "{count} vuodetta",
|
||
capacityBathrooms: "{count} kylpyhuonetta",
|
||
capacityUnknown: "Kapasiteettia ei ole asetettu",
|
||
browseListingsTitle: "Selaa kohteita",
|
||
browseListingsLead:
|
||
"Hae julkaistuja kohteita, rajaa sijainnilla ja tutki kartalla.",
|
||
sampleBadge: "Mallikohde",
|
||
privacyTitle: "Tietosuoja ja evästeet",
|
||
privacyUpdated: "Päivitetty: {date}",
|
||
privacyCollectTitle: "Mitä tietoja keräämme",
|
||
privacyCollectAccounts:
|
||
"Käyttäjätiedot: sähköposti, salasanatiiviste, nimi, puhelin (valinnainen), rooli/tila.",
|
||
privacyCollectListings:
|
||
"Kohdetiedot: sijainti, yhteystiedot, varustelu, kuvat, kieliversiot.",
|
||
privacyCollectLogs: "Lokit: rajattu pyyntömeta diagnostiikkaan.",
|
||
privacyUseTitle: "Miten käytämme tietoja",
|
||
privacyUseAuth: "Kirjautumiseen ja sessioiden hallintaan.",
|
||
privacyUseListings: "Kohteiden julkaisuun ja moderointiin.",
|
||
privacyUseMail:
|
||
"Transaktiosähköposteihin (vahvistus, salasanan palautus).",
|
||
privacyUseLegal: "Lakivelvoitteiden täyttämiseen ja palvelun suojaamiseen.",
|
||
privacyStoreTitle: "Säilytys",
|
||
privacyStoreDb: "Tiedot tallennetaan EU:ssa olevaan Postgres-tietokantaan.",
|
||
privacyStoreBackups:
|
||
"Varmuuskopioita säilytetään palautusta varten; poistetut tiedot voivat esiintyä varmuuskopioissa rajatun ajan.",
|
||
privacyCookiesTitle: "Evästeet",
|
||
privacyCookiesSession:
|
||
"Käytämme vain välttämättömiä evästeitä: kirjautumissessio.",
|
||
privacyCookiesNoTracking:
|
||
"Ei analytiikka-, mainos- tai seurantateknologioita.",
|
||
privacySharingTitle: "Tietojen jakaminen",
|
||
privacySharingAds: "Emme myy tai jaa henkilötietoja mainostajille.",
|
||
privacySharingOps:
|
||
"Palveluntarjoajille vain palvelun pyörittämiseen (sähköposti, hosting).",
|
||
privacyRightsTitle: "Oikeutesi",
|
||
privacyRightsAccess:
|
||
"Voit pyytää tietojen nähtävyyttä, oikaisua tai poistoa.",
|
||
privacyRightsConsent:
|
||
"Voit perua suostumuksen ei-välttämättömästä käsittelystä (tällä hetkellä vain välttämättömät).",
|
||
privacyRightsContact:
|
||
"Ota yhteyttä, jos sinulla on kysyttävää tietosuojasta.",
|
||
searchLabel: "Haku",
|
||
searchPlaceholder: "Hae nimellä, kuvauksella tai paikkakunnalla",
|
||
cityFilter: "Kaupunki/kunta",
|
||
regionFilter: "Maakunta/alue",
|
||
searchButton: "Hae",
|
||
searchAmenities: "Varustelu",
|
||
searchAvailability: "Saatavuus",
|
||
startDate: "Alkupäivä",
|
||
endDate: "Loppupäivä",
|
||
availabilityOnlyWithCalendar:
|
||
"Päiväsuodatus näyttää vain kohteet, joissa on kalenteri yhdistettynä.",
|
||
availableForDates: "Vapaa valituille päiville",
|
||
notAvailableForDates: "Ei vapaana valituille päiville",
|
||
calendarConnected: "Kalenteri yhdistetty",
|
||
clearFilters: "Tyhjennä suodattimet",
|
||
addressSearchLabel: "Etsi kohteita osoitteen läheltä",
|
||
addressSearchPlaceholder: "Katu, kaupunki tai paikka",
|
||
locateAddress: "Paikanna kartalle",
|
||
addressRadiusLabel: "{km} km säteellä",
|
||
listingsFound: "{count} kohdetta",
|
||
openListing: "Avaa kohde",
|
||
mapNoResults: "Suodattimilla ei löytynyt kohteita.",
|
||
addressNotFound: "Osoitetta ei löydy",
|
||
addressLookupFailed: "Paikannus epäonnistui",
|
||
loadingMap: "Ladataan karttaa…",
|
||
latestListingsTitle: "Viimeisimmät kohteet",
|
||
latestListingsLead: "Tuoreimmat kohteet heti julkaisun jälkeen.",
|
||
},
|
||
} as const;
|
||
|
||
const svMessages: Record<keyof typeof baseMessages.en, string> = {
|
||
...baseMessages.en,
|
||
navProfile: "Profil",
|
||
navMyListings: "Mina annonser",
|
||
navNewListing: "Ny annons",
|
||
navAdmin: "Admin",
|
||
navApprovals: "Godkännanden",
|
||
navUsers: "Användare",
|
||
navMonitoring: "Övervakning",
|
||
navSettings: "Inställningar",
|
||
navLogout: "Logga ut",
|
||
navLogin: "Logga in",
|
||
navSignup: "Registrera dig",
|
||
navBrowse: "Bläddra bland annonser",
|
||
navLanguage: "Språk",
|
||
monitorTitle: "Admin: övervakning",
|
||
monitorLead: "Livesstatus för Hetzner-noder, Kubernetes-pods och PostgreSQL.",
|
||
monitorRefresh: "Uppdatera nu",
|
||
monitorLastUpdated: "Senast uppdaterad",
|
||
monitorNoData: "Ingen data ännu.",
|
||
monitorHetznerTitle: "Hetzner-noder",
|
||
monitorHetznerMissingToken: "Sätt HCLOUD_TOKEN för att visa Hetzner-noder.",
|
||
monitorHetznerEmpty: "Inga Hetzner-servrar hittades.",
|
||
monitorK8sTitle: "Kubernetes",
|
||
monitorNodesTitle: "Noder",
|
||
monitorPodsTitle: "Pods",
|
||
monitorNoPods: "Inga pods i lomavuokraus-namespaces.",
|
||
monitorRestarts: "Omstarter",
|
||
monitorAge: "Ålder",
|
||
monitorDbTitle: "PostgreSQL",
|
||
monitorServerTime: "Servertid",
|
||
monitorDbSize: "Databasstorlek",
|
||
monitorDbRecovery: "Recovery-läge",
|
||
monitorConnections: "Anslutningar",
|
||
monitorHealthy: "OK",
|
||
monitorAttention: "Behöver åtgärd",
|
||
monitorLoadFailed: "Kunde inte hämta övervakningsdata.",
|
||
monitorCreated: "Skapad",
|
||
monitorLastReady: "Senaste Ready-ändring",
|
||
adminSettingsTitle: "Admin: inställningar",
|
||
adminSettingsLead: "Webbplatsövergripande funktioner och policyer.",
|
||
slugHelp:
|
||
"Hitta på en kort och enkel länk; använd små bokstäver och bindestreck (t.ex. sjo-stuga).",
|
||
slugPreview: "Länk till annonsen: {url}",
|
||
heroTitle: "Hitta ditt nästa finska getaway",
|
||
heroBody:
|
||
"Upptäck stugor, lägenheter och villor direkt från ägarna. Annonser verifieras innan publicering och du kontaktar värdarna direkt — enkelt och transparent.",
|
||
ctaBrowse: "Bläddra bland annonser",
|
||
createListingTitle: "Skapa annons",
|
||
edit: "Redigera",
|
||
languageTabsLabel: "Annonsens språk",
|
||
languageTabsHint: "Lägg till översättningar för varje språk",
|
||
localeSectionTitle: "Annonsinnehåll",
|
||
localeReady: "Klar",
|
||
localePartial: "Pågår",
|
||
localeMissing: "Saknas",
|
||
aiHelperTitle: "AI-översättningshjälp",
|
||
aiHelperLead: "Låt AI fylla i de andra språken åt dig.",
|
||
aiOptionalHint: "Valfritt: AI-hjälpen kan fylla i andra språk.",
|
||
aiAutoExplain:
|
||
"Fyll texterna på ett språk och klicka på knappen så fyller AI i resten.",
|
||
aiAutoTranslate: "Översätt saknade språk",
|
||
aiAutoTranslating: "Översätter…",
|
||
aiAutoSuccess: "Översättningar uppdaterades med AI.",
|
||
aiAutoError:
|
||
"AI-översättning misslyckades. Försök igen eller använd manuellt läge nedan.",
|
||
aiHelperNote: "Använder OpenAI för att översätta saknade texter.",
|
||
aiManualLead:
|
||
"Om autokorrespondens misslyckas, kopiera prompten till din AI och klistra in JSON-svaret nedan.",
|
||
aiPromptLabel: "Prompt till AI",
|
||
aiCopyPrompt: "Kopiera prompt",
|
||
aiPromptCopied: "Kopierad",
|
||
aiCopyError: "Kopiering misslyckades",
|
||
aiResponseLabel: "Klistra in AI-svar (JSON)",
|
||
aiApply: "Använd AI-svar",
|
||
aiApplyError:
|
||
"Kunde inte läsa AI-svaret. Se till att det är giltig JSON med locales-nyckel.",
|
||
aiApplySuccess: "Översättningar uppdaterades från AI-svaret.",
|
||
settingContactVisibilityTitle: "Synlighet för kontaktuppgifter",
|
||
settingContactVisibilityHelp:
|
||
"Dölj värdens kontaktuppgifter för anonyma besökare så att bara inloggade ser dem.",
|
||
settingRequireLoginForContact: "Kräv inloggning för att se kontaktuppgifter",
|
||
billingSettingsTitle: "Faktureringsassistent",
|
||
billingSettingsLead:
|
||
"Aktivera n8n-fakturor om du vill och ange standard- samt objektspecifika uppgifter.",
|
||
billingEnableLabel: "Aktivera faktureringsassistent för mina annonser",
|
||
billingAccountNameLabel: "Kontoinnehavarens namn",
|
||
billingAccountPlaceholder: "Använd profilens standard",
|
||
billingIbanLabel: "IBAN för utbetalningar",
|
||
billingIbanPlaceholder: "Använd profilens standard",
|
||
billingIncludeVat: "Ta med momsrad på fakturor",
|
||
billingListingsTitle: "Objektvisa inställningar",
|
||
billingListingsLead:
|
||
"Åsidosätt uppgifter per annons; tomma fält använder profilens värden.",
|
||
billingNoListings: "Inga annonser ännu.",
|
||
billingVatChoice: "Momsrad",
|
||
billingVatInherit: "Använd profilinställning",
|
||
billingVatYes: "Lägg till momsrad",
|
||
billingVatNo: "Lägg inte till momsrad",
|
||
billingDisabledHint:
|
||
"Aktivera assistenten för att hantera fakturauppgifter och momsval.",
|
||
billingLoadFailed: "Kunde inte ladda faktureringsinställningar.",
|
||
billingSaveFailed: "Kunde inte spara faktureringsinställningar.",
|
||
billingSaved: "Faktureringsinställningar sparade.",
|
||
settingsSaved: "Inställningar sparade.",
|
||
translationMissing: "Lägg till minst ett språk med titel och beskrivning.",
|
||
saveDraft: "Spara utkast",
|
||
missingFields: "Saknas: {fields}",
|
||
slugChecking: "Kontrollerar tillgänglighet…",
|
||
slugAvailable: "Sluggen är ledig",
|
||
slugTaken: "Sluggen används redan",
|
||
slugCheckError: "Kunde inte kontrollera sluggen nu",
|
||
teaserHelp: "Kort ingress som syns i korten",
|
||
priceWeekdayLabel: "Pris från (vardag, € / natt)",
|
||
priceWeekendLabel: "Pris från (helg, € / natt)",
|
||
priceHintHelp:
|
||
"Priserna är endast vägledande (från), inte ett bindande erbjudande.",
|
||
priceStartingFromShort: "Från {price}€ / natt",
|
||
priceWeekdayShort: "{price}€ vardag",
|
||
priceWeekendShort: "{price}€ helg",
|
||
makeCover: "Gör till omslag",
|
||
existingImageLabel: "Befintlig bild",
|
||
imageRemoveLastError: "Lägg till en annan bild innan du tar bort den sista.",
|
||
imageRemoveFailed: "Det gick inte att ta bort bilden.",
|
||
priceNotSet: "Ej angivet",
|
||
listingPrices: "Pris (från)",
|
||
listingContact: "Kontakt",
|
||
contactLoginToView: "Logga in för att se kontaktuppgifter för denna annons.",
|
||
capacityUnknown: "Kapacitet ej angiven",
|
||
amenityEvAvailable: "EV-laddning i närheten",
|
||
amenityEvNearby: "EV-laddning i närheten",
|
||
amenityEvOnSite: "EV-laddning på plats",
|
||
amenityWheelchairAccessible: "Rullstolsanpassat",
|
||
amenitySkiPass: "Liftkort ingår",
|
||
amenityMicrowave: "Mikrovågsugn",
|
||
amenityFreeParking: "Gratis parkering",
|
||
evChargingLabel: "EV-laddning",
|
||
evChargingYes: "EV-laddning finns",
|
||
evChargingNo: "Ingen EV-laddning",
|
||
evChargingAny: "Alla",
|
||
evChargingExplain: "Finns det EV-laddning på plats?",
|
||
footerCookieNotice:
|
||
"Vi använder endast nödvändiga cookies för inloggning och säkerhet. Genom att använda sajten godkänner du cookies; om du inte gör det, använd inte webbplatsen.",
|
||
};
|
||
|
||
export const messages = { ...baseMessages, sv: svMessages } as const;
|
||
export type MessageKey = keyof typeof baseMessages.en;
|
||
|
||
function normalizeLocale(input?: string | null): Locale | null {
|
||
if (!input) return null;
|
||
const lower = input.toLowerCase();
|
||
if (lower.startsWith("fi")) return "fi";
|
||
if (lower.startsWith("en")) return "en";
|
||
if (lower.startsWith("sv")) return "sv";
|
||
return null;
|
||
}
|
||
|
||
export function resolveLocale(opts: {
|
||
cookieLocale?: string | null;
|
||
acceptLanguage?: string | null;
|
||
}): Locale {
|
||
const fromCookie = normalizeLocale(opts.cookieLocale);
|
||
if (fromCookie) return fromCookie;
|
||
const fromHeader = normalizeLocale(opts.acceptLanguage);
|
||
if (fromHeader) return fromHeader;
|
||
return "en";
|
||
}
|
||
|
||
export function t(
|
||
locale: Locale,
|
||
key: MessageKey,
|
||
vars?: Record<string, string | number>,
|
||
) {
|
||
const table = messages[locale] ?? messages.en;
|
||
const template = String(table[key] ?? messages.en[key]);
|
||
if (!vars) return template;
|
||
return Object.entries(vars).reduce<string>(
|
||
(acc, [k, v]) => acc.replace(`{${k}}`, String(v)),
|
||
template,
|
||
);
|
||
}
|