fix(i18n): prepare for Lokalise
This commit is contained in:
@@ -36,10 +36,10 @@ export default function Confirmation({
|
||||
<Alert
|
||||
type={AlertTypeEnum.Info}
|
||||
heading={intl.formatMessage({
|
||||
id: "booking.confirmation.membershipInfo.heading",
|
||||
id: "Failed to verify membership",
|
||||
})}
|
||||
text={intl.formatMessage({
|
||||
id: "booking.confirmation.membershipInfo.text",
|
||||
id: "Your booking(s) is confirmed but we could not verify your membership. If you have booked with a member discount, you'll either need to present your existing membership number upon check-in, become a member or pay the price difference at the hotel. Signing up is preferably done online before the stay.",
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -24,7 +24,9 @@ export default function Header({
|
||||
const intl = useIntl()
|
||||
|
||||
const text = intl.formatMessage<React.ReactNode>(
|
||||
{ id: "booking.confirmation.text" },
|
||||
{
|
||||
id: "Thank you for booking with us! We look forward to welcoming you and hope you have a pleasant stay. If you have any questions or need to make changes to your reservation, please <emailLink>contact us.</emailLink>",
|
||||
},
|
||||
{
|
||||
emailLink: (str) => (
|
||||
<Link color="burgundy" href="#" textDecoration="underline">
|
||||
@@ -57,7 +59,7 @@ export default function Header({
|
||||
<header className={styles.header}>
|
||||
<hgroup className={styles.hgroup}>
|
||||
<Title as="h2" color="red" textTransform="uppercase" type="h2">
|
||||
{intl.formatMessage({ id: "booking.confirmation.title" })}
|
||||
{intl.formatMessage({ id: "Booking confirmation" })}
|
||||
</Title>
|
||||
<Title as="h2" color="burgundy" textTransform="uppercase" type="h1">
|
||||
{hotel.name}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Link from "@/components/TempDesignSystem/Link"
|
||||
@@ -23,8 +24,14 @@ export default function HotelDetails({
|
||||
<div className={styles.hotel}>
|
||||
<Body color="uiTextHighContrast">{hotel.name}</Body>
|
||||
<Body color="uiTextHighContrast">
|
||||
{hotel.address.streetAddress}, {hotel.address.zipCode}{" "}
|
||||
{hotel.address.city}
|
||||
{intl.formatMessage(
|
||||
{ id: "{streetAddress}, {zipCode} {city}" },
|
||||
{
|
||||
streetAddress: hotel.address.streetAddress,
|
||||
zipCode: hotel.address.zipCode,
|
||||
city: hotel.address.city,
|
||||
}
|
||||
)}
|
||||
</Body>
|
||||
<Body asChild color="uiTextHighContrast">
|
||||
<Link
|
||||
@@ -64,7 +71,7 @@ export default function HotelDetails({
|
||||
<div className={styles.toast}>
|
||||
<Toast variant="info">
|
||||
<ul className={styles.list}>
|
||||
<li>N/A</li>
|
||||
<li>{intl.formatMessage({ id: "N/A" })}</li>
|
||||
</ul>
|
||||
</Toast>
|
||||
</div>
|
||||
|
||||
@@ -26,8 +26,16 @@ export default function PaymentDetails({
|
||||
</Subtitle>
|
||||
<div className={styles.payment}>
|
||||
<Body color="uiTextHighContrast">
|
||||
{formatPrice(intl, booking.totalPrice, booking.currencyCode)}{" "}
|
||||
{intl.formatMessage({ id: "has been paid" })}
|
||||
{intl.formatMessage(
|
||||
{ id: "{amount} has been paid" },
|
||||
{
|
||||
amount: formatPrice(
|
||||
intl,
|
||||
booking.totalPrice,
|
||||
booking.currencyCode
|
||||
),
|
||||
}
|
||||
)}
|
||||
</Body>
|
||||
<Body color="uiTextHighContrast">
|
||||
{dt(booking.createDateTime)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client"
|
||||
|
||||
import { notFound } from "next/navigation"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
@@ -47,7 +48,7 @@ export default function Receipt({
|
||||
{booking.rateDefinition.isMemberRate ? (
|
||||
<div className={styles.memberPrice}>
|
||||
<Body color="uiTextPlaceholder">
|
||||
<s>N/A</s>
|
||||
<s>{intl.formatMessage({ id: "N/A" })}</s>
|
||||
</Body>
|
||||
<Body color="red">
|
||||
{formatPrice(intl, booking.roomPrice, booking.currencyCode)}
|
||||
@@ -60,7 +61,7 @@ export default function Receipt({
|
||||
)}
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "booking.adults" },
|
||||
{ id: "{totalAdults, plural, one {# adult} other {# adults}}" },
|
||||
{
|
||||
totalAdults: booking.adults,
|
||||
}
|
||||
@@ -156,7 +157,12 @@ export default function Receipt({
|
||||
<ChevronRightSmallIcon />
|
||||
</Button>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage({ id: "Approx." })} N/A EUR
|
||||
{intl.formatMessage(
|
||||
{ id: "Approx. {value}" },
|
||||
{
|
||||
value: "N/A EUR",
|
||||
}
|
||||
)}
|
||||
</Caption>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { dt } from "@/lib/dt"
|
||||
@@ -23,6 +24,7 @@ export default function Room({ booking, img, roomName }: RoomProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
|
||||
const guestName = `${booking.guest.firstName} ${booking.guest.lastName}`
|
||||
const fromDate = dt(booking.checkInDate).locale(lang)
|
||||
const toDate = dt(booking.checkOutDate).locale(lang)
|
||||
return (
|
||||
@@ -33,7 +35,12 @@ export default function Room({ booking, img, roomName }: RoomProps) {
|
||||
{intl.formatMessage({ id: "Room" })} 1
|
||||
</Subtitle> */}
|
||||
<Subtitle color="uiTextHighContrast" type="two">
|
||||
{`${intl.formatMessage({ id: "Reservation number" })} ${booking.confirmationNumber}`}
|
||||
{intl.formatMessage(
|
||||
{ id: "Reservation number {value}" },
|
||||
{
|
||||
value: booking.confirmationNumber,
|
||||
}
|
||||
)}
|
||||
</Subtitle>
|
||||
</div>
|
||||
<div className={styles.benefits}>
|
||||
@@ -81,7 +88,13 @@ export default function Room({ booking, img, roomName }: RoomProps) {
|
||||
{intl.formatMessage({ id: "Check-in" })}
|
||||
</Body>
|
||||
<Body color="uiTextHighContrast">
|
||||
{`${fromDate.format("ddd, D MMM")} ${intl.formatMessage({ id: "from" })} ${fromDate.format("HH:mm")}`}
|
||||
{intl.formatMessage(
|
||||
{ id: "{checkInDate} from {checkInTime}" },
|
||||
{
|
||||
checkInDate: fromDate.format("ddd, D MMM"),
|
||||
checkInTime: fromDate.format("HH:mm"),
|
||||
}
|
||||
)}
|
||||
</Body>
|
||||
</li>
|
||||
<li className={styles.listItem}>
|
||||
@@ -89,14 +102,22 @@ export default function Room({ booking, img, roomName }: RoomProps) {
|
||||
{intl.formatMessage({ id: "Check-out" })}
|
||||
</Body>
|
||||
<Body color="uiTextHighContrast">
|
||||
{`${toDate.format("ddd, D MMM")} ${intl.formatMessage({ id: "from" })} ${toDate.format("HH:mm")}`}
|
||||
{intl.formatMessage(
|
||||
{ id: "{checkOutDate} from {checkOutTime}" },
|
||||
{
|
||||
checkOutDate: toDate.format("ddd, D MMM"),
|
||||
checkOutTime: toDate.format("HH:mm"),
|
||||
}
|
||||
)}
|
||||
</Body>
|
||||
</li>
|
||||
<li className={styles.listItem}>
|
||||
<Body color="uiTextPlaceholder">
|
||||
{intl.formatMessage({ id: "Breakfast" })}
|
||||
</Body>
|
||||
<Body color="uiTextHighContrast">N/A</Body>
|
||||
<Body color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "N/A" })}
|
||||
</Body>
|
||||
</li>
|
||||
<li className={styles.listItem}>
|
||||
<Body color="uiTextPlaceholder">
|
||||
@@ -110,19 +131,24 @@ export default function Room({ booking, img, roomName }: RoomProps) {
|
||||
<Body color="uiTextPlaceholder">
|
||||
{intl.formatMessage({ id: "Rebooking" })}
|
||||
</Body>
|
||||
<Body color="uiTextHighContrast">N/A</Body>
|
||||
<Body color="uiTextHighContrast">
|
||||
{intl.formatMessage({ id: "N/A" })}
|
||||
</Body>
|
||||
</li>
|
||||
</ul>
|
||||
<div className={styles.guest}>
|
||||
<Body color="uiTextPlaceholder">
|
||||
{intl.formatMessage({ id: "Main guest" })}
|
||||
</Body>
|
||||
<Body color="uiTextHighContrast">
|
||||
{`${booking.guest.firstName} ${booking.guest.lastName}`}
|
||||
</Body>
|
||||
<Body color="uiTextHighContrast">{guestName}</Body>
|
||||
{booking.guest.membershipNumber ? (
|
||||
<Body color="uiTextHighContrast">
|
||||
{`${intl.formatMessage({ id: "Friend no." })} ${booking.guest.membershipNumber}`}
|
||||
{intl.formatMessage(
|
||||
{ id: "Friend no. {value}" },
|
||||
{
|
||||
value: booking.guest.membershipNumber,
|
||||
}
|
||||
)}
|
||||
</Body>
|
||||
) : null}
|
||||
{booking.guest.phoneNumber ? (
|
||||
|
||||
@@ -16,6 +16,9 @@ export default function Contact({ hotel }: ContactProps) {
|
||||
const lang = useLang()
|
||||
const intl = useIntl()
|
||||
|
||||
const addressStr = `${hotel.address.streetAddress}, `
|
||||
const cityStr = hotel.address.city
|
||||
|
||||
return (
|
||||
<section className={styles.wrapper}>
|
||||
<address className={styles.address}>
|
||||
@@ -24,8 +27,11 @@ export default function Contact({ hotel }: ContactProps) {
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({ id: "Address" })}
|
||||
</Body>
|
||||
<Body>{`${hotel.address.streetAddress}, `}</Body>
|
||||
<Body>{hotel.address.city}</Body>
|
||||
<Body>
|
||||
{addressStr}
|
||||
<br />
|
||||
{cityStr}
|
||||
</Body>
|
||||
</li>
|
||||
<li>
|
||||
<Body textTransform="bold">
|
||||
@@ -34,7 +40,9 @@ export default function Contact({ hotel }: ContactProps) {
|
||||
<Link
|
||||
href={`https://www.google.com/maps/dir/?api=1&destination=${hotel.location.latitude},${hotel.location.longitude}`}
|
||||
>
|
||||
<span className={styles.link}>Google Maps</span>
|
||||
<span className={styles.link}>
|
||||
{intl.formatMessage({ id: "Google Maps" })}
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
@@ -24,12 +24,10 @@ export default function BedTypeInfo({ hasMultipleBedTypes }: BedTypeInfoProps) {
|
||||
id: "Extra bed will be provided additionally",
|
||||
})
|
||||
|
||||
const combinedStr = `${availabilityText}. ${extraBedText}`
|
||||
|
||||
if (hasMultipleBedTypes && hasChildWithExtraBed) {
|
||||
return (
|
||||
<Body>
|
||||
{availabilityText}. {extraBedText}
|
||||
</Body>
|
||||
)
|
||||
return <Body>{combinedStr}</Body>
|
||||
}
|
||||
|
||||
if (hasMultipleBedTypes) {
|
||||
|
||||
@@ -90,7 +90,9 @@ export default function Breakfast({ packages }: BreakfastProps) {
|
||||
subtitle={
|
||||
pkg.code === BreakfastPackageEnum.FREE_MEMBER_BREAKFAST
|
||||
? intl.formatMessage<React.ReactNode>(
|
||||
{ id: "breakfast.price.free" },
|
||||
{
|
||||
id: "<strikethrough>{amount}</strikethrough> <free>0 {currency}</free>/night per adult",
|
||||
},
|
||||
{
|
||||
amount: formatPrice(
|
||||
intl,
|
||||
@@ -103,7 +105,7 @@ export default function Breakfast({ packages }: BreakfastProps) {
|
||||
}
|
||||
)
|
||||
: intl.formatMessage(
|
||||
{ id: "breakfast.price" },
|
||||
{ id: "{amount}/night per adult" },
|
||||
{
|
||||
amount: formatPrice(
|
||||
intl,
|
||||
|
||||
@@ -89,10 +89,10 @@ export default function JoinScandicFriendsCard({
|
||||
<Footnote color="uiTextPlaceholder">
|
||||
{intl.formatMessage<React.ReactNode>(
|
||||
{
|
||||
id: "signup.terms",
|
||||
id: "By signing up you accept the Scandic Friends <termsAndConditionsLink>Terms and Conditions</termsAndConditionsLink>. Your membership is valid until further notice, and you can terminate your membership at any time by sending an email to Scandic's customer service",
|
||||
},
|
||||
{
|
||||
termsLink: (str) => (
|
||||
termsAndConditionsLink: (str) => (
|
||||
<Link
|
||||
variant="default"
|
||||
textDecoration="underline"
|
||||
|
||||
@@ -57,7 +57,7 @@ export default function MemberPriceModal({
|
||||
)}
|
||||
</div>
|
||||
<Button intent="primary" theme="base" onClick={() => setIsOpen(false)}>
|
||||
OK
|
||||
{intl.formatMessage({ id: "OK" })}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@@ -12,7 +12,6 @@ import useLang from "@/hooks/useLang"
|
||||
import styles from "./signup.module.css"
|
||||
|
||||
export default function Signup({ name }: { name: string }) {
|
||||
const lang = useLang()
|
||||
const intl = useIntl()
|
||||
|
||||
const [isJoinChecked, setIsJoinChecked] = useState(false)
|
||||
@@ -35,7 +34,9 @@ export default function Signup({ name }: { name: string }) {
|
||||
<div className={styles.dateField}>
|
||||
<header>
|
||||
<Caption type="bold">
|
||||
{intl.formatMessage({ id: "Birth date" })} *
|
||||
<span className={styles.required}>
|
||||
{intl.formatMessage({ id: "Birth date" })}
|
||||
</span>
|
||||
</Caption>
|
||||
</header>
|
||||
<DateSelect name="dateOfBirth" registerOptions={{ required: true }} />
|
||||
|
||||
@@ -13,3 +13,7 @@
|
||||
display: grid;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.required:after {
|
||||
content: " *";
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ export default async function HotelHeader({ hotelData }: HotelHeaderProps) {
|
||||
const hotel = hotelData.data.attributes
|
||||
|
||||
const image = hotel.hotelContent?.images
|
||||
|
||||
const addressStr = `${hotel.address.streetAddress}, ${hotel.address.city}`
|
||||
|
||||
return (
|
||||
<header className={styles.header}>
|
||||
<Image
|
||||
@@ -30,14 +33,12 @@ export default async function HotelHeader({ hotelData }: HotelHeaderProps) {
|
||||
<Title as="h1" level="h1" color="white" className={styles.title}>
|
||||
{hotel.name}
|
||||
</Title>
|
||||
<address className={styles.address}>
|
||||
<Caption color="white">
|
||||
{hotel.address.streetAddress}, {hotel.address.city}
|
||||
</Caption>
|
||||
<div className={styles.address}>
|
||||
<Caption color="white">{addressStr}</Caption>
|
||||
<Caption color="white">∙</Caption>
|
||||
<Caption color="white">
|
||||
{intl.formatMessage(
|
||||
{ id: "Distance in km to city centre" },
|
||||
{ id: "{number} km to city centre" },
|
||||
{
|
||||
number: getSingleDecimal(
|
||||
hotel.location.distanceToCentre / 1000
|
||||
@@ -45,7 +46,7 @@ export default async function HotelHeader({ hotelData }: HotelHeaderProps) {
|
||||
}
|
||||
)}
|
||||
</Caption>
|
||||
</address>
|
||||
</div>
|
||||
</div>
|
||||
<ToggleSidePeek hotelId={hotel.operaId} />
|
||||
</div>
|
||||
|
||||
@@ -160,7 +160,7 @@ export default function PaymentClient({
|
||||
(errorMessage: string) => {
|
||||
toast.error(
|
||||
intl.formatMessage({
|
||||
id: "payment.error.failed",
|
||||
id: "We had an issue processing your booking. Please try again. No charges have been made.",
|
||||
})
|
||||
)
|
||||
const currentPaymentMethod = methods.getValues("paymentMethod")
|
||||
@@ -312,10 +312,6 @@ export default function PaymentClient({
|
||||
return <LoadingSpinner />
|
||||
}
|
||||
|
||||
const guaranteeing = intl.formatMessage({ id: "guaranteeing" })
|
||||
const paying = intl.formatMessage({ id: "paying" })
|
||||
const paymentVerb = mustBeGuaranteed ? guaranteeing : paying
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormProvider {...methods}>
|
||||
@@ -387,11 +383,10 @@ export default function PaymentClient({
|
||||
<Caption>
|
||||
{intl.formatMessage<React.ReactNode>(
|
||||
{
|
||||
id: "booking.terms",
|
||||
id: "By paying with any of the payment methods available, I accept the terms for this booking and the general <termsAndConditionsLink>Terms & Conditions</termsAndConditionsLink>, and understand that Scandic will process my personal data for this booking in accordance with <privacyPolicyLink>Scandic's Privacy policy</privacyPolicyLink>. I also accept that Scandic require a valid credit card during my visit in case anything is left unpaid.",
|
||||
},
|
||||
{
|
||||
paymentVerb,
|
||||
termsLink: (str) => (
|
||||
termsAndConditionsLink: (str) => (
|
||||
<Link
|
||||
className={styles.link}
|
||||
variant="underscored"
|
||||
@@ -401,7 +396,7 @@ export default function PaymentClient({
|
||||
{str}
|
||||
</Link>
|
||||
),
|
||||
privacyLink: (str) => (
|
||||
privacyPolicyLink: (str) => (
|
||||
<Link
|
||||
className={styles.link}
|
||||
variant="underscored"
|
||||
|
||||
@@ -53,7 +53,7 @@ export default function PriceChangeDialog({
|
||||
<br />
|
||||
<span className={styles.oldPrice}>
|
||||
{formatPrice(intl, oldPrice, currency)}
|
||||
</span>{" "}
|
||||
</span>
|
||||
<strong className={styles.newPrice}>
|
||||
{formatPrice(intl, newPrice, currency)}
|
||||
</strong>
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function ToggleSidePeek({
|
||||
intent="text"
|
||||
wrapping
|
||||
>
|
||||
{intl.formatMessage({ id: "See room details" })}{" "}
|
||||
{intl.formatMessage({ id: "See room details" })}
|
||||
<ChevronRight height="14" />
|
||||
</Button>
|
||||
)
|
||||
|
||||
@@ -47,8 +47,16 @@ export default function SelectedRoom({
|
||||
className={styles.description}
|
||||
color="uiTextHighContrast"
|
||||
>
|
||||
{room.roomType}{" "}
|
||||
<span className={styles.rate}>{rateDescription}</span>
|
||||
{intl.formatMessage<React.ReactNode>(
|
||||
{ id: "{roomType} <rate>{rateDescription}</rate>" },
|
||||
{
|
||||
roomType: room.roomType,
|
||||
rateDescription,
|
||||
rate: (str) => {
|
||||
return <span className={styles.rate}>{str}</span>
|
||||
},
|
||||
}
|
||||
)}
|
||||
</Subtitle>
|
||||
<Link
|
||||
className={styles.button}
|
||||
|
||||
@@ -55,7 +55,7 @@ export default function SummaryBottomSheet({ children }: PropsWithChildren) {
|
||||
onClick={toggleSummaryOpen}
|
||||
className={styles.priceDetailsButton}
|
||||
>
|
||||
<Caption>{intl.formatMessage({ id: "Total price" })}:</Caption>
|
||||
<Caption>{intl.formatMessage({ id: "Total price" })}</Caption>
|
||||
<Subtitle>
|
||||
{formatPrice(
|
||||
intl,
|
||||
|
||||
@@ -104,10 +104,17 @@ export default function PriceDetailsTable({
|
||||
<TableSection>
|
||||
<TableSectionHeader title={intl.formatMessage({ id: "Breakfast" })} />
|
||||
<Row
|
||||
label={intl.formatMessage(
|
||||
{ id: "booking.adults.breakfasts" },
|
||||
{ totalAdults: adults, totalBreakfasts: nights.length }
|
||||
)}
|
||||
label={`${intl.formatMessage(
|
||||
{
|
||||
id: "{totalAdults, plural, one {# voksen} other {# voksne}}",
|
||||
},
|
||||
{ totalAdults: adults }
|
||||
)}, ${intl.formatMessage(
|
||||
{
|
||||
id: "{totalBreakfasts, plural, one {# morgenmad} other {# morgenmad}}",
|
||||
},
|
||||
{ totalBreakfasts: nights.length }
|
||||
)}`}
|
||||
value={formatPrice(
|
||||
intl,
|
||||
parseInt(breakfast.localPrice.totalPrice),
|
||||
@@ -116,13 +123,17 @@ export default function PriceDetailsTable({
|
||||
/>
|
||||
{children?.length ? (
|
||||
<Row
|
||||
label={intl.formatMessage(
|
||||
{ id: "booking.children.breakfasts" },
|
||||
label={`${intl.formatMessage(
|
||||
{
|
||||
totalChildren: children.length,
|
||||
totalBreakfasts: nights.length,
|
||||
}
|
||||
)}
|
||||
id: "{totalChildren, plural, one {# child} other {# children}}",
|
||||
},
|
||||
{ totalChildren: children.length }
|
||||
)}, ${intl.formatMessage(
|
||||
{
|
||||
id: "{totalBreakfasts, plural, one {# breakfast} other {# breakfasts}}",
|
||||
},
|
||||
{ totalBreakfasts: nights.length }
|
||||
)}`}
|
||||
value={formatPrice(intl, 0, breakfast.localPrice.currency)}
|
||||
/>
|
||||
) : null}
|
||||
@@ -131,17 +142,17 @@ export default function PriceDetailsTable({
|
||||
<TableSection>
|
||||
<TableSectionHeader title={intl.formatMessage({ id: "Total" })} />
|
||||
<Row
|
||||
label={intl.formatMessage({ id: "booking.vat.excl" })}
|
||||
label={intl.formatMessage({ id: "Price excluding VAT" })}
|
||||
value={formatPrice(intl, priceExclVat, totalPrice.local.currency)}
|
||||
/>
|
||||
<Row
|
||||
label={intl.formatMessage({ id: "booking.vat" }, { vat })}
|
||||
label={intl.formatMessage({ id: "VAT {vat}%" }, { vat })}
|
||||
value={formatPrice(intl, vatAmount, totalPrice.local.currency)}
|
||||
/>
|
||||
<tr className={styles.row}>
|
||||
<td>
|
||||
<Body textTransform="bold">
|
||||
{intl.formatMessage({ id: "booking.vat.incl" })}
|
||||
{intl.formatMessage({ id: "Price including VAT" })}
|
||||
</Body>
|
||||
</td>
|
||||
<td className={styles.price}>
|
||||
|
||||
@@ -107,7 +107,7 @@ export default function SummaryUI({
|
||||
const diff = dt(booking.toDate).diff(booking.fromDate, "days")
|
||||
|
||||
const nights = intl.formatMessage(
|
||||
{ id: "booking.nights" },
|
||||
{ id: "{totalNights, plural, one {# night} other {# nights}}" },
|
||||
{ totalNights: diff }
|
||||
)
|
||||
|
||||
@@ -123,6 +123,22 @@ export default function SummaryUI({
|
||||
}
|
||||
}
|
||||
|
||||
const adultsMsg = intl.formatMessage(
|
||||
{ id: "{totalAdults, plural, one {# adult} other {# adults}}" },
|
||||
{ totalAdults: adults }
|
||||
)
|
||||
|
||||
const guestsParts = [adultsMsg]
|
||||
if (children?.length) {
|
||||
const childrenMsg = intl.formatMessage(
|
||||
{
|
||||
id: "{totalChildren, plural, one {# child} other {# children}}",
|
||||
},
|
||||
{ totalChildren: children.length }
|
||||
)
|
||||
guestsParts.push(childrenMsg)
|
||||
}
|
||||
|
||||
return (
|
||||
<section className={styles.summary}>
|
||||
<header className={styles.header}>
|
||||
@@ -157,17 +173,7 @@ export default function SummaryUI({
|
||||
</Body>
|
||||
</div>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{`${intl.formatMessage(
|
||||
{ id: "booking.adults" },
|
||||
{ totalAdults: adults }
|
||||
)}${
|
||||
children?.length
|
||||
? `, ${intl.formatMessage(
|
||||
{ id: "booking.children" },
|
||||
{ totalChildren: children.length }
|
||||
)}`
|
||||
: ""
|
||||
}`}
|
||||
{guestsParts.join(", ")}
|
||||
</Caption>
|
||||
<Caption color="uiTextMediumContrast">{cancellationText}</Caption>
|
||||
<Modal
|
||||
@@ -231,7 +237,10 @@ export default function SummaryUI({
|
||||
<div className={styles.entry}>
|
||||
<div>
|
||||
<Body color="uiTextHighContrast">
|
||||
{`${intl.formatMessage({ id: "Crib (child)" })} × ${childBedCrib}`}
|
||||
{intl.formatMessage(
|
||||
{ id: "Crib (child) × {count}" },
|
||||
{ count: childBedCrib }
|
||||
)}
|
||||
</Body>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage({ id: "Based on availability" })}
|
||||
@@ -246,7 +255,12 @@ export default function SummaryUI({
|
||||
<div className={styles.entry}>
|
||||
<div>
|
||||
<Body color="uiTextHighContrast">
|
||||
{`${intl.formatMessage({ id: "Extra bed (child)" })} × ${childBedExtraBed}`}
|
||||
{intl.formatMessage(
|
||||
{ id: "Extra bed (child) × {count}" },
|
||||
{
|
||||
count: childBedExtraBed,
|
||||
}
|
||||
)}
|
||||
</Body>
|
||||
</div>
|
||||
<Body color="uiTextHighContrast">
|
||||
@@ -278,7 +292,9 @@ export default function SummaryUI({
|
||||
<div className={styles.entry}>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "booking.adults" },
|
||||
{
|
||||
id: "{totalAdults, plural, one {# adult} other {# adults}}",
|
||||
},
|
||||
{ totalAdults: adults }
|
||||
)}
|
||||
</Caption>
|
||||
@@ -294,7 +310,9 @@ export default function SummaryUI({
|
||||
<div className={styles.entry}>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "booking.children" },
|
||||
{
|
||||
id: "{totalChildren, plural, one {# child} other {# children}}",
|
||||
},
|
||||
{ totalChildren: children.length }
|
||||
)}
|
||||
</Caption>
|
||||
@@ -345,11 +363,15 @@ export default function SummaryUI({
|
||||
</Body>
|
||||
{totalPrice.requested && (
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{intl.formatMessage({ id: "Approx." })}{" "}
|
||||
{formatPrice(
|
||||
intl,
|
||||
totalPrice.requested.price,
|
||||
totalPrice.requested.currency
|
||||
{intl.formatMessage(
|
||||
{ id: "Approx. {value}" },
|
||||
{
|
||||
value: formatPrice(
|
||||
intl,
|
||||
totalPrice.requested.price,
|
||||
totalPrice.requested.currency
|
||||
),
|
||||
}
|
||||
)}
|
||||
</Caption>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client"
|
||||
|
||||
import { useParams } from "next/dist/client/components/navigation"
|
||||
import { memo, useCallback } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
@@ -64,6 +65,8 @@ function HotelCard({
|
||||
state,
|
||||
})
|
||||
|
||||
const addressStr = `${hotelData.address.streetAddress}, ${hotelData.address.city}`
|
||||
|
||||
return (
|
||||
<article
|
||||
className={classNames}
|
||||
@@ -94,9 +97,7 @@ function HotelCard({
|
||||
</Subtitle>
|
||||
<div className={styles.addressContainer}>
|
||||
<address className={styles.address}>
|
||||
<Caption color="uiTextPlaceholder">
|
||||
{hotelData.address.streetAddress}, {hotelData.address.city}
|
||||
</Caption>
|
||||
<Caption color="uiTextPlaceholder">{addressStr}</Caption>
|
||||
</address>
|
||||
<address className={styles.addressMobile}>
|
||||
<Caption color="burgundy" type="underline" asChild>
|
||||
@@ -112,7 +113,7 @@ function HotelCard({
|
||||
color="burgundy"
|
||||
size="small"
|
||||
>
|
||||
{hotelData.address.streetAddress}, {hotelData.address.city}
|
||||
{addressStr}
|
||||
</Link>
|
||||
</Caption>
|
||||
</address>
|
||||
@@ -121,7 +122,7 @@ function HotelCard({
|
||||
</div>
|
||||
<Caption color="uiTextPlaceholder">
|
||||
{intl.formatMessage(
|
||||
{ id: "Distance in km to city centre" },
|
||||
{ id: "{number} km to city centre" },
|
||||
{
|
||||
number: getSingleDecimal(
|
||||
hotelData.location.distanceToCentre / 1000
|
||||
|
||||
@@ -93,7 +93,13 @@ export default function ListingHotelCardDialog({
|
||||
{publicPrice && memberPrice && <Caption>/</Caption>}
|
||||
{memberPrice && (
|
||||
<Subtitle type="two" color="red" className={styles.memberPrice}>
|
||||
{memberPrice} {currency}
|
||||
{intl.formatMessage(
|
||||
{ id: "{price} {currency}" },
|
||||
{
|
||||
price: memberPrice,
|
||||
currency,
|
||||
}
|
||||
)}
|
||||
</Subtitle>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -88,7 +88,13 @@ export default function StandaloneHotelCardDialog({
|
||||
</Caption>
|
||||
{publicPrice && (
|
||||
<Subtitle type="two">
|
||||
{publicPrice} {currency}
|
||||
{intl.formatMessage(
|
||||
{ id: "{price} {currency}" },
|
||||
{
|
||||
price: publicPrice,
|
||||
currency,
|
||||
}
|
||||
)}
|
||||
<Body asChild>
|
||||
<span>/{intl.formatMessage({ id: "night" })}</span>
|
||||
</Body>
|
||||
@@ -100,7 +106,13 @@ export default function StandaloneHotelCardDialog({
|
||||
color="red"
|
||||
className={styles.memberPrice}
|
||||
>
|
||||
{memberPrice} {currency}
|
||||
{intl.formatMessage(
|
||||
{ id: "{price} {currency}" },
|
||||
{
|
||||
price: memberPrice,
|
||||
currency,
|
||||
}
|
||||
)}
|
||||
<Body asChild color="red">
|
||||
<span>/{intl.formatMessage({ id: "night" })}</span>
|
||||
</Body>
|
||||
|
||||
@@ -81,8 +81,10 @@ export default function HotelCardListing({
|
||||
) : activeFilters ? (
|
||||
<Alert
|
||||
type={AlertTypeEnum.Info}
|
||||
heading={intl.formatMessage({ id: "filters.nohotel.heading" })}
|
||||
text={intl.formatMessage({ id: "filters.nohotel.text" })}
|
||||
heading={intl.formatMessage({ id: "No hotels match your filters" })}
|
||||
text={intl.formatMessage({
|
||||
id: "It looks like no hotels match your filters. Try adjusting your search to find the perfect stay.",
|
||||
})}
|
||||
/>
|
||||
) : null}
|
||||
{showBackToTop && (
|
||||
|
||||
@@ -139,8 +139,10 @@ export default function FilterAndSortModal({
|
||||
onClick={() => handleApplyFiltersAndSorting(close)}
|
||||
>
|
||||
{intl.formatMessage(
|
||||
{ id: "See results" },
|
||||
{ count: resultCount }
|
||||
{ id: "See results ({ count })" },
|
||||
{
|
||||
count: resultCount,
|
||||
}
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client"
|
||||
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { useHotelFilterStore } from "@/stores/hotel-filters"
|
||||
@@ -13,7 +14,7 @@ export default function HotelCount() {
|
||||
<Preamble>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
id: "Hotel(s)",
|
||||
id: "{amount, plural, one {# hotel} other {# hotels}}",
|
||||
},
|
||||
{ amount: resultCount }
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import SelectionCard from "../SelectionCard"
|
||||
|
||||
@@ -11,6 +13,7 @@ export default function BedSelection({
|
||||
alternatives,
|
||||
nextPath,
|
||||
}: BedSelectionProps) {
|
||||
const intl = useIntl()
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
|
||||
@@ -46,7 +49,7 @@ export default function BedSelection({
|
||||
</ul>
|
||||
|
||||
<button type="submit" hidden>
|
||||
Submit
|
||||
{intl.formatMessage({ id: "Submit" })}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { useRouter, useSearchParams } from "next/navigation"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import SelectionCard from "../SelectionCard"
|
||||
|
||||
@@ -11,6 +13,7 @@ export default function BreakfastSelection({
|
||||
alternatives,
|
||||
nextPath,
|
||||
}: BreakfastSelectionProps) {
|
||||
const intl = useIntl()
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
|
||||
@@ -49,7 +52,7 @@ export default function BreakfastSelection({
|
||||
</ul>
|
||||
|
||||
<button type="submit" hidden>
|
||||
Submit
|
||||
{intl.formatMessage({ id: "Submit" })}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { useSearchParams } from "next/navigation"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
|
||||
@@ -8,12 +10,13 @@ import styles from "./details.module.css"
|
||||
import type { DetailsProps } from "@/types/components/hotelReservation/selectRate/section"
|
||||
|
||||
export default function Details({ nextPath }: DetailsProps) {
|
||||
const intl = useIntl()
|
||||
const searchParams = useSearchParams()
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<form method="GET" action={`${nextPath}?${searchParams}`}>
|
||||
<Button type="submit">Submit</Button>
|
||||
<Button type="submit">{intl.formatMessage({ id: "Submit" })}</Button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -70,7 +70,18 @@ export default async function HotelInfoCard({
|
||||
</Title>
|
||||
<div className={styles.hotelAddressDescription}>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{`${hotelAttributes.address.streetAddress}, ${hotelAttributes.address.city} ∙ ${getSingleDecimal(hotelAttributes.location.distanceToCentre / 1000)} ${intl.formatMessage({ id: "km to city center" })}`}
|
||||
{intl.formatMessage(
|
||||
{
|
||||
id: "{address}, {city} ∙ {distanceToCityCentreInKm} km to city center",
|
||||
},
|
||||
{
|
||||
address: hotelAttributes.address.streetAddress,
|
||||
city: hotelAttributes.address.city,
|
||||
distanceToCityCentreInKm: getSingleDecimal(
|
||||
hotelAttributes.location.distanceToCentre / 1000
|
||||
),
|
||||
}
|
||||
)}
|
||||
</Caption>
|
||||
<Body color="uiTextHighContrast">
|
||||
{hotelAttributes.hotelContent.texts.descriptions.medium}
|
||||
|
||||
@@ -84,8 +84,12 @@ export default function RoomFilter({
|
||||
<div className={styles.infoDesktop}>
|
||||
<Body color="uiTextHighContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "Room types available" },
|
||||
{ numberOfRooms }
|
||||
{
|
||||
id: "{numberOfRooms, plural, one {# room type} other {# room types}} available",
|
||||
},
|
||||
{
|
||||
numberOfRooms,
|
||||
}
|
||||
)}
|
||||
</Body>
|
||||
</div>
|
||||
@@ -117,8 +121,12 @@ export default function RoomFilter({
|
||||
</div>
|
||||
<Caption color="uiTextHighContrast">
|
||||
{intl.formatMessage(
|
||||
{ id: "Room types available" },
|
||||
{ numberOfRooms }
|
||||
{
|
||||
id: "{numberOfRooms, plural, one {# room type} other {# room types}} available",
|
||||
},
|
||||
{
|
||||
numberOfRooms,
|
||||
}
|
||||
)}
|
||||
</Caption>
|
||||
</div>
|
||||
@@ -138,7 +146,7 @@ export default function RoomFilter({
|
||||
<CheckboxChip
|
||||
name={code}
|
||||
key={code}
|
||||
label={intl.formatMessage({ id: description })}
|
||||
label={description}
|
||||
disabled={isDisabled}
|
||||
selected={getValues(code)}
|
||||
Icon={getIconForFeatureCode(code)}
|
||||
|
||||
+10
-5
@@ -85,7 +85,7 @@ export default function PriceList({
|
||||
</div>
|
||||
) : (
|
||||
<Subtitle type="two" color="baseTextDisabled">
|
||||
{intl.formatMessage({ id: "n/a" })}
|
||||
{intl.formatMessage({ id: "N/A" })}
|
||||
</Subtitle>
|
||||
)}
|
||||
</dd>
|
||||
@@ -112,7 +112,7 @@ export default function PriceList({
|
||||
</div>
|
||||
) : (
|
||||
<Body textTransform="bold" color="disabled">
|
||||
- {intl.formatMessage({ id: "Currency Code" })}
|
||||
-
|
||||
</Body>
|
||||
)}
|
||||
</dd>
|
||||
@@ -126,9 +126,14 @@ export default function PriceList({
|
||||
</dt>
|
||||
<dd>
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{totalPublicRequestedPricePerNight}/
|
||||
{totalMemberRequestedPricePerNight}{" "}
|
||||
{publicRequestedPrice.currency}
|
||||
{intl.formatMessage(
|
||||
{ id: "{publicPrice}/{memberPrice} {currency}" },
|
||||
{
|
||||
publicPrice: totalPublicRequestedPricePerNight,
|
||||
memberPrice: totalMemberRequestedPricePerNight,
|
||||
currency: publicRequestedPrice.currency,
|
||||
}
|
||||
)}
|
||||
</Caption>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
@@ -80,15 +80,15 @@ export default function RateSummary({
|
||||
const showMemberDiscountBanner = member && !isUserLoggedIn
|
||||
|
||||
const summaryPriceText = `${intl.formatMessage(
|
||||
{ id: "booking.nights" },
|
||||
{ id: "{totalNights, plural, one {# night} other {# nights}}" },
|
||||
{ totalNights: nights }
|
||||
)}, ${intl.formatMessage(
|
||||
{ id: "booking.adults" },
|
||||
{ id: "{totalAdults, plural, one {# adult} other {# adults}}" },
|
||||
{ totalAdults: roomsAvailability.occupancy?.adults }
|
||||
)}${
|
||||
roomsAvailability.occupancy?.children?.length
|
||||
? `, ${intl.formatMessage(
|
||||
{ id: "booking.children" },
|
||||
{ id: "{totalChildren, plural, one {# child} other {# children}}" },
|
||||
{ totalChildren: roomsAvailability.occupancy.children.length }
|
||||
)}`
|
||||
: ""
|
||||
@@ -136,11 +136,15 @@ export default function RateSummary({
|
||||
</Subtitle>
|
||||
{totalPriceToShow?.requestedPrice ? (
|
||||
<Body color="uiTextMediumContrast">
|
||||
{intl.formatMessage({ id: "Approx." })}{" "}
|
||||
{formatPrice(
|
||||
intl,
|
||||
totalPriceToShow.requestedPrice.price,
|
||||
totalPriceToShow.requestedPrice.currency
|
||||
{intl.formatMessage(
|
||||
{ id: "Approx. {value}" },
|
||||
{
|
||||
value: formatPrice(
|
||||
intl,
|
||||
totalPriceToShow.requestedPrice.price,
|
||||
totalPriceToShow.requestedPrice.currency
|
||||
),
|
||||
}
|
||||
)}
|
||||
</Body>
|
||||
) : null}
|
||||
|
||||
@@ -118,10 +118,12 @@ export default function RoomCard({
|
||||
{roomConfiguration.roomsLeft > 0 &&
|
||||
roomConfiguration.roomsLeft < 5 && (
|
||||
<span className={styles.chip}>
|
||||
<Footnote
|
||||
color="burgundy"
|
||||
textTransform="uppercase"
|
||||
>{`${roomConfiguration.roomsLeft} ${intl.formatMessage({ id: "Left" })}`}</Footnote>
|
||||
<Footnote color="burgundy" textTransform="uppercase">
|
||||
{intl.formatMessage(
|
||||
{ id: "{amount, number} left" },
|
||||
{ amount: roomConfiguration.roomsLeft }
|
||||
)}
|
||||
</Footnote>
|
||||
</span>
|
||||
)}
|
||||
{roomConfiguration.features
|
||||
@@ -150,7 +152,7 @@ export default function RoomCard({
|
||||
<Caption color="uiTextMediumContrast" className={styles.guests}>
|
||||
{intl.formatMessage(
|
||||
{
|
||||
id: "booking.guests",
|
||||
id: "Max {max, plural, one {{range} guest} other {{range} guests}}",
|
||||
},
|
||||
{ max: totalOccupancy.max, range: totalOccupancy.range }
|
||||
)}
|
||||
@@ -159,9 +161,21 @@ export default function RoomCard({
|
||||
{roomSize && (
|
||||
<Caption color="uiTextMediumContrast">
|
||||
{roomSize.min === roomSize.max
|
||||
? roomSize.min
|
||||
: `${roomSize.min}-${roomSize.max}`}
|
||||
m²
|
||||
? intl.formatMessage(
|
||||
{ id: "{roomSize} m²" },
|
||||
{
|
||||
roomSize: roomSize.min,
|
||||
}
|
||||
)
|
||||
: intl.formatMessage(
|
||||
{
|
||||
id: "{roomSizeMin} - {roomSizeMax} m²",
|
||||
},
|
||||
{
|
||||
roomSizeMin: roomSize.min,
|
||||
roomSizeMax: roomSize.max,
|
||||
}
|
||||
)}
|
||||
</Caption>
|
||||
)}
|
||||
<div className={styles.toggleSidePeek}>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import RoomFilter from "../RoomFilter"
|
||||
import RoomSelection from "../RoomSelection"
|
||||
@@ -27,6 +28,8 @@ export default function Rooms({
|
||||
hotelType,
|
||||
isUserLoggedIn,
|
||||
}: SelectRateProps) {
|
||||
const intl = useIntl()
|
||||
|
||||
const visibleRooms: RoomConfiguration[] = useMemo(() => {
|
||||
const deduped = filterDuplicateRoomTypesByLowestPrice(
|
||||
roomsAvailability.roomConfigurations
|
||||
@@ -59,27 +62,27 @@ export default function Rooms({
|
||||
() => [
|
||||
{
|
||||
code: RoomPackageCodeEnum.ACCESSIBILITY_ROOM,
|
||||
description: "Accessible Room",
|
||||
description: intl.formatMessage({ id: "Accessible Room" }),
|
||||
itemCode: availablePackages.find(
|
||||
(pkg) => pkg.code === RoomPackageCodeEnum.ACCESSIBILITY_ROOM
|
||||
)?.itemCode,
|
||||
},
|
||||
{
|
||||
code: RoomPackageCodeEnum.ALLERGY_ROOM,
|
||||
description: "Allergy Room",
|
||||
description: intl.formatMessage({ id: "Allergy Room" }),
|
||||
itemCode: availablePackages.find(
|
||||
(pkg) => pkg.code === RoomPackageCodeEnum.ALLERGY_ROOM
|
||||
)?.itemCode,
|
||||
},
|
||||
{
|
||||
code: RoomPackageCodeEnum.PET_ROOM,
|
||||
description: "Pet Room",
|
||||
description: intl.formatMessage({ id: "Pet Room" }),
|
||||
itemCode: availablePackages.find(
|
||||
(pkg) => pkg.code === RoomPackageCodeEnum.PET_ROOM
|
||||
)?.itemCode,
|
||||
},
|
||||
],
|
||||
[availablePackages]
|
||||
[availablePackages, intl]
|
||||
)
|
||||
|
||||
const handleFilter = useCallback(
|
||||
|
||||
@@ -30,15 +30,20 @@ export default function SelectionCard({
|
||||
|
||||
<div>
|
||||
<Caption color="burgundy" className={styles.price}>
|
||||
{formatPrice(intl, price, currency)}/
|
||||
{intl.formatMessage({ id: "night" })}
|
||||
{intl.formatMessage(
|
||||
{ id: "{price}/night" },
|
||||
{
|
||||
price: formatPrice(intl, price, currency),
|
||||
}
|
||||
)}
|
||||
</Caption>
|
||||
|
||||
{membersPrice && (
|
||||
<Caption color="burgundy" className={styles.membersPrice}>
|
||||
{intl.formatMessage({ id: "Members" })}{" "}
|
||||
{formatPrice(intl, membersPrice, currency)}/
|
||||
{intl.formatMessage({ id: "night" })}
|
||||
{intl.formatMessage(
|
||||
{ id: "Members {price}/night" },
|
||||
{ price: formatPrice(intl, membersPrice, currency) }
|
||||
)}
|
||||
</Caption>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user