feat(SW-706): make eslint rule 'formatjs/no-literal-string-in-jsx' pass

This commit is contained in:
Michael Zetterberg
2025-02-07 06:51:36 +01:00
parent e22fc1f3c8
commit 440e1f92df
393 changed files with 4839 additions and 1554 deletions

View File

@@ -73,15 +73,15 @@
"ignoreRestSiblings": true
}
],
"formatjs/enforce-description": ["warn", "anything"],
"formatjs/enforce-default-message": ["error", "literal"],
// "formatjs/enforce-description": ["warn", "anything"],
// "formatjs/enforce-default-message": ["error", "literal"],
"formatjs/enforce-placeholders": ["error"],
"formatjs/enforce-plural-rules": ["error"],
"formatjs/no-literal-string-in-jsx": ["error"],
"formatjs/no-multiple-whitespaces": ["error"],
"formatjs/no-multiple-plurals": ["error"],
"formatjs/no-invalid-icu": ["error"],
"formatjs/no-id": ["error"],
// "formatjs/no-id": ["error"],
"formatjs/no-complex-selectors": ["error"],
"formatjs/no-useless-message": ["error"],
"formatjs/prefer-pound-in-plural": ["error"]

View File

@@ -61,7 +61,7 @@ export const editProfile = protectedServerActionProcedure
message: issue.message,
})),
message: intl.formatMessage({
id: "An error occured when trying to update profile.",
defaultMessage: "An error occured when trying to update profile.",
}),
status: Status.error,
}
@@ -81,7 +81,7 @@ export const editProfile = protectedServerActionProcedure
data: input,
issues: [],
message: intl.formatMessage({
id: "An error occured when trying to update profile.",
defaultMessage: "An error occured when trying to update profile.",
}),
status: Status.error,
}
@@ -137,7 +137,9 @@ export const editProfile = protectedServerActionProcedure
if (Object.keys(body).length === 0) {
return {
data: input,
message: intl.formatMessage({ id: "Successfully updated profile!" }),
message: intl.formatMessage({
defaultMessage: "Successfully updated profile!",
}),
status: Status.success,
}
} else {
@@ -172,7 +174,7 @@ export const editProfile = protectedServerActionProcedure
data: input,
issues: [],
message: intl.formatMessage({
id: "An error occured when trying to update profile.",
defaultMessage: "An error occured when trying to update profile.",
}),
status: Status.error,
}
@@ -208,7 +210,7 @@ export const editProfile = protectedServerActionProcedure
message: issue.message,
})),
message: intl.formatMessage({
id: "An error occured when trying to update profile.",
defaultMessage: "An error occured when trying to update profile.",
}),
status: Status.error,
}
@@ -216,7 +218,9 @@ export const editProfile = protectedServerActionProcedure
return {
data: validatedData.data,
message: intl.formatMessage({ id: "Successfully updated profile!" }),
message: intl.formatMessage({
defaultMessage: "Successfully updated profile!",
}),
status: Status.success,
}
})

View File

@@ -22,7 +22,9 @@ export default function Error({
<p>
<strong>
{intl.formatMessage(
{ id: "Breadcrumbs failed for this page ({errorId})" },
{
defaultMessage: "Breadcrumbs failed for this page ({errorId})",
},
{
errorId: `${error.digest}@${Date.now()}`,
}

View File

@@ -22,7 +22,9 @@ export default function Error({
<p>
<strong>
{intl.formatMessage(
{ id: "Error loading this page ({errorId})" },
{
defaultMessage: "Error loading this page ({errorId})",
},
{
errorId: `${error.digest}@${Date.now()}`,
}

View File

@@ -37,7 +37,11 @@ export default async function MyPages({}: PageArgs<
{content?.length ? (
<Blocks blocks={content} />
) : (
<p>{intl.formatMessage({ id: "No content published" })}</p>
<p>
{intl.formatMessage({
defaultMessage: "No content published",
})}
</p>
)}
</main>
<TrackingSDK pageData={tracking} />

View File

@@ -25,7 +25,9 @@ export default function Error({
<p>
<strong>
{intl.formatMessage(
{ id: "An error occurred ({errorId})" },
{
defaultMessage: "An error occurred ({errorId})",
},
{
errorId: `${error.digest}@${Date.now()}`,
}

View File

@@ -128,12 +128,17 @@ export default async function DetailsPage({
<Alert
type={AlertTypeEnum.Alarm}
variant="inline"
heading={intl.formatMessage({ id: "Room sold out" })}
heading={intl.formatMessage({
defaultMessage: "Room sold out",
})}
text={intl.formatMessage({
id: "Unfortunately, one of the rooms you selected is sold out. Please choose another room to proceed.",
defaultMessage:
"Unfortunately, one of the rooms you selected is sold out. Please choose another room to proceed.",
})}
link={{
title: intl.formatMessage({ id: "Change room" }),
title: intl.formatMessage({
defaultMessage: "Change room",
}),
url: `${selectRate(lang)}?${selectRoomParams.toString()}`,
keepSearchParams: true,
}}

View File

@@ -26,6 +26,7 @@ export default function HotelReservationPage({ params }: PageArgs<LangParams>) {
}
return (
// eslint-disable-next-line formatjs/no-literal-string-in-jsx
<div className={styles.page}>
<TrackingSDK pageData={pageTrackingData} />
</div>

View File

@@ -61,7 +61,9 @@ export default function Error({
return (
<section className={styles.layout}>
<div className={styles.content}>
{intl.formatMessage({ id: "Something went wrong!" })}
{intl.formatMessage({
defaultMessage: "Something went wrong!",
})}
{env.NEXT_PUBLIC_NODE_ENV === "development" && (
<pre>{error.stack || error.message}</pre>
)}

View File

@@ -52,6 +52,7 @@ export default async function RootLayout({
id="Cookiebot"
src="https://consent.cookiebot.com/uc.js"
/>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<Script id="ensure-adobeDataLayer">{`
window.adobeDataLayer = window.adobeDataLayer || []
`}</Script>

View File

@@ -6,8 +6,9 @@ export default async function MiddlewareError({
params,
}: LayoutArgs<LangParams & StatusParams>) {
return (
// eslint-disable-next-line formatjs/no-literal-string-in-jsx
<div className={styles.layout}>
Middleware error {params.lang}: {params.status}
Middleware error {params.lang} {params.status}
</div>
)
}

View File

@@ -1,3 +1,5 @@
/* eslint-disable formatjs/no-literal-string-in-jsx */
import "@scandic-hotels/design-system/fonts.css"
import "@/app/globals.css"
import "@/public/_static/css/design-system-current-deprecated.css"

View File

@@ -43,6 +43,7 @@ export default async function RootLayout({
id="Cookiebot"
src="https://consent.cookiebot.com/uc.js"
/>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<Script id="ensure-adobeDataLayer">{`
window.adobeDataLayer = window.adobeDataLayer || []
`}</Script>

View File

@@ -20,18 +20,25 @@ export function AlreadyLinkedError() {
<SASModal>
<MaterialIcon icon="cancel" isFilled size={64} />
<Typography variant="Title/Subtitle/lg">
<h1>{intl.formatMessage({ id: "Accounts are already linked" })}</h1>
<h1>
{intl.formatMessage({
defaultMessage: "Accounts are already linked",
})}
</h1>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "Looks like youve already linked your Scandic Friends and SAS EuroBonus accounts!",
defaultMessage:
"Looks like youve already linked your Scandic Friends and SAS EuroBonus accounts!",
})}
</p>
</Typography>
<Button theme="base" asChild>
<Link href={partnerSas[lang]}>
{intl.formatMessage({ id: "View your linked accounts" })}
{intl.formatMessage({
defaultMessage: "View your linked accounts",
})}
</Link>
</Button>
<SASModalDivider />

View File

@@ -25,18 +25,25 @@ export function DateOfBirthError() {
size={64}
/>
<Typography variant="Title/Subtitle/lg">
<h1>{intl.formatMessage({ id: "Date of birth not matching" })}</h1>
<h1>
{intl.formatMessage({
defaultMessage: "Date of birth not matching",
})}
</h1>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "We couldnt connect your accounts. Please contact us and well help you resolve this.",
defaultMessage:
"We couldnt connect your accounts. Please contact us and well help you resolve this.",
})}
</p>
</Typography>
<Button theme="base" asChild>
<Link href={profile[lang]}>
{intl.formatMessage({ id: "View your details" })}
{intl.formatMessage({
defaultMessage: "View your details",
})}
</Link>
</Button>
<SASModalDivider />

View File

@@ -13,18 +13,22 @@ export function FailedAttemptsError() {
return (
<GenericError
title={intl.formatMessage({ id: "Too many failed attempts" })}
title={intl.formatMessage({
defaultMessage: "Too many failed attempts",
})}
variant="info"
>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "Please wait 1 hour before trying again.",
defaultMessage: "Please wait 1 hour before trying again.",
})}
</p>
</Typography>
<Button theme="base" disabled>
{intl.formatMessage({ id: "Send new code" })}
{intl.formatMessage({
defaultMessage: "Send new code",
})}
</Button>
</GenericError>
)

View File

@@ -18,7 +18,11 @@ export function SASModalDivider() {
return (
<div className={styles.divider}>
<Typography variant="Body/Paragraph/mdRegular">
<span>{intl.formatMessage({ id: "or" })}</span>
<span>
{intl.formatMessage({
defaultMessage: "or",
})}
</span>
</Typography>
</div>
)
@@ -27,13 +31,17 @@ export function SASModalDivider() {
export function SASModalContactBlock() {
const intl = useIntl()
const phone = intl.formatMessage({ id: "+46 8 517 517 00" })
const phone = intl.formatMessage({
defaultMessage: "+46 8 517 517 00",
})
return (
<div style={{ display: "flex", flexDirection: "column" }}>
<Typography variant="Title/Subtitle/md">
<h4 className={styles.contactBlockTitle}>
{intl.formatMessage({ id: "Contact us" })}
{intl.formatMessage({
defaultMessage: "Contact us",
})}
</h4>
</Typography>
<Link
@@ -42,6 +50,7 @@ export function SASModalContactBlock() {
>
{phone}
</Link>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<Link href="mailto:member@scandichotels.com" textDecoration="underline">
member@scandichotels.com
</Link>

View File

@@ -13,18 +13,22 @@ export function TooManyCodesError() {
return (
<GenericError
title={intl.formatMessage({ id: "Youve requested too many codes" })}
title={intl.formatMessage({
defaultMessage: "Youve requested too many codes",
})}
variant="info"
>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "Please wait 1 hour before trying again.",
defaultMessage: "Please wait 1 hour before trying again.",
})}
</p>
</Typography>
<Button theme="base" disabled>
{intl.formatMessage({ id: "Send new code" })}
{intl.formatMessage({
defaultMessage: "Send new code",
})}
</Button>
</GenericError>
)

View File

@@ -13,18 +13,22 @@ export function TooManyFailedAttemptsError() {
return (
<GenericError
title={intl.formatMessage({ id: "Too many failed attempts." })}
title={intl.formatMessage({
defaultMessage: "Too many failed attempts.",
})}
variant="info"
>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "Please wait 1 hour before trying again.",
defaultMessage: "Please wait 1 hour before trying again.",
})}
</p>
</Typography>
<Button theme="base" disabled>
{intl.formatMessage({ id: "Send new code" })}
{intl.formatMessage({
defaultMessage: "Send new code",
})}
</Button>
</GenericError>
)

View File

@@ -26,13 +26,13 @@ export default function Error({
return (
<GenericError
title={intl.formatMessage({
id: "Something went wrong",
defaultMessage: "Something went wrong",
})}
>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "Please try again later",
defaultMessage: "Please try again later",
})}
</p>
</Typography>

View File

@@ -37,13 +37,14 @@ export default async function Page({
return (
<GenericError
title={intl.formatMessage({
id: "We could not connect your accounts",
defaultMessage: "We could not connect your accounts",
})}
>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "We couldnt connect your accounts. Please contact us and well help you resolve this.",
defaultMessage:
"We couldnt connect your accounts. Please contact us and well help you resolve this.",
})}
</p>
</Typography>

View File

@@ -29,10 +29,14 @@ export default async function SasXScandicLayout({
<Link className={styles.backLink} href={profileOverview[params.lang]}>
<ArrowLeft height={20} width={20} />
<span className={styles.long}>
{intl.formatMessage({ id: "Back to scandichotels.com" })}
{intl.formatMessage({
defaultMessage: "Back to scandichotels.com",
})}
</span>
<span className={styles.short}>
{intl.formatMessage({ id: "Back" })}
{intl.formatMessage({
defaultMessage: "Back",
})}
</span>
</Link>
<MainMenuLogo />
@@ -45,7 +49,13 @@ export default async function SasXScandicLayout({
async function MainMenuLogo() {
const intl = await getIntl()
return <Logo alt={intl.formatMessage({ id: "Back to scandichotels.com" })} />
return (
<Logo
alt={intl.formatMessage({
defaultMessage: "Back to scandichotels.com",
})}
/>
)
}
function Logo({ alt }: { alt: string }) {

View File

@@ -61,7 +61,11 @@ export function LinkAccountForm({
src="/_static/img/partner/sas/sas-campaign-logo.png"
/>
<Typography variant="Title/Subtitle/lg">
<h3>{intl.formatMessage({ id: "Link your accounts" })}</h3>
<h3>
{intl.formatMessage({
defaultMessage: "Link your accounts",
})}
</h3>
</Typography>
</div>
<div className={styles.dateOfBirth}>
@@ -70,19 +74,23 @@ export function LinkAccountForm({
{userDateOfBirth
? intl.formatMessage(
{
id: "Birth date: {dateOfBirth, date, ::MMMM d yyyy}",
defaultMessage:
"Birth date: {dateOfBirth, date, ::MMMM d yyyy}",
},
{
dateOfBirth: new Date(userDateOfBirth),
}
)
: intl.formatMessage({ id: "Birth date is missing" })}
: intl.formatMessage({
defaultMessage: "Birth date is missing",
})}
</p>
</Typography>
<Typography variant="Label/xsRegular">
<p className={styles.dateOfBirthDescription}>
{intl.formatMessage({
id: "We require your birth date in order to link your Scandic Friends account with your SAS EuroBonus account. Please check that it is correct.",
defaultMessage:
"We require your birth date in order to link your Scandic Friends account with your SAS EuroBonus account. Please check that it is correct.",
})}
</p>
</Typography>
@@ -93,7 +101,7 @@ export function LinkAccountForm({
variant="underscored"
>
{intl.formatMessage({
id: "Edit your personal details",
defaultMessage: "Edit your personal details",
})}
<MaterialIcon icon="arrow_forward" size={18} color="CurrentColor" />
@@ -106,7 +114,7 @@ export function LinkAccountForm({
required: {
value: true,
message: intl.formatMessage({
id: "You must accept the terms and conditions",
defaultMessage: "You must accept the terms and conditions",
}),
},
disabled: !userDateOfBirth,
@@ -115,7 +123,7 @@ export function LinkAccountForm({
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "I accept the terms and conditions",
defaultMessage: "I accept the terms and conditions",
})}
</p>
</Typography>
@@ -124,7 +132,8 @@ export function LinkAccountForm({
<p className={styles.termsDescription}>
{intl.formatMessage(
{
id: "By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Terms & Conditions for Scandic Friends and SAS EuroBonus Account Linking</sasScandicTermsAndConditionsLink>.",
defaultMessage:
"By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Terms & Conditions for Scandic Friends and SAS EuroBonus Account Linking</sasScandicTermsAndConditionsLink>.",
},
{
sasScandicTermsAndConditionsLink: (str) => (
@@ -150,7 +159,9 @@ export function LinkAccountForm({
type="submit"
disabled={isPending || disableSubmit}
>
{intl.formatMessage({ id: "Link my accounts" })}
{intl.formatMessage({
defaultMessage: "Link my accounts",
})}
</Button>
</div>
</form>

View File

@@ -26,20 +26,24 @@ export default async function SASxScandicLinkPage({
color="Icon/Feedback/Success"
/>
<Typography variant="Title/Subtitle/lg">
<h1>{intl.formatMessage({ id: "Your accounts are linked" })}</h1>
<h1>
{intl.formatMessage({
defaultMessage: "Your accounts are linked",
})}
</h1>
</Typography>
<div>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "We successfully connected your accounts!",
defaultMessage: "We successfully connected your accounts!",
})}
</p>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "Redirecting you to my pages.",
defaultMessage: "Redirecting you to my pages.",
})}
</p>
</Typography>

View File

@@ -51,13 +51,16 @@ export default async function SASxScandicLoginPage({
const intentDescriptions: Record<Intent, string> = {
link: intl.formatMessage({
id: "Log in to your SAS EuroBonus account to confirm account linking.",
defaultMessage:
"Log in to your SAS EuroBonus account to confirm account linking.",
}),
unlink: intl.formatMessage({
id: "Log in to your SAS Eurobonus account to confirm account unlinking.",
defaultMessage:
"Log in to your SAS Eurobonus account to confirm account unlinking.",
}),
transfer: intl.formatMessage({
id: "In order to transfer your points we will ask you to sign in to your SAS EuroBonus account.",
defaultMessage:
"In order to transfer your points we will ask you to sign in to your SAS EuroBonus account.",
}),
}
@@ -72,7 +75,11 @@ export default async function SASxScandicLoginPage({
style={{ marginTop: 16 }}
/>
<Typography variant="Title/Subtitle/lg">
<h1>{intl.formatMessage({ id: "Redirecting you to SAS" })}</h1>
<h1>
{intl.formatMessage({
defaultMessage: "Redirecting you to SAS",
})}
</h1>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<p style={{ textAlign: "center" }}>
@@ -82,7 +89,8 @@ export default async function SASxScandicLoginPage({
<Footnote textAlign="center">
{intl.formatMessage(
{
id: "If you are not redirected automatically, please <loginLink>click here</loginLink>.",
defaultMessage:
"If you are not redirected automatically, please <loginLink>click here</loginLink>.",
},
{
loginLink: (str) => (

View File

@@ -56,7 +56,9 @@ export default function OneTimePasswordForm({
if (requestOtp.isError) {
const cause = requestOtp.error?.data?.cause as RequestOtpError
const title = intl.formatMessage({ id: "Error requesting OTP" })
const title = intl.formatMessage({
defaultMessage: "Error requesting OTP",
})
const body = getRequestErrorBody(intl, cause?.errorCode)
return (
@@ -125,11 +127,12 @@ export default function OneTimePasswordForm({
const errorMessages: Record<OtpError, ReactNode> = {
invalidCode: intl.formatMessage({
id: "The code you've entered is incorrect.",
defaultMessage: "The code you've entered is incorrect.",
}),
expiredCode: intl.formatMessage(
{
id: "This code has expired. <resendOtpLink>Send new code.</resendOtpLink>",
defaultMessage:
"This code has expired. <resendOtpLink>Send new code.</resendOtpLink>",
},
{
resendOtpLink: getResendOtpLink,
@@ -189,7 +192,8 @@ export default function OneTimePasswordForm({
<p>
{intl.formatMessage(
{
id: "Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>",
defaultMessage:
"Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>",
},
{
resendOtpLink: getResendOtpLink,
@@ -226,11 +230,11 @@ const getRequestErrorBody = (
switch (errorCode) {
case "TOO_MANY_REQUESTS":
return intl.formatMessage({
id: "Too many requests. Please try again later.",
defaultMessage: "Too many requests. Please try again later.",
})
default:
return intl.formatMessage({
id: "An error occurred while requesting a new OTP",
defaultMessage: "An error occurred while requesting a new OTP",
})
}
}

View File

@@ -16,7 +16,7 @@ export default function Loading() {
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "Hang tight...",
defaultMessage: "Hang tight...",
})}
</p>
</Typography>

View File

@@ -110,19 +110,22 @@ export default async function SASxScandicOneTimePasswordPage({
const intentDescriptions: Record<Intent, ReactNode> = {
link: intl.formatMessage(
{
id: "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
defaultMessage:
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
},
{ maskedContactInfo }
),
unlink: intl.formatMessage(
{
id: "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
defaultMessage:
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
},
{ maskedContactInfo }
),
transfer: intl.formatMessage(
{
id: "Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to transfer your points.",
defaultMessage:
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to transfer your points.",
},
{ maskedContactInfo }
),
@@ -130,10 +133,12 @@ export default async function SASxScandicOneTimePasswordPage({
return (
<OneTimePasswordForm
heading={intl.formatMessage({ id: "Verification code" })}
heading={intl.formatMessage({
defaultMessage: "Verification code",
})}
ingress={intentDescriptions[intent]}
footnote={intl.formatMessage({
id: "This verifcation is needed for additional security.",
defaultMessage: "This verifcation is needed for additional security.",
})}
otpLength={6}
onSubmit={handleOtpVerified}

View File

@@ -40,7 +40,11 @@ export default async function SASxScandicTransferSuccessPage({
/>
<div className={styles.container}>
<Typography variant="Title/Subtitle/lg">
<h1>{intl.formatMessage({ id: "Point transfer completed!" })}</h1>
<h1>
{intl.formatMessage({
defaultMessage: "Point transfer completed!",
})}
</h1>
</Typography>
<TransactionCard addedPoints={addedPoints} lang={lang} />
<div className={styles.divider} />
@@ -52,7 +56,7 @@ export default async function SASxScandicTransferSuccessPage({
>
<Link href={partnerSas[params.lang]} color="none">
{intl.formatMessage({
id: "Go back to My Pages",
defaultMessage: "Go back to My Pages",
})}
</Link>
</Button>
@@ -80,20 +84,33 @@ async function TransactionCard({
return (
<div className={styles.transactionBox}>
<Typography variant="Title/Subtitle/md">
<h2>{intl.formatMessage({ id: "Your transaction" })}</h2>
<h2>
{intl.formatMessage({
defaultMessage: "Your transaction",
})}
</h2>
</Typography>
<div className={styles.transactionDetails}>
<div className={styles.transactionRow}>
<Typography variant="Title/Overline/sm">
<h3>{intl.formatMessage({ id: "Points added" })}</h3>
<h3>
{intl.formatMessage({
defaultMessage: "Points added",
})}
</h3>
</Typography>
<Typography variant="Body/Paragraph/mdBold">
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<p>+ {transferredPoints}</p>
</Typography>
</div>
<div className={styles.transactionRow}>
<Typography variant="Title/Overline/sm">
<h3>{intl.formatMessage({ id: "Your new total" })}</h3>
<h3>
{intl.formatMessage({
defaultMessage: "Your new total",
})}
</h3>
</Typography>
<Suspense fallback={<SkeletonShimmer width="15ch" height="24px" />}>
<TotalPoints />
@@ -106,14 +123,15 @@ async function TransactionCard({
<Typography variant="Body/Paragraph/mdBold">
<h3>
{intl.formatMessage({
id: "You have enough points for a bonus night!",
defaultMessage: "You have enough points for a bonus night!",
})}
</h3>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "Bonus Nights range from 10 000 - 80 000 points. Book your next stay with us today!",
defaultMessage:
"Bonus Nights range from 10 000 - 80 000 points. Book your next stay with us today!",
})}
</p>
</Typography>
@@ -128,7 +146,13 @@ async function TransactionCard({
>
{/* TODO correct link */}
<Link href={hotelreservation(lang)} color="none">
{intl.formatMessage({ id: "Book now" })}{" "}
{intl.formatMessage({
defaultMessage: "Book now",
})}
{
/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */
" "
}
<MaterialIcon
icon="calendar_add_on"
size={20}
@@ -150,9 +174,12 @@ async function TotalPoints() {
return (
<Typography variant="Body/Paragraph/mdBold">
<p>
={" "}
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{"= "}
{intl.formatMessage(
{ id: "{points, number} points" },
{
defaultMessage: "{points, number} points",
},
{
points,
}

View File

@@ -26,13 +26,17 @@ export default async function SASxScandicUnlinkSuccessPage({
color="Icon/Feedback/Success"
/>
<Typography variant="Title/Subtitle/lg">
<h1>{intl.formatMessage({ id: "Your accounts are now unlinked" })}</h1>
<h1>
{intl.formatMessage({
defaultMessage: "Your accounts are now unlinked",
})}
</h1>
</Typography>
<div>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "Redirecting you to My Pages.",
defaultMessage: "Redirecting you to My Pages.",
})}
</p>
</Typography>

View File

@@ -47,6 +47,7 @@ export default async function RootLayout({
id="Cookiebot"
src="https://consent.cookiebot.com/uc.js"
/>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<Script id="ensure-adobeDataLayer">{`
window.adobeDataLayer = window.adobeDataLayer || []
`}</Script>

View File

@@ -22,7 +22,13 @@ export default async function ContentTypePage({
if (!user) {
console.log(`[webview:page] unable to load user`)
return <p>{intl.formatMessage({ id: "Error: No user could be loaded" })}</p>
return (
<p>
{intl.formatMessage({
defaultMessage: "Error: No user could be loaded",
})}
</p>
)
}
if ("error" in user) {
@@ -36,11 +42,19 @@ export default async function ContentTypePage({
console.log(`[webview:page] user error, redirecting to: ${redirectURL}`)
redirect(redirectURL)
case "notfound":
return <p>{intl.formatMessage({ id: "Error: user not found" })}</p>
return (
<p>
{intl.formatMessage({
defaultMessage: "Error: user not found",
})}
</p>
)
case "unknown":
return (
<p>
{intl.formatMessage({ id: "Unknown error occurred loading user" })}
{intl.formatMessage({
defaultMessage: "Unknown error occurred loading user",
})}
</p>
)
default:

View File

@@ -38,6 +38,7 @@ export default async function RootLayout({
<head>
<AdobeSDKScript />
<GTMScript />
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<Script id="ensure-adobeDataLayer">{`
window.adobeDataLayer = window.adobeDataLayer || []
`}</Script>

View File

@@ -23,7 +23,11 @@ export default function GlobalError({
<html>
<body>
<div className={styles.layout}>
<h1>{intl.formatMessage({ id: "Something went really wrong!" })}</h1>
<h1>
{intl.formatMessage({
defaultMessage: "Something went really wrong!",
})}
</h1>
</div>
</body>
</html>

View File

@@ -12,7 +12,11 @@ export default async function HowItWorks({ dynamic_content }: HowItWorksProps) {
return (
<SectionWrapper dynamic_content={dynamic_content}>
<section className={styles.container}>
<Title level="h3">{intl.formatMessage({ id: "How it works" })}</Title>
<Title level="h3">
{intl.formatMessage({
defaultMessage: "How it works",
})}
</Title>
</section>
</SectionWrapper>
)

View File

@@ -33,8 +33,12 @@ export default function Filter({
<Select
items={countryFilters}
defaultSelectedKey={""}
label={intl.formatMessage({ id: "Country" })}
aria-label={intl.formatMessage({ id: "Country" })}
label={intl.formatMessage({
defaultMessage: "Country",
})}
aria-label={intl.formatMessage({
defaultMessage: "Country",
})}
name="country"
onSelect={(value) => onFilterChange(JobylonFilterKey.country, value)}
/>
@@ -42,10 +46,10 @@ export default function Filter({
items={cityFilters}
defaultSelectedKey={""}
label={intl.formatMessage({
id: "Location (shown in local language)",
defaultMessage: "Location (shown in local language)",
})}
aria-label={intl.formatMessage({
id: "Location (shown in local language)",
defaultMessage: "Location (shown in local language)",
})}
name="city"
onSelect={(value) => onFilterChange(JobylonFilterKey.city, value)}
@@ -53,16 +57,24 @@ export default function Filter({
<Select
items={departmentFilters}
defaultSelectedKey={""}
label={intl.formatMessage({ id: "Hotel or office" })}
aria-label={intl.formatMessage({ id: "Hotel or office" })}
label={intl.formatMessage({
defaultMessage: "Hotel or office",
})}
aria-label={intl.formatMessage({
defaultMessage: "Hotel or office",
})}
name="department"
onSelect={(value) => onFilterChange(JobylonFilterKey.department, value)}
/>
<Select
items={categoryFilters}
defaultSelectedKey={""}
label={intl.formatMessage({ id: "Category" })}
aria-label={intl.formatMessage({ id: "Category" })}
label={intl.formatMessage({
defaultMessage: "Category",
})}
aria-label={intl.formatMessage({
defaultMessage: "Category",
})}
name="category"
onSelect={(value) => onFilterChange(JobylonFilterKey.category, value)}
/>

View File

@@ -31,19 +31,39 @@ export default function JobList({ allJobs }: JobListProps) {
}
const countryFilters = [
{ label: intl.formatMessage({ id: "All countries" }), value: "" },
{
label: intl.formatMessage({
defaultMessage: "All countries",
}),
value: "",
},
...state.countryFilters,
]
const cityFilters = [
{ label: intl.formatMessage({ id: "All locations" }), value: "" },
{
label: intl.formatMessage({
defaultMessage: "All locations",
}),
value: "",
},
...state.cityFilters,
]
const departmentFilters = [
{ label: intl.formatMessage({ id: "All hotels and offices" }), value: "" },
{
label: intl.formatMessage({
defaultMessage: "All hotels and offices",
}),
value: "",
},
...state.departmentFilters,
]
const categoryFilters = [
{ label: intl.formatMessage({ id: "All categories" }), value: "" },
{
label: intl.formatMessage({
defaultMessage: "All categories",
}),
value: "",
},
...state.categoryFilters,
]
@@ -59,7 +79,7 @@ export default function JobList({ allJobs }: JobListProps) {
<Subtitle type="two">
{intl.formatMessage(
{
id: "{count, plural, one {# Result} other {# Results}}",
defaultMessage: "{count, plural, one {# Result} other {# Results}}",
},
{ count: state.jobs.length }
)}

View File

@@ -24,10 +24,14 @@ export default function JobylonCard({ job }: JobylonCardProps) {
const lang = useLang()
const deadlineText = job.toDate
? intl.formatMessage(
{ id: "Deadline: {date}" },
{
defaultMessage: "Deadline: {date}",
},
{ date: dt(job.toDate).locale(lang).format("Do MMMM") }
)
: intl.formatMessage({ id: "Open for application" })
: intl.formatMessage({
defaultMessage: "Open for application",
})
return (
<div className={styles.jobylonCard}>
@@ -53,7 +57,9 @@ export default function JobylonCard({ job }: JobylonCardProps) {
asChild
>
<a href={job.url} target="_blank" rel="noopener noreferrer">
{intl.formatMessage({ id: "View & apply" })}
{intl.formatMessage({
defaultMessage: "View & apply",
})}
<MaterialIcon icon="open_in_new" size={20} />
</a>
</Button>

View File

@@ -37,14 +37,17 @@ async function LevelCard({ level }: LevelCardProps) {
const intl = await getIntl()
let pointsMsg: React.ReactNode = intl.formatMessage(
{ id: "{pointsAmount, number} points" },
{
defaultMessage: "{pointsAmount, number} points",
},
{ pointsAmount: level.required_points }
)
if (level.required_nights) {
pointsMsg = intl.formatMessage(
{
id: "{pointsAmount, number} points <highlight>or {nightsAmount, number} nights</highlight>",
defaultMessage:
"{pointsAmount, number} points <highlight>or {nightsAmount, number} nights</highlight>",
},
{
pointsAmount: level.required_points,
@@ -63,7 +66,9 @@ async function LevelCard({ level }: LevelCardProps) {
tilted="large"
>
{intl.formatMessage(
{ id: "Level {level}" },
{
defaultMessage: "Level {level}",
},
{ level: level.user_facing_tag }
)}
</BiroScript>

View File

@@ -18,10 +18,16 @@ export default function CopyButton({ membershipNumber }: CopyButtonProps) {
try {
navigator.clipboard.writeText(membershipNumber)
toast.success(
intl.formatMessage({ id: "Membership ID copied to clipboard" })
intl.formatMessage({
defaultMessage: "Membership ID copied to clipboard",
})
)
} catch {
toast.error(intl.formatMessage({ id: "Failed to copy" }))
toast.error(
intl.formatMessage({
defaultMessage: "Failed to copy",
})
)
}
}

View File

@@ -19,12 +19,17 @@ export default async function MembershipNumber({
return (
<div className={classNames}>
<Caption color="pale">
{intl.formatMessage({ id: "Membership ID:" })}
{intl.formatMessage({
defaultMessage: "Membership ID:",
})}
</Caption>
<span className={styles.icon}>
<Caption className={styles.icon} color="pale" asChild>
<code data-hj-suppress>
{membership?.membershipNumber ?? intl.formatMessage({ id: "N/A" })}
{membership?.membershipNumber ??
intl.formatMessage({
defaultMessage: "N/A",
})}
</code>
</Caption>
{membership?.membershipNumber && (

View File

@@ -24,10 +24,14 @@ export default async function Friend({
}
const isHighestLevel = isHighestMembership(membership.membershipLevel)
const lvlMessageHighest = intl.formatMessage({ id: "Highest level" })
const lvlMessageHighest = intl.formatMessage({
defaultMessage: "Highest level",
})
const lvlMessageLevel = intl.formatMessage(
{ id: "Level {level}" },
{
defaultMessage: "Level {level}",
},
{ level: membershipLevels[membership.membershipLevel] }
)

View File

@@ -24,7 +24,9 @@ export default async function ExpiringPoints({ user }: UserProps) {
<section>
<Body color="white" textTransform="bold" textAlign="center">
{intl.formatMessage(
{ id: "{points} spendable points expiring by {date}" },
{
defaultMessage: "{points} spendable points expiring by {date}",
},
{
points: intl.formatNumber(membership.pointsToExpire),
date: d.format(dateFormat),

View File

@@ -25,15 +25,23 @@ export default async function Points({ user }: UserProps) {
<PointsContainer>
<PointsColumn
value={membership?.currentPoints}
title={intl.formatMessage({ id: "Your points to spend" })}
subtitle={intl.formatMessage({ id: "as of today" })}
title={intl.formatMessage({
defaultMessage: "Your points to spend",
})}
subtitle={intl.formatMessage({
defaultMessage: "as of today",
})}
/>
{nextLevel && (
<PointsColumn
value={membership?.pointsRequiredToNextlevel}
title={intl.formatMessage({ id: "Points needed to level up" })}
title={intl.formatMessage({
defaultMessage: "Points needed to level up",
})}
subtitle={intl.formatMessage(
{ id: "next level: {nextLevel}" },
{
defaultMessage: "next level: {nextLevel}",
},
{ nextLevel: nextLevel.name }
)}
/>

View File

@@ -99,9 +99,13 @@ export default function OverviewTableClient({
showDescription={false}
/>
<Select
aria-label={intl.formatMessage({ id: "Level" })}
aria-label={intl.formatMessage({
defaultMessage: "Level",
})}
name={`reward` + column}
label={intl.formatMessage({ id: "Level" })}
label={intl.formatMessage({
defaultMessage: "Level",
})}
items={levelOptions}
value={selectedLevelMobile.level_id}
onSelect={handleSelectChange(actionEnumMobile)}
@@ -134,9 +138,13 @@ export default function OverviewTableClient({
}
return (
<Select
aria-label={intl.formatMessage({ id: "Level" })}
aria-label={intl.formatMessage({
defaultMessage: "Level",
})}
name={`reward` + column}
label={intl.formatMessage({ id: "Level" })}
label={intl.formatMessage({
defaultMessage: "Level",
})}
items={levelOptions}
value={selectedLevelDesktop.level_id}
onSelect={handleSelectChange(actionEnumDesktop)}

View File

@@ -13,7 +13,8 @@ export default function LevelSummary({
const pointsMsg: React.ReactNode = level.required_nights
? intl.formatMessage(
{
id: "{pointsAmount, number} points or {nightsAmount, number} nights",
defaultMessage:
"{pointsAmount, number} points or {nightsAmount, number} nights",
},
{
pointsAmount: level.required_points,
@@ -21,7 +22,9 @@ export default function LevelSummary({
}
)
: intl.formatMessage(
{ id: "{pointsAmount, number} points" },
{
defaultMessage: "{pointsAmount, number} points",
},
{ pointsAmount: level.required_points }
)

View File

@@ -13,7 +13,9 @@ export default function YourLevel() {
type="two"
textAlign={"center"}
>
{intl.formatMessage({ id: "Your level" })}
{intl.formatMessage({
defaultMessage: "Your level",
})}
</BiroScript>
)
}

View File

@@ -34,7 +34,9 @@ export default function AwardPoints({
<Body textTransform="bold" className={classNames}>
{isCalculated
? intl.formatNumber(awardPoints)
: intl.formatMessage({ id: "Points being calculated" })}
: intl.formatMessage({
defaultMessage: "Points being calculated",
})}
</Body>
)
}

View File

@@ -24,7 +24,7 @@ export default function Row({ transaction }: RowProps) {
const nightsMsg = intl.formatMessage(
{
id: "{totalNights, plural, one {# night} other {# nights}}",
defaultMessage: "{totalNights, plural, one {# night} other {# nights}}",
},
{
totalNights: transaction.nights,
@@ -40,28 +40,40 @@ export default function Row({ transaction }: RowProps) {
case Transactions.rewardType.stay:
case Transactions.rewardType.stayAdj:
if (transaction.hotelId === "ORS") {
description = intl.formatMessage({ id: "Former Scandic Hotel" })
description = intl.formatMessage({
defaultMessage: "Former Scandic Hotel",
})
}
if (transaction.confirmationNumber === "BALFWD") {
description = intl.formatMessage({
id: "Points earned prior to May 1, 2021",
defaultMessage: "Points earned prior to May 1, 2021",
})
}
break
case Transactions.rewardType.ancillary:
description = intl.formatMessage({ id: "Extras to your booking" })
description = intl.formatMessage({
defaultMessage: "Extras to your booking",
})
break
case Transactions.rewardType.enrollment:
description = intl.formatMessage({ id: "Sign up bonus" })
description = intl.formatMessage({
defaultMessage: "Sign up bonus",
})
break
case Transactions.rewardType.mastercard_points:
description = intl.formatMessage({ id: "Scandic Friends Mastercard" })
description = intl.formatMessage({
defaultMessage: "Scandic Friends Mastercard",
})
break
case Transactions.rewardType.tui_points:
description = intl.formatMessage({ id: "TUI Points" })
description = intl.formatMessage({
defaultMessage: "TUI Points",
})
case Transactions.rewardType.pointShop:
description = intl.formatMessage({ id: "Scandic Friends Point Shop" })
description = intl.formatMessage({
defaultMessage: "Scandic Friends Point Shop",
})
break
}

View File

@@ -15,10 +15,18 @@ export default function ClientTable({ transactions }: ClientTableProps) {
const intl = useIntl()
const tableHeadings = [
intl.formatMessage({ id: "Points" }),
intl.formatMessage({ id: "Description" }),
intl.formatMessage({ id: "Booking number" }),
intl.formatMessage({ id: "Arrival date" }),
intl.formatMessage({
defaultMessage: "Points",
}),
intl.formatMessage({
defaultMessage: "Description",
}),
intl.formatMessage({
defaultMessage: "Booking number",
}),
intl.formatMessage({
defaultMessage: "Arrival date",
}),
]
return (
@@ -44,7 +52,9 @@ export default function ClientTable({ transactions }: ClientTableProps) {
) : (
<Table.TR className={styles.placeholder}>
<Table.TD colSpan={tableHeadings.length}>
{intl.formatMessage({ id: "No transactions available" })}
{intl.formatMessage({
defaultMessage: "No transactions available",
})}
</Table.TD>
</Table.TR>
)}

View File

@@ -22,8 +22,12 @@ export default function ExpiringPointsTable({
const expiration = dt(expirationDate).locale(lang).format("DD MMM YYYY")
const tableHeadings = [
intl.formatMessage({ id: "Points" }),
intl.formatMessage({ id: "Expiration Date" }),
intl.formatMessage({
defaultMessage: "Points",
}),
intl.formatMessage({
defaultMessage: "Expiration Date",
}),
]
return (

View File

@@ -47,12 +47,16 @@ export default async function NextLevelRewardsBlock({
<article key={reward.reward_id} className={styles.card}>
<Chip>
<Lock height={16} />
{intl.formatMessage({ id: "Level up to unlock" })}
{intl.formatMessage({
defaultMessage: "Level up to unlock",
})}
</Chip>
<div className={styles.textContainer}>
<Body color="peach50" textAlign="center">
{intl.formatMessage(
{ id: "As our {level}" },
{
defaultMessage: "As our {level}",
},
{ level: nextLevelRewards.level?.name }
)}
</Body>

View File

@@ -26,7 +26,11 @@ export default function ActiveRedeemedBadge() {
>
<MaterialIcon icon="check_circle" color="Icon/Feedback/Success" />
</motion.div>
<Caption>{intl.formatMessage({ id: "Active" })}</Caption>
<Caption>
{intl.formatMessage({
defaultMessage: "Active",
})}
</Caption>
</div>
)
}

View File

@@ -17,17 +17,18 @@ export function ConfirmClose({ close }: { close: VoidFunction }) {
<div className={styles.modalContent}>
<Title level="h3" textAlign="center" textTransform="regular">
{intl.formatMessage({
id: "If you close this your benefit will be removed",
defaultMessage: "If you close this your benefit will be removed",
})}
</Title>
<Body>
{intl.formatMessage({
id: "Have you showed this benefit to the hotel staff?",
defaultMessage: "Have you showed this benefit to the hotel staff?",
})}
</Body>
<Body>
{intl.formatMessage({
id: "If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.",
defaultMessage:
"If not, please go back and do so before you close this. Once you close this your benefit will be void and removed from My Benefits.",
})}
</Body>
</div>
@@ -37,10 +38,14 @@ export function ConfirmClose({ close }: { close: VoidFunction }) {
intent="primary"
theme="base"
>
{intl.formatMessage({ id: "No, go back" })}
{intl.formatMessage({
defaultMessage: "No, go back",
})}
</Button>
<Button onClick={close} intent="secondary" theme="base">
{intl.formatMessage({ id: "Yes, close and remove benefit" })}
{intl.formatMessage({
defaultMessage: "Yes, close and remove benefit",
})}
</Button>
</footer>
</>

View File

@@ -29,7 +29,9 @@ export default function Campaign({ reward }: { reward: Campaign }) {
<Body textAlign="center">{reward.description}</Body>
<div className={styles.rewardBadge}>
<Caption textAlign="center" color="uiTextHighContrast" type="bold">
{intl.formatMessage({ id: "Promo code" })}
{intl.formatMessage({
defaultMessage: "Promo code",
})}
</Caption>
<Caption textAlign="center" color="uiTextHighContrast">
{reward.operaRewardId}
@@ -41,9 +43,17 @@ export default function Campaign({ reward }: { reward: Campaign }) {
onClick={() => {
try {
navigator.clipboard.writeText(reward.operaRewardId)
toast.success(intl.formatMessage({ id: "Copied to clipboard" }))
toast.success(
intl.formatMessage({
defaultMessage: "Copied to clipboard",
})
)
} catch {
toast.error(intl.formatMessage({ id: "Failed to copy" }))
toast.error(
intl.formatMessage({
defaultMessage: "Failed to copy",
})
)
}
}}
type="button"
@@ -53,7 +63,9 @@ export default function Campaign({ reward }: { reward: Campaign }) {
intent="primary"
>
<MaterialIcon icon="content_copy" color="CurrentColor" />
{intl.formatMessage({ id: "Copy promotion code" })}
{intl.formatMessage({
defaultMessage: "Copy promotion code",
})}
</Button>
</footer>
</>

View File

@@ -86,7 +86,9 @@ export default function Tier({
intent="primary"
theme="base"
>
{intl.formatMessage({ id: "Redeem benefit" })}
{intl.formatMessage({
defaultMessage: "Redeem benefit",
})}
</Button>
</footer>
)}
@@ -99,14 +101,18 @@ export default function Tier({
intent="primary"
theme="base"
>
{intl.formatMessage({ id: "Yes, redeem" })}
{intl.formatMessage({
defaultMessage: "Yes, redeem",
})}
</Button>
<Button
onClick={() => setRedeemStep("initial")}
intent="secondary"
theme="base"
>
{intl.formatMessage({ id: "Go back" })}
{intl.formatMessage({
defaultMessage: "Go back",
})}
</Button>
</footer>
)}

View File

@@ -15,7 +15,9 @@ export default function MembershipNumberBadge({
<div className={styles.rewardBadge}>
<Caption textAlign="center" color="uiTextHighContrast">
{intl.formatMessage(
{ id: "Membership ID: {id}" },
{
defaultMessage: "Membership ID: {id}",
},
{ id: membershipNumber }
)}
</Caption>

View File

@@ -24,7 +24,7 @@ export default function TimedRedeemedBadge() {
<MaterialIcon icon="check_circle" color="Icon/Feedback/Success" />
<Caption>
{intl.formatMessage({
id: "Redeemed & valid through:",
defaultMessage: "Redeemed & valid through:",
})}
</Caption>
</div>

View File

@@ -71,8 +71,12 @@ export default function Redeem({ reward, membershipNumber }: RedeemProps) {
>
<Button intent="primary" fullWidth>
{reward.redeemLocation === "Non-redeemable"
? intl.formatMessage({ id: "How to use" })
: intl.formatMessage({ id: "Open" })}
? intl.formatMessage({
defaultMessage: "How to use",
})
: intl.formatMessage({
defaultMessage: "Open",
})}
</Button>
<MotionOverlay
className={styles.overlay}

View File

@@ -21,11 +21,17 @@ export default function ScriptedRewardText({
: null
}
case "Campaign":
return intl.formatMessage({ id: "Campaign" })
return intl.formatMessage({
defaultMessage: "Campaign",
})
case "Surprise":
return intl.formatMessage({ id: "Surprise!" })
return intl.formatMessage({
defaultMessage: "Surprise!",
})
case "Member-voucher":
return intl.formatMessage({ id: "Voucher" })
return intl.formatMessage({
defaultMessage: "Voucher",
})
default:
return null
}

View File

@@ -17,17 +17,24 @@ export function UnlinkSAS() {
return (
<Dialog
titleText={intl.formatMessage({
id: "Are you sure you want to unlink your account?",
defaultMessage: "Are you sure you want to unlink your account?",
})}
bodyText={intl.formatMessage({
id: "This will remove any membership level upgrades gained from the linking.",
defaultMessage:
"This will remove any membership level upgrades gained from the linking.",
})}
cancelButtonText={intl.formatMessage({
defaultMessage: "Go back",
})}
proceedText={intl.formatMessage({
defaultMessage: "Yes, unlink my accounts",
})}
cancelButtonText={intl.formatMessage({ id: "Go back" })}
proceedText={intl.formatMessage({ id: "Yes, unlink my accounts" })}
proceedHref={`/${params.lang}/sas-x-scandic/login?intent=unlink`}
trigger={
<Button intent="text" theme="base">
{intl.formatMessage({ id: "Unlink accounts" })}
{intl.formatMessage({
defaultMessage: "Unlink accounts",
})}
<MaterialIcon icon="chevron_right" size={20} color="CurrentColor" />
</Button>
}

View File

@@ -58,7 +58,8 @@ export default async function SASLinkedAccount({
<p className={styles.caption}>
<MaterialIcon icon="info" size={20} />
{intl.formatMessage({
id: "Changes in your level match can take up to 24 hours to be displayed.",
defaultMessage:
"Changes in your level match can take up to 24 hours to be displayed.",
})}
</p>
</Typography>
@@ -96,20 +97,37 @@ async function MatchedAccountInfo() {
<section className={styles.matchedAccountSection}>
<div className={styles.accountDetails}>
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Linked account" })}</Label>
<Label>
{intl.formatMessage({
defaultMessage: "Linked account",
})}
</Label>
<Typography variant="Body/Paragraph/mdBold">
<p>{intl.formatMessage({ id: "SAS EuroBonus" })}</p>
<p>
{intl.formatMessage({
defaultMessage: "SAS EuroBonus",
})}
</p>
</Typography>
</div>
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Level" })}</Label>
<Label>
{intl.formatMessage({
defaultMessage: "Level",
})}
</Label>
<Typography variant="Body/Paragraph/mdBold">
<p>{sasLevelName}</p>
</Typography>
</div>
<div className={cx(styles.stack, styles.accountMemberNumber)}>
<Label>{intl.formatMessage({ id: "Membership number" })}</Label>
<Label>
{intl.formatMessage({
defaultMessage: "Membership number",
})}
</Label>
<Typography variant="Body/Paragraph/mdBold">
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<p className={styles.textRight}>EB{sasMembershipNumber}</p>
</Typography>
</div>
@@ -137,17 +155,33 @@ async function MatchedAccountInfoSkeleton() {
<section className={styles.matchedAccountSection}>
<div className={styles.accountDetails}>
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Linked account" })}</Label>
<Label>
{intl.formatMessage({
defaultMessage: "Linked account",
})}
</Label>
<Typography variant="Body/Paragraph/mdBold">
<p>{intl.formatMessage({ id: "SAS EuroBonus" })}</p>
<p>
{intl.formatMessage({
defaultMessage: "SAS EuroBonus",
})}
</p>
</Typography>
</div>
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Level" })}</Label>
<Label>
{intl.formatMessage({
defaultMessage: "Level",
})}
</Label>
<SkeletonShimmer width="6ch" height="24px" />
</div>
<div className={cx(styles.stack, styles.accountMemberNumber)}>
<Label>{intl.formatMessage({ id: "Membership number" })}</Label>
<Label>
{intl.formatMessage({
defaultMessage: "Membership number",
})}
</Label>
<SkeletonShimmer width="10ch" height="24px" />
</div>
</div>
@@ -188,19 +222,22 @@ async function TierMatchMessage({
const messageMap: Record<MatchState, ReactNode> = {
boostedBySAS: intl.formatMessage(
{
id: "<sasMark>EuroBonus {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.",
defaultMessage:
"<sasMark>EuroBonus {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.",
},
messageValues
),
boostedByScandic: intl.formatMessage(
{
id: "Your Scandic Friends level <scandicMark>{scandicLevelName}</scandicMark> has upgraded you to <sasMark>EuroBonus {sasLevelName}</sasMark>.",
defaultMessage:
"Your Scandic Friends level <scandicMark>{scandicLevelName}</scandicMark> has upgraded you to <sasMark>EuroBonus {sasLevelName}</sasMark>.",
},
messageValues
),
noBoost: intl.formatMessage(
{
id: "<sasMark>EuroBonus {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up in one of your memberships to qualify for an upgrade!",
defaultMessage:
"<sasMark>EuroBonus {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched tiers. Level up in one of your memberships to qualify for an upgrade!",
},
messageValues
),
@@ -214,7 +251,11 @@ async function TierMatchMessage({
return (
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Level match status" })}</Label>
<Label>
{intl.formatMessage({
defaultMessage: "Level match status",
})}
</Label>
<div className={styles.tierMatchText}>
<div className={styles.iconWrapper}>{iconMap[matchState]}</div>
<Typography variant="Body/Paragraph/mdRegular">
@@ -230,7 +271,11 @@ async function TierMatchMessageSkeleton() {
return (
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Level match status" })}</Label>
<Label>
{intl.formatMessage({
defaultMessage: "Level match status",
})}
</Label>
<div className={styles.tierMatchText}>
<SkeletonShimmer width="250px" height="24px" />
</div>
@@ -256,7 +301,11 @@ async function TierMatchExpiration({
return (
<div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Upgrade valid until" })}</Label>
<Label>
{intl.formatMessage({
defaultMessage: "Upgrade valid until",
})}
</Label>
<Typography variant="Body/Paragraph/mdBold">
<p>
{matchState === "boostedBySAS"

View File

@@ -78,18 +78,28 @@ async function TransferPointsFormContent({
<div>
<div className={styles.labelWithIcon}>
<Typography variant="Tag/sm">
<p>{intl.formatMessage({ id: "Transfer from" })}</p>
<p>
{intl.formatMessage({
defaultMessage: "Transfer from",
})}
</p>
</Typography>
<MaterialIcon icon="upload" />
</div>
<Typography variant="Title/Subtitle/md">
<p>{intl.formatMessage({ id: "SAS EuroBonus" })}</p>
<p>
{intl.formatMessage({
defaultMessage: "SAS EuroBonus",
})}
</p>
</Typography>
</div>
<div>
<Typography variant="Tag/sm">
<p className={styles.balanceLabel}>
{intl.formatMessage({ id: "Balance" })}
{intl.formatMessage({
defaultMessage: "Balance",
})}
</p>
</Typography>
{sasPoints === null ? (
@@ -102,7 +112,9 @@ async function TransferPointsFormContent({
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage(
{ id: "{points, number} p" },
{
defaultMessage: "{points, number} p",
},
{ points: sasPoints }
)}
</p>
@@ -115,7 +127,7 @@ async function TransferPointsFormContent({
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "You have no points to transfer.",
defaultMessage: "You have no points to transfer.",
})}
</p>
</Typography>
@@ -126,17 +138,27 @@ async function TransferPointsFormContent({
<div className={styles.labelWithIcon}>
<MaterialIcon icon="download" />
<Typography variant="Tag/sm">
<p>{intl.formatMessage({ id: "Transfer to" })}</p>
<p>
{intl.formatMessage({
defaultMessage: "Transfer to",
})}
</p>
</Typography>
</div>
<Typography variant="Title/Subtitle/md">
<p>{intl.formatMessage({ id: "Scandic Friends" })}</p>
<p>
{intl.formatMessage({
defaultMessage: "Scandic Friends",
})}
</p>
</Typography>
</div>
<div>
<Typography variant="Tag/sm">
<p className={styles.balanceLabel}>
{intl.formatMessage({ id: "Balance" })}
{intl.formatMessage({
defaultMessage: "Balance",
})}
</p>
</Typography>
{scandicPoints === null ? (
@@ -149,7 +171,9 @@ async function TransferPointsFormContent({
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage(
{ id: "{points, number} p" },
{
defaultMessage: "{points, number} p",
},
{ points: scandicPoints }
)}
</p>
@@ -167,7 +191,7 @@ async function TransferPointsFormContent({
<Typography variant="Body/Supporting text (caption)/smRegular">
<p style={{ color: "var(--Text-Tertiary)" }}>
{intl.formatMessage({
id: "Transferred points will not be level qualifying",
defaultMessage: "Transferred points will not be level qualifying",
})}
</p>
</Typography>

View File

@@ -66,7 +66,9 @@ export function TransferPointsFormClient({
className={styles.slider}
// Set max value to 1 if sasPoints is 0 since slider requires a range
maxValue={hasNoSasPoints ? 1 : sasPoints}
aria-label={intl.formatMessage({ id: "EB points to transfer" })}
aria-label={intl.formatMessage({
defaultMessage: "EB points to transfer",
})}
formatOptions={{
useGrouping: true,
maximumFractionDigits: 0,
@@ -85,7 +87,9 @@ export function TransferPointsFormClient({
<div className={styles.inputsWrapper}>
<TextField type="number" isDisabled={disabled}>
<AriaInputWithLabel
label={intl.formatMessage({ id: "EB points to transfer" })}
label={intl.formatMessage({
defaultMessage: "EB points to transfer",
})}
type="number"
min={0}
value={pointState ?? ""}
@@ -105,13 +109,17 @@ export function TransferPointsFormClient({
<p className={styles.conversionRate}>
{/* TODO maybe dynamic string based on exchange rate */}
{intl.formatMessage({
id: "1 EuroBonus point = 2 Scandic Friends points",
defaultMessage: "1 EuroBonus point = 2 Scandic Friends points",
})}
</p>
</Typography>
<div className={styles.pointsOutput}>
<Typography variant="Label/xsRegular">
<p>{intl.formatMessage({ id: "SF points to receive" })}</p>
<p>
{intl.formatMessage({
defaultMessage: "SF points to receive",
})}
</p>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<p>{intl.formatNumber(calculatedPoints)}</p>
@@ -174,7 +182,9 @@ function ConfirmModal({
onClick={() => handleToggle(true)}
disabled={disabled}
>
{intl.formatMessage({ id: "Transfer points" })}
{intl.formatMessage({
defaultMessage: "Transfer points",
})}
</Button>
<Modal isOpen={isOpen} onToggle={handleToggle}>
<div className={styles.modalContainer}>
@@ -186,18 +196,25 @@ function ConfirmModal({
/>
<Typography variant="Title/Subtitle/lg">
<h3>
{intl.formatMessage({ id: "Proceed with point transfer?" })}
{intl.formatMessage({
defaultMessage: "Proceed with point transfer?",
})}
</h3>
</Typography>
<div>
<Typography variant="Body/Paragraph/mdRegular">
<p>{intl.formatMessage({ id: "You are about to exchange:" })}</p>
<p>
{intl.formatMessage({
defaultMessage: "You are about to exchange:",
})}
</p>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage(
{
id: "<bold>{sasPoints, number} EuroBonus points</bold> to <bold>{scandicPoints, number} Scandic Friends points</bold>",
defaultMessage:
"<bold>{sasPoints, number} EuroBonus points</bold> to <bold>{scandicPoints, number} Scandic Friends points</bold>",
},
{
sasPoints,
@@ -215,7 +232,8 @@ function ConfirmModal({
<Typography variant="Body/Supporting text (caption)/smRegular">
<p className={styles.expiryText}>
{intl.formatMessage({
id: "Your exchanged points will retain their original expiry date with a maximum validity of 12 months.",
defaultMessage:
"Your exchanged points will retain their original expiry date with a maximum validity of 12 months.",
})}
</p>
</Typography>
@@ -227,7 +245,7 @@ function ConfirmModal({
color="none"
>
{intl.formatMessage({
id: "Yes, I want to transfer my points",
defaultMessage: "Yes, I want to transfer my points",
})}
</Link>
</Button>
@@ -237,7 +255,9 @@ function ConfirmModal({
theme="base"
onClick={() => handleToggle(false)}
>
{intl.formatMessage({ id: "Cancel" })}
{intl.formatMessage({
defaultMessage: "Cancel",
})}
</Button>
</div>
</div>

View File

@@ -9,7 +9,7 @@ export default async function EmptyPreviousStaysBlock() {
<section className={styles.container}>
<Title as="h4" level="h3" color="red" textAlign="center">
{intl.formatMessage({
id: "You have no previous stays.",
defaultMessage: "You have no previous stays.",
})}
</Title>
</section>

View File

@@ -30,7 +30,9 @@ export default function ShowMoreButton({
size={20}
color="CurrentColor"
/>
{intl.formatMessage({ id: "Show more" })}
{intl.formatMessage({
defaultMessage: "Show more",
})}
</Button>
</div>
)

View File

@@ -28,14 +28,20 @@ export default async function EmptyUpcomingStaysBlock() {
className={styles.title}
textAlign="center"
>
{intl.formatMessage({ id: "You have no upcoming stays." })}
{intl.formatMessage({
defaultMessage: "You have no upcoming stays.",
})}
<span className={styles.burgundyTitle}>
{intl.formatMessage({ id: "Where should you go next?" })}
{intl.formatMessage({
defaultMessage: "Where should you go next?",
})}
</span>
</Title>
</div>
<Link href={href} className={styles.link} color="peach80">
{intl.formatMessage({ id: "Get inspired" })}
{intl.formatMessage({
defaultMessage: "Get inspired",
})}
<MaterialIcon icon="arrow_forward" color="CurrentColor" />
</Link>
</section>

View File

@@ -59,6 +59,7 @@ export default function StayCard({ stay }: StayCardProps) {
<Caption asChild>
<time dateTime={arrivalDateTime}>{arrivalDate}</time>
</Caption>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{" - "}
<Caption asChild>
<time dateTime={departDateTime}>{departDate}</time>

View File

@@ -28,14 +28,20 @@ export default async function EmptyUpcomingStaysBlock() {
className={styles.title}
textAlign="center"
>
{intl.formatMessage({ id: "You have no upcoming stays." })}
{intl.formatMessage({
defaultMessage: "You have no upcoming stays.",
})}
<span className={styles.burgundyTitle}>
{intl.formatMessage({ id: "Where should you go next?" })}
{intl.formatMessage({
defaultMessage: "Where should you go next?",
})}
</span>
</Title>
</div>
<Link href={href} className={styles.link} color="peach80">
{intl.formatMessage({ id: "Get inspired" })}
{intl.formatMessage({
defaultMessage: "Get inspired",
})}
<MaterialIcon icon="arrow_forward" color="CurrentColor" />
</Link>
</section>

View File

@@ -56,7 +56,9 @@ export default async function HotelListingItem({
</div>
<Caption color="uiTextPlaceholder">
{intl.formatMessage(
{ id: "{number} km to city center" },
{
defaultMessage: "{number} km to city center",
},
{
number: getSingleDecimal(
hotel.location.distanceToCentre / 1000

View File

@@ -21,7 +21,9 @@ export function getTypeSpecificInformation(
alt: images.metaData.altText,
},
cta: {
text: intl.formatMessage({ id: "See hotel details" }),
text: intl.formatMessage({
defaultMessage: "See hotel details",
}),
url,
openInNewTab: false,
},
@@ -41,7 +43,9 @@ export function getTypeSpecificInformation(
}
if (meetingUrl) {
data.cta = {
text: intl.formatMessage({ id: "Book a meeting" }),
text: intl.formatMessage({
defaultMessage: "Book a meeting",
}),
url: meetingUrl,
openInNewTab: true,
}

View File

@@ -53,22 +53,31 @@ export default function MobileToggleButton({
}, 0)
const totalNightsMsg = intl.formatMessage(
{ id: "{totalNights, plural, one {# night} other {# nights}}" },
{
defaultMessage: "{totalNights, plural, one {# night} other {# nights}}",
},
{ totalNights }
)
const totalAdultsMsg = intl.formatMessage(
{ id: "{totalAdults, plural, one {# adult} other {# adults}}" },
{
defaultMessage: "{totalAdults, plural, one {# adult} other {# adults}}",
},
{ totalAdults }
)
const totalChildrenMsg = intl.formatMessage(
{ id: "{totalChildren, plural, one {# child} other {# children}}" },
{
defaultMessage:
"{totalChildren, plural, one {# child} other {# children}}",
},
{ totalChildren }
)
const totalRoomsMsg = intl.formatMessage(
{ id: "{totalRooms, plural, one {# room} other {# rooms}}" },
{
defaultMessage: "{totalRooms, plural, one {# room} other {# rooms}}",
},
{ totalRooms }
)
@@ -88,14 +97,18 @@ export default function MobileToggleButton({
<span className={styles.block}>
<Typography variant={"Body/Supporting text (caption)/smBold"}>
<span className={styles.blockLabel}>
{intl.formatMessage({ id: "Where to?" })}
{intl.formatMessage({
defaultMessage: "Where to?",
})}
</span>
</Typography>
<Typography variant={"Body/Paragraph/mdRegular"}>
<span className={styles.placeholder}>
{searchTerm
? searchTerm
: intl.formatMessage({ id: "Destination" })}
: intl.formatMessage({
defaultMessage: "Destination",
})}
</span>
</Typography>
</span>
@@ -108,7 +121,9 @@ export default function MobileToggleButton({
<Typography variant={"Body/Paragraph/mdRegular"}>
<span>
{intl.formatMessage(
{ id: "{selectedFromDate} - {selectedToDate}" },
{
defaultMessage: "{selectedFromDate} - {selectedToDate}",
},
{
selectedFromDate,
selectedToDate,
@@ -133,7 +148,8 @@ export default function MobileToggleButton({
<span className={styles.locationAndDate}>
{intl.formatMessage(
{
id: "{selectedFromDate} - {selectedToDate} ({totalNights}) {details}",
defaultMessage:
"{selectedFromDate} - {selectedToDate} ({totalNights}) {details}",
},
{
selectedFromDate,
@@ -162,7 +178,9 @@ export function MobileToggleButtonSkeleton() {
<span className={styles.block}>
<Typography variant={"Body/Supporting text (caption)/smBold"}>
<span className={styles.blockLabel}>
{intl.formatMessage({ id: "Where to?" })}
{intl.formatMessage({
defaultMessage: "Where to?",
})}
</span>
</Typography>
<SkeletonShimmer display={"block"} height="24px" />
@@ -172,7 +190,10 @@ export function MobileToggleButtonSkeleton() {
<Typography variant="Body/Supporting text (caption)/smBold">
<span className={styles.blockLabel}>
{intl.formatMessage(
{ id: "{totalNights, plural, one {# night} other {# nights}}" },
{
defaultMessage:
"{totalNights, plural, one {# night} other {# nights}}",
},
{ totalNights: 0 }
)}
</span>

View File

@@ -22,7 +22,9 @@ export function CarouselPrevious({ className, ...props }: CarouselButtonProps) {
<button
className={cx(styles.button, styles.buttonPrev, className)}
onClick={scrollPrev}
aria-label={intl.formatMessage({ id: "Previous slide" })}
aria-label={intl.formatMessage({
defaultMessage: "Previous slide",
})}
{...props}
>
<MaterialIcon color="Icon/Interactive/Default" icon="arrow_back" />
@@ -41,7 +43,9 @@ export function CarouselNext({ className, ...props }: CarouselButtonProps) {
<button
className={cx(styles.button, styles.buttonNext, className)}
onClick={scrollNext}
aria-label={intl.formatMessage({ id: "Next slide" })}
aria-label={intl.formatMessage({
defaultMessage: "Next slide",
})}
{...props}
>
<MaterialIcon color="Icon/Interactive/Default" icon="arrow_forward" />

View File

@@ -31,11 +31,18 @@ export default async function CityDataContainer({
const sortItems: SortItem[] = [
{
label: intl.formatMessage({ id: "Recommended" }),
label: intl.formatMessage({
defaultMessage: "Recommended",
}),
value: SortOption.Recommended,
isDefault: true,
},
{ label: intl.formatMessage({ id: "Name" }), value: SortOption.Name },
{
label: intl.formatMessage({
defaultMessage: "Name",
}),
value: SortOption.Name,
},
]
return (

View File

@@ -31,7 +31,9 @@ export default function CityListingItem({ city }: CityListingItemProps) {
images={galleryImages}
fill
title={intl.formatMessage(
{ id: "{title} - Image gallery" },
{
defaultMessage: "{title} - Image gallery",
},
{ title: city.cityName }
)}
/>
@@ -57,7 +59,9 @@ export default function CityListingItem({ city }: CityListingItemProps) {
>
<Link href={city.url}>
{intl.formatMessage(
{ id: "Explore {city}" },
{
defaultMessage: "Explore {city}",
},
{ city: city.cityName }
)}
</Link>

View File

@@ -38,7 +38,8 @@ export default function CityListing() {
<Subtitle type="two">
{intl.formatMessage(
{
id: "{count, plural, one {# Location} other {# Locations}}",
defaultMessage:
"{count, plural, one {# Location} other {# Locations}}",
},
{ count: activeCities.length }
)}
@@ -48,9 +49,12 @@ export default function CityListing() {
{activeCities.length === 0 ? (
<Alert
type={AlertTypeEnum.Info}
heading={intl.formatMessage({ id: "No matching locations found" })}
heading={intl.formatMessage({
defaultMessage: "No matching locations found",
})}
text={intl.formatMessage({
id: "It looks like no location match your filters. Try adjusting your search to find the perfect stay.",
defaultMessage:
"It looks like no location match your filters. Try adjusting your search to find the perfect stay.",
})}
/>
) : (

View File

@@ -63,7 +63,9 @@ export default function HotelList() {
<div className={styles.header}>
<Body>
{intl.formatMessage(
{ id: "{count} hotels" },
{
defaultMessage: "{count} hotels",
},
{ count: visibleHotels.length }
)}
</Body>
@@ -72,9 +74,12 @@ export default function HotelList() {
{activeHotels.length === 0 ? (
<Alert
type={AlertTypeEnum.Info}
heading={intl.formatMessage({ id: "No matching hotels found" })}
heading={intl.formatMessage({
defaultMessage: "No matching hotels found",
})}
text={intl.formatMessage({
id: "It looks like no hotels match your filters. Try adjusting your search to find the perfect stay.",
defaultMessage:
"It looks like no hotels match your filters. Try adjusting your search to find the perfect stay.",
})}
/>
) : (

View File

@@ -67,7 +67,9 @@ export default function HotelListItem(data: DestinationPagesHotelData) {
fill
sizes="(min-width: 768px) 350px, 100vw"
title={intl.formatMessage(
{ id: "{title} - Image gallery" },
{
defaultMessage: "{title} - Image gallery",
},
{ title: hotel.name }
)}
/>
@@ -93,7 +95,9 @@ export default function HotelListItem(data: DestinationPagesHotelData) {
<Divider variant="vertical" color="beige" />
<Caption color="uiTextPlaceholder">
{intl.formatMessage(
{ id: "{number} km to city center" },
{
defaultMessage: "{number} km to city center",
},
{
number: getSingleDecimal(
hotel.location.distanceToCentre / 1000
@@ -120,7 +124,9 @@ export default function HotelListItem(data: DestinationPagesHotelData) {
<div className={styles.ctaWrapper}>
<Button intent="tertiary" theme="base" size="small" asChild>
<Link href={url}>
{intl.formatMessage({ id: "See hotel details" })}
{intl.formatMessage({
defaultMessage: "See hotel details",
})}
</Link>
</Button>
</div>

View File

@@ -29,7 +29,9 @@ export default function CityList() {
<div className={styles.header}>
<Body>
{intl.formatMessage(
{ id: "{count} destinations" },
{
defaultMessage: "{count} destinations",
},
{ count: activeCities.length }
)}
</Body>
@@ -38,9 +40,12 @@ export default function CityList() {
{activeCities.length === 0 ? (
<Alert
type={AlertTypeEnum.Info}
heading={intl.formatMessage({ id: "No matching locations found" })}
heading={intl.formatMessage({
defaultMessage: "No matching locations found",
})}
text={intl.formatMessage({
id: "It looks like no location match your filters. Try adjusting your search to find the perfect stay.",
defaultMessage:
"It looks like no location match your filters. Try adjusting your search to find the perfect stay.",
})}
/>
) : (

View File

@@ -29,7 +29,9 @@ export default function CityListItem({ city }: CityListItemProps) {
images={galleryImages}
fill
title={intl.formatMessage(
{ id: "{title} - Image gallery" },
{
defaultMessage: "{title} - Image gallery",
},
{ title: city.cityName }
)}
/>
@@ -47,7 +49,9 @@ export default function CityListItem({ city }: CityListItemProps) {
<Button intent="tertiary" theme="base" size="small" asChild>
<Link href={city.url}>
{intl.formatMessage(
{ id: "Explore {city}" },
{
defaultMessage: "Explore {city}",
},
{ city: city.cityName }
)}
</Link>

View File

@@ -18,15 +18,24 @@ export function getHeadingText(
if (facilityFilter) {
return intl.formatMessage(
{ id: "Hotels with {filter} in {location}" },
{
defaultMessage: "Hotels with {filter} in {location}",
},
{ location, filter: facilityFilter.name }
)
} else if (surroudingsFilter) {
return intl.formatMessage(
{ id: "Hotels near {filter} in {location}" },
{
defaultMessage: "Hotels near {filter} in {location}",
},
{ location, filter: surroudingsFilter.name }
)
}
}
return intl.formatMessage({ id: "Hotels in {location}" }, { location })
return intl.formatMessage(
{
defaultMessage: "Hotels in {location}",
},
{ location }
)
}

View File

@@ -18,7 +18,7 @@ export default async function Destination({
const intl = await getIntl()
const accordionSubtitle = intl.formatMessage(
{
id: "{amount, plural, one {# hotel} other {# hotels}}",
defaultMessage: "{amount, plural, one {# hotel} other {# hotels}}",
},
{ amount: numberOfHotels }
)
@@ -36,10 +36,14 @@ export default async function Destination({
color="baseTextMediumContrast"
textDecoration="underline"
>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
{`${city.name} (${city.hotelCount})`}
</Link>
) : (
<Body>{`${city.name} (${city.hotelCount})`}</Body>
<>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<Body>{`${city.name} (${city.hotelCount})`}</Body>
</>
)}
</li>
) : null
@@ -49,7 +53,7 @@ export default async function Destination({
<Link href={countryUrl} variant="icon" color="burgundy" weight="bold">
{intl.formatMessage(
{
id: "View all hotels in {country}",
defaultMessage: "View all hotels in {country}",
},
{ country: country }
)}

View File

@@ -18,7 +18,9 @@ export default async function HotelsSection() {
return (
<section className={styles.container}>
<Title level="h4" as="h2" textAlign="center">
{intl.formatMessage({ id: "Explore all our hotels" })}
{intl.formatMessage({
defaultMessage: "Explore all our hotels",
})}
</Title>
<DestinationsList destinations={destinations} />
</section>

View File

@@ -60,14 +60,16 @@ export function ClientInline({
<div className={styles.fields}>
<Typography variant="Body/Supporting text (caption)/smBold">
<Label className={styles.label}>
{intl.formatMessage({ id: "Where to?" })}
{intl.formatMessage({
defaultMessage: "Where to?",
})}
</Label>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<Input
className={styles.input}
placeholder={intl.formatMessage({
id: "Hotels & Destinations",
defaultMessage: "Hotels & Destinations",
})}
onChange={(evt) => {
startTransition(() => {
@@ -83,7 +85,11 @@ export function ClientInline({
{state.value !== "" && (
<Typography variant="Body/Supporting text (caption)/smBold">
<ButtonRAC className={styles.clearButton}>Clear</ButtonRAC>
<ButtonRAC className={styles.clearButton}>
{intl.formatMessage({
defaultMessage: "Clear",
})}
</ButtonRAC>
</Typography>
)}
</div>
@@ -96,7 +102,9 @@ export function ClientInline({
type="submit"
>
<MaterialIcon icon="search" color="CurrentColor" />
{intl.formatMessage({ id: "Search" })}
{intl.formatMessage({
defaultMessage: "Search",
})}
</Button>
</Typography>
</form>
@@ -113,7 +121,9 @@ export function ClientInline({
>
{showResults ? (
<ResultsMemo
aria-label={intl.formatMessage({ id: "Results" })}
aria-label={intl.formatMessage({
defaultMessage: "Results",
})}
results={results}
onAction={onAction}
renderEmptyState={true}
@@ -122,7 +132,7 @@ export function ClientInline({
{showHistory ? (
<ResultsMemo
aria-label={intl.formatMessage({
id: "Latest searches",
defaultMessage: "Latest searches",
})}
results={latest}
onAction={onAction}

View File

@@ -45,12 +45,16 @@ export function ClientModal({
<span>
<Typography variant="Body/Supporting text (caption)/smBold">
<span className={styles.label}>
{intl.formatMessage({ id: "Where to?" })}
{intl.formatMessage({
defaultMessage: "Where to?",
})}
</span>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<span className={styles.placeholder}>
{intl.formatMessage({ id: "Hotels & Destinations" })}
{intl.formatMessage({
defaultMessage: "Hotels & Destinations",
})}
</span>
</Typography>
</span>
@@ -62,12 +66,16 @@ export function ClientModal({
<Modal className={styles.modal}>
<Dialog className={styles.dialog}>
<Heading level={2} className="sr-only">
{intl.formatMessage({ id: "Find a location" })}
{intl.formatMessage({
defaultMessage: "Find a location",
})}
</Heading>
<ButtonRAC
className={styles.closeButton}
slot="close"
aria-label={intl.formatMessage({ id: "Close" })}
aria-label={intl.formatMessage({
defaultMessage: "Close",
})}
>
<MaterialIcon icon="close" color="CurrentColor" />
</ButtonRAC>
@@ -91,14 +99,16 @@ export function ClientModal({
>
<Typography variant="Body/Supporting text (caption)/smBold">
<Label className={styles.label}>
{intl.formatMessage({ id: "Where to?" })}
{intl.formatMessage({
defaultMessage: "Where to?",
})}
</Label>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<Input
className={styles.input}
placeholder={intl.formatMessage({
id: "Hotels & Destinations",
defaultMessage: "Hotels & Destinations",
})}
onChange={(evt) => {
startTransition(() => {
@@ -115,7 +125,9 @@ export function ClientModal({
{state.value !== "" && (
<Typography variant="Body/Supporting text (caption)/smBold">
<ButtonRAC className={styles.clearButton}>
Clear
{intl.formatMessage({
defaultMessage: "Clear",
})}
</ButtonRAC>
</Typography>
)}
@@ -132,7 +144,9 @@ export function ClientModal({
>
{showResults ? (
<ResultsMemo
aria-label={intl.formatMessage({ id: "Results" })}
aria-label={intl.formatMessage({
defaultMessage: "Results",
})}
results={results}
onAction={onAction}
renderEmptyState={true}
@@ -141,7 +155,7 @@ export function ClientModal({
{showHistory ? (
<ResultsMemo
aria-label={intl.formatMessage({
id: "Latest searches",
defaultMessage: "Latest searches",
})}
results={latest}
onAction={onAction}

View File

@@ -16,7 +16,7 @@ export function ResultsSkeleton() {
<Typography variant="Title/Overline/sm">
<header className={styles.sectionHeader}>
{intl.formatMessage({
id: "Loading results",
defaultMessage: "Loading results",
})}
</header>
</Typography>

View File

@@ -45,13 +45,16 @@ export function Results({
<>
<Typography variant="Body/Paragraph/mdBold">
<Header className={styles.noResultsLabel}>
{intl.formatMessage({ id: "No results" })}
{intl.formatMessage({
defaultMessage: "No results",
})}
</Header>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<span className={styles.noResultsDescription}>
{intl.formatMessage({
id: "We couldn't find a matching location for your search.",
defaultMessage:
"We couldn't find a matching location for your search.",
})}
</span>
</Typography>

View File

@@ -158,7 +158,9 @@ export function JumpToClient<T extends JumpToData>({
type: "clearHistory",
closesModal: false,
icon: <MaterialIcon icon="delete" color="CurrentColor" />,
displayName: intl.formatMessage({ id: "Clear searches" }),
displayName: intl.formatMessage({
defaultMessage: "Clear searches",
}),
},
],
},

View File

@@ -10,7 +10,7 @@ export default function InputForm() {
<input
type="text"
placeholder={intl.formatMessage({
id: "Find hotels and destinations",
defaultMessage: "Find hotels and destinations",
})}
className={styles.formInput}
/>

View File

@@ -17,49 +17,63 @@ export function mapExperiencesToListData(
Icon: (props: MaterialIconSetIconProps) => (
<MaterialIcon icon="hiking" {...props} />
),
name: intl.formatMessage({ id: "Hiking" }),
name: intl.formatMessage({
defaultMessage: "Hiking",
}),
}
case "Kayaking":
return {
Icon: (props: MaterialIconSetIconProps) => (
<MaterialIcon icon="kayaking" {...props} />
),
name: intl.formatMessage({ id: "Kayaking" }),
name: intl.formatMessage({
defaultMessage: "Kayaking",
}),
}
case "Bike friendly":
return {
Icon: (props: MaterialIconSetIconProps) => (
<MaterialIcon icon="pedal_bike" {...props} />
),
name: intl.formatMessage({ id: "Bike friendly" }),
name: intl.formatMessage({
defaultMessage: "Bike friendly",
}),
}
case "Museums":
return {
Icon: (props: MaterialIconSetIconProps) => (
<MaterialIcon icon="museum" {...props} />
),
name: intl.formatMessage({ id: "Museums" }),
name: intl.formatMessage({
defaultMessage: "Museums",
}),
}
case "Family friendly":
return {
Icon: (props: MaterialIconSetIconProps) => (
<MaterialIcon icon="family_restroom" {...props} />
),
name: intl.formatMessage({ id: "Family friendly" }),
name: intl.formatMessage({
defaultMessage: "Family friendly",
}),
}
case "City pulse":
return {
Icon: (props: MaterialIconSetIconProps) => (
<MaterialIcon icon="location_city" {...props} />
),
name: intl.formatMessage({ id: "City pulse" }),
name: intl.formatMessage({
defaultMessage: "City pulse",
}),
}
case "Nightlife":
return {
Icon: (props: MaterialIconSetIconProps) => (
<MaterialIcon icon="nightlife" {...props} />
),
name: intl.formatMessage({ id: "Nightlife" }),
name: intl.formatMessage({
defaultMessage: "Nightlife",
}),
}
default:
return {

View File

@@ -23,13 +23,22 @@ export default async function HotelDataContainer({
const sortItems: SortItem[] = [
{
label: intl.formatMessage({ id: "Distance to city center" }),
label: intl.formatMessage({
defaultMessage: "Distance to city center",
}),
value: SortOption.Distance,
isDefault: true,
},
{ label: intl.formatMessage({ id: "Name" }), value: SortOption.Name },
{
label: intl.formatMessage({ id: "TripAdvisor rating" }),
label: intl.formatMessage({
defaultMessage: "Name",
}),
value: SortOption.Name,
},
{
label: intl.formatMessage({
defaultMessage: "TripAdvisor rating",
}),
value: SortOption.TripAdvisorRating,
},
]

View File

@@ -48,7 +48,9 @@ export default function HotelListingItem(data: DestinationPagesHotelData) {
fill
sizes="(min-width: 768px) 350px, 100vw"
title={intl.formatMessage(
{ id: "{title} - Image gallery" },
{
defaultMessage: "{title} - Image gallery",
},
{ title: hotel.name }
)}
/>
@@ -72,7 +74,9 @@ export default function HotelListingItem(data: DestinationPagesHotelData) {
<Divider variant="vertical" color="beige" />
<Caption color="uiTextPlaceholder">
{intl.formatMessage(
{ id: "{number} km to city center" },
{
defaultMessage: "{number} km to city center",
},
{
number: getSingleDecimal(
hotel.location.distanceToCentre / 1000
@@ -107,7 +111,9 @@ export default function HotelListingItem(data: DestinationPagesHotelData) {
scroll={true}
onClick={() => setActiveMarker(hotel.id)}
>
{intl.formatMessage({ id: "See on map" })}
{intl.formatMessage({
defaultMessage: "See on map",
})}
<MaterialIcon
icon="chevron_right"
size={20}
@@ -123,7 +129,9 @@ export default function HotelListingItem(data: DestinationPagesHotelData) {
<div className={styles.ctaWrapper}>
<Button intent="tertiary" theme="base" size="small" asChild>
<Link href={url}>
{intl.formatMessage({ id: "See hotel details" })}
{intl.formatMessage({
defaultMessage: "See hotel details",
})}
</Link>
</Button>
</div>

View File

@@ -51,7 +51,7 @@ export default function HotelListing() {
<Subtitle type="two">
{intl.formatMessage(
{
id: "{count, plural, one {# Hotel} other {# Hotels}}",
defaultMessage: "{count, plural, one {# Hotel} other {# Hotels}}",
},
{ count: activeHotels.length }
)}
@@ -68,7 +68,9 @@ export default function HotelListing() {
>
<Link href={mapUrl}>
<MaterialIcon icon="map" color="CurrentColor" />
{intl.formatMessage({ id: "See on map" })}
{intl.formatMessage({
defaultMessage: "See on map",
})}
</Link>
</Button>
)}
@@ -78,9 +80,12 @@ export default function HotelListing() {
{activeHotels.length === 0 ? (
<Alert
type={AlertTypeEnum.Info}
heading={intl.formatMessage({ id: "No matching hotels found" })}
heading={intl.formatMessage({
defaultMessage: "No matching hotels found",
})}
text={intl.formatMessage({
id: "It looks like no hotels match your filters. Try adjusting your search to find the perfect stay.",
defaultMessage:
"It looks like no hotels match your filters. Try adjusting your search to find the perfect stay.",
})}
/>
) : (

View File

@@ -55,7 +55,9 @@ export default function HotelMapCard({
variant="icon"
className={styles.closeButton}
onClick={handleClose}
aria-label={intl.formatMessage({ id: "Close" })}
aria-label={intl.formatMessage({
defaultMessage: "Close",
})}
>
<MaterialIcon
icon="close"
@@ -120,7 +122,9 @@ export default function HotelMapCard({
size="small"
>
<Link href={url}>
{intl.formatMessage({ id: "See hotel information" })}
{intl.formatMessage({
defaultMessage: "See hotel information",
})}
</Link>
</Button>
)}

View File

@@ -111,7 +111,15 @@ export default function DynamicMap({
)}
ref={ref}
>
<ErrorBoundary fallback={<h2>Unable to display map</h2>}>
<ErrorBoundary
fallback={
<h2>
{intl.formatMessage({
defaultMessage: "Unable to display map",
})}
</h2>
}
>
<Map {...mapOptions}>{children}</Map>
</ErrorBoundary>
<div className={styles.ctaButtons}>
@@ -125,7 +133,11 @@ export default function DynamicMap({
onClick={onClose}
>
<MaterialIcon icon="close" color="CurrentColor" />
<span>{intl.formatMessage({ id: "Close the map" })}</span>
<span>
{intl.formatMessage({
defaultMessage: "Close the map",
})}
</span>
</Button>
)}
<div className={styles.zoomButtons}>
@@ -136,7 +148,9 @@ export default function DynamicMap({
size="small"
className={styles.zoomButton}
onClick={zoomIn}
aria-label={intl.formatMessage({ id: "Zoom out" })}
aria-label={intl.formatMessage({
defaultMessage: "Zoom out",
})}
>
<MaterialIcon icon="add" color="CurrentColor" size={20} />
</Button>
@@ -147,7 +161,9 @@ export default function DynamicMap({
size="small"
className={styles.zoomButton}
onClick={zoomOut}
aria-label={intl.formatMessage({ id: "Zoom in" })}
aria-label={intl.formatMessage({
defaultMessage: "Zoom in",
})}
>
<MaterialIcon icon="remove" color="CurrentColor" size={20} />
</Button>

View File

@@ -146,7 +146,9 @@ export default function Map({
style={
{ "--destination-map-height": mapHeight } as React.CSSProperties
}
aria-label={intl.formatMessage({ id: "Map view" })}
aria-label={intl.formatMessage({
defaultMessage: "Map view",
})}
>
<div className={styles.mobileNavigation}>
<Button
@@ -160,7 +162,9 @@ export default function Map({
size={20}
color="CurrentColor"
/>
{intl.formatMessage({ id: "Back" })}
{intl.formatMessage({
defaultMessage: "Back",
})}
</Button>
<DestinationFilterAndSort
listType={pageType === "city" ? "hotel" : "city"}

View File

@@ -45,7 +45,10 @@ export default function DestinationPageSidepeek({
size="small"
wrapping
>
{buttonText || intl.formatMessage({ id: "Read more" })}
{buttonText ||
intl.formatMessage({
defaultMessage: "Read more",
})}
<MaterialIcon icon="chevron_right" size={20} color="CurrentColor" />
</Button>
<SidePeek

View File

@@ -39,7 +39,9 @@ export default function MapButton({ className = "" }: MapButtonProps) {
>
<Link href={mapUrl} scroll={true}>
<MaterialIcon icon="map" color="CurrentColor" />
{intl.formatMessage({ id: "See on map" })}
{intl.formatMessage({
defaultMessage: "See on map",
})}
</Link>
</Button>
)

View File

@@ -32,8 +32,12 @@ export default async function DestinationStaticMap({
}: StaticMapProps) {
const intl = await getIntl()
const altText = city
? intl.formatMessage({ id: "Map of the city" })
: intl.formatMessage({ id: "Map of the country" })
? intl.formatMessage({
defaultMessage: "Map of the city",
})
: intl.formatMessage({
defaultMessage: "Map of the country",
})
const coordinates = location
? { lat: location.latitude, lng: location.longitude }
: undefined

View File

@@ -65,12 +65,16 @@ export default function TopImages({ images, destinationName }: TopImageProps) {
onClick={() => setLightboxState({ open: true, activeIndex: 0 })}
className={styles.seeAllButton}
>
{intl.formatMessage({ id: "See all photos" })}
{intl.formatMessage({
defaultMessage: "See all photos",
})}
</Button>
<Lightbox
images={lightboxImages}
dialogTitle={intl.formatMessage(
{ id: "{title} - Image gallery" },
{
defaultMessage: "{title} - Image gallery",
},
{ title: destinationName }
)}
isOpen={lightboxState.open}

Some files were not shown because too many files have changed in this diff Show More