diff --git a/PROGRESS.md b/PROGRESS.md
index f842433..be8a5ce 100644
--- a/PROGRESS.md
+++ b/PROGRESS.md
@@ -88,3 +88,4 @@
- Forgejo deployment scaffolding added: Docker Compose + runner config guidance and Apache vhost for git.halla-aho.net, plus CI workflow placeholder under `.forgejo/workflows/`.
- Amenities: added separate EV charging flags (on-site vs nearby) plus wheelchair accessibility, including browse filters and admin approvals view badges.
- Navbar: combined admin actions (approvals/users/monitoring) under a single “Admin” dropdown menu.
+- Pricing copy: treat listing prices as indicative “starting from” values and show starting-from line on browse cards.
diff --git a/app/listings/[slug]/page.tsx b/app/listings/[slug]/page.tsx
index 4b9032b..f418f82 100644
--- a/app/listings/[slug]/page.tsx
+++ b/app/listings/[slug]/page.tsx
@@ -114,11 +114,17 @@ export default async function ListingPage({ params }: ListingPageProps) {
const capacityLine = capacityParts.length ? capacityParts.join(' · ') : t('capacityUnknown');
const contactLine = `${listing.contactName} · ${listing.contactEmail}${listing.contactPhone ? ` · ${listing.contactPhone}` : ''}`;
const coverImage = listing.images.find((img) => img.isCover) ?? listing.images[0] ?? null;
+ const priceCandidates = [listing.priceWeekdayEuros, listing.priceWeekendEuros].filter((p): p is number => typeof p === 'number');
+ const startingFromEuros = priceCandidates.length ? Math.min(...priceCandidates) : null;
const priceLine =
listing.priceWeekdayEuros || listing.priceWeekendEuros
- ? [listing.priceWeekdayEuros ? t('priceWeekdayShort', { price: listing.priceWeekdayEuros }) : null, listing.priceWeekendEuros ? t('priceWeekendShort', { price: listing.priceWeekendEuros }) : null]
- .filter(Boolean)
- .join(' · ')
+ ? `${startingFromEuros !== null ? t('priceStartingFromShort', { price: startingFromEuros }) : ''}${
+ listing.priceWeekdayEuros || listing.priceWeekendEuros
+ ? ` (${[listing.priceWeekdayEuros ? t('priceWeekdayShort', { price: listing.priceWeekdayEuros }) : null, listing.priceWeekendEuros ? t('priceWeekendShort', { price: listing.priceWeekendEuros }) : null]
+ .filter(Boolean)
+ .join(' · ')})`
+ : ''
+ }`
: t('priceNotSet');
const isDraftOrPending = listing.status !== ListingStatus.PUBLISHED;
const isOwnerView = viewerId && listing.ownerId === viewerId;
diff --git a/app/listings/page.tsx b/app/listings/page.tsx
index e82908e..75a6c39 100644
--- a/app/listings/page.tsx
+++ b/app/listings/page.tsx
@@ -469,13 +469,18 @@ export default function ListingsIndexPage() {
) : null}
{l.teaser ?? ''}
+ {l.priceWeekdayEuros || l.priceWeekendEuros ? (
+
+ {t('priceStartingFromShort', {
+ price: Math.min(...([l.priceWeekdayEuros, l.priceWeekendEuros].filter((p): p is number => typeof p === 'number'))),
+ })}
+
+ ) : null}
{l.streetAddress ? `${l.streetAddress}, ` : ''}
{l.city}, {l.region}
- {l.priceWeekdayEuros ? {t('priceWeekdayShort', { price: l.priceWeekdayEuros })} : null}
- {l.priceWeekendEuros ? {t('priceWeekendShort', { price: l.priceWeekendEuros })} : null}
{t('capacityGuests', { count: l.maxGuests })}
{t('capacityBedrooms', { count: l.bedrooms })}
{l.hasCalendar ? {t('calendarConnected')} : null}
diff --git a/lib/i18n.ts b/lib/i18n.ts
index 6f4fa66..d6450c5 100644
--- a/lib/i18n.ts
+++ b/lib/i18n.ts
@@ -153,7 +153,7 @@ const baseMessages = {
listingLocation: 'Location',
listingAddress: 'Address',
listingCapacity: 'Capacity',
- listingPrices: 'Pricing',
+ listingPrices: 'Price (starting from)',
listingAmenities: 'Amenities',
listingNoAmenities: 'No amenities listed yet.',
listingContact: 'Contact',
@@ -218,9 +218,10 @@ const baseMessages = {
bedroomsLabel: 'Bedrooms',
bedsLabel: 'Beds',
bathroomsLabel: 'Bathrooms',
- priceWeekdayLabel: 'Weeknight price (€ / night)',
- priceWeekendLabel: 'Weekend price (€ / night)',
- priceHintHelp: 'Set separate weeknight and weekend prices in euros (optional, not a binding offer).',
+ 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',
@@ -500,7 +501,7 @@ const baseMessages = {
listingLocation: 'Sijainti',
listingAddress: 'Osoite',
listingCapacity: 'Tilat',
- listingPrices: 'Hinta',
+ listingPrices: 'Hinta (alkaen)',
listingAmenities: 'Varustelu',
listingNoAmenities: 'Varustelua ei ole listattu.',
listingContact: 'Yhteystiedot',
@@ -538,9 +539,10 @@ const baseMessages = {
bedroomsLabel: 'Makuuhuoneita',
bedsLabel: 'Vuoteita',
bathroomsLabel: 'Kylpyhuoneita',
- priceWeekdayLabel: 'Arkiyön hinta (€ / yö)',
- priceWeekendLabel: 'Viikonlopun hinta (€ / yö)',
- priceHintHelp: 'Aseta erilliset hinnat arki- ja viikonloppuyöille euroissa (valinnainen, ei sitova).',
+ 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',
@@ -719,13 +721,14 @@ const svMessages: Record = {
slugTaken: 'Sluggen används redan',
slugCheckError: 'Kunde inte kontrollera sluggen nu',
teaserHelp: 'Kort ingress som syns i korten',
- priceWeekdayLabel: 'Vardagspris (€ / natt)',
- priceWeekendLabel: 'Helgpris (€ / natt)',
- priceHintHelp: 'Ange separata priser för vardag och helg i euro per natt (frivilligt).',
+ 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',
priceNotSet: 'Ej angivet',
- listingPrices: 'Priser',
+ listingPrices: 'Pris (från)',
capacityUnknown: 'Kapacitet ej angiven',
amenityEvAvailable: 'EV-laddning i närheten',
amenityEvNearby: 'EV-laddning i närheten',