fix(i18n): prepare for Lokalise

This commit is contained in:
Michael Zetterberg
2025-01-03 14:54:46 +01:00
parent cbc17e2c5b
commit d2ce9c0d7c
120 changed files with 1703 additions and 1042 deletions
@@ -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 ? (
+11 -3
View File
@@ -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)}
@@ -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>