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 "ignoreRestSiblings": true
} }
], ],
"formatjs/enforce-description": ["warn", "anything"], // "formatjs/enforce-description": ["warn", "anything"],
"formatjs/enforce-default-message": ["error", "literal"], // "formatjs/enforce-default-message": ["error", "literal"],
"formatjs/enforce-placeholders": ["error"], "formatjs/enforce-placeholders": ["error"],
"formatjs/enforce-plural-rules": ["error"], "formatjs/enforce-plural-rules": ["error"],
"formatjs/no-literal-string-in-jsx": ["error"], "formatjs/no-literal-string-in-jsx": ["error"],
"formatjs/no-multiple-whitespaces": ["error"], "formatjs/no-multiple-whitespaces": ["error"],
"formatjs/no-multiple-plurals": ["error"], "formatjs/no-multiple-plurals": ["error"],
"formatjs/no-invalid-icu": ["error"], "formatjs/no-invalid-icu": ["error"],
"formatjs/no-id": ["error"], // "formatjs/no-id": ["error"],
"formatjs/no-complex-selectors": ["error"], "formatjs/no-complex-selectors": ["error"],
"formatjs/no-useless-message": ["error"], "formatjs/no-useless-message": ["error"],
"formatjs/prefer-pound-in-plural": ["error"] "formatjs/prefer-pound-in-plural": ["error"]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -128,12 +128,17 @@ export default async function DetailsPage({
<Alert <Alert
type={AlertTypeEnum.Alarm} type={AlertTypeEnum.Alarm}
variant="inline" variant="inline"
heading={intl.formatMessage({ id: "Room sold out" })} heading={intl.formatMessage({
defaultMessage: "Room sold out",
})}
text={intl.formatMessage({ 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={{ link={{
title: intl.formatMessage({ id: "Change room" }), title: intl.formatMessage({
defaultMessage: "Change room",
}),
url: `${selectRate(lang)}?${selectRoomParams.toString()}`, url: `${selectRate(lang)}?${selectRoomParams.toString()}`,
keepSearchParams: true, keepSearchParams: true,
}} }}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,18 +20,25 @@ export function AlreadyLinkedError() {
<SASModal> <SASModal>
<MaterialIcon icon="cancel" isFilled size={64} /> <MaterialIcon icon="cancel" isFilled size={64} />
<Typography variant="Title/Subtitle/lg"> <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>
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<p> <p>
{intl.formatMessage({ {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> </p>
</Typography> </Typography>
<Button theme="base" asChild> <Button theme="base" asChild>
<Link href={partnerSas[lang]}> <Link href={partnerSas[lang]}>
{intl.formatMessage({ id: "View your linked accounts" })} {intl.formatMessage({
defaultMessage: "View your linked accounts",
})}
</Link> </Link>
</Button> </Button>
<SASModalDivider /> <SASModalDivider />

View File

@@ -25,18 +25,25 @@ export function DateOfBirthError() {
size={64} size={64}
/> />
<Typography variant="Title/Subtitle/lg"> <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>
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<p> <p>
{intl.formatMessage({ {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> </p>
</Typography> </Typography>
<Button theme="base" asChild> <Button theme="base" asChild>
<Link href={profile[lang]}> <Link href={profile[lang]}>
{intl.formatMessage({ id: "View your details" })} {intl.formatMessage({
defaultMessage: "View your details",
})}
</Link> </Link>
</Button> </Button>
<SASModalDivider /> <SASModalDivider />

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -37,13 +37,14 @@ export default async function Page({
return ( return (
<GenericError <GenericError
title={intl.formatMessage({ title={intl.formatMessage({
id: "We could not connect your accounts", defaultMessage: "We could not connect your accounts",
})} })}
> >
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<p> <p>
{intl.formatMessage({ {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> </p>
</Typography> </Typography>

View File

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

View File

@@ -61,7 +61,11 @@ export function LinkAccountForm({
src="/_static/img/partner/sas/sas-campaign-logo.png" src="/_static/img/partner/sas/sas-campaign-logo.png"
/> />
<Typography variant="Title/Subtitle/lg"> <Typography variant="Title/Subtitle/lg">
<h3>{intl.formatMessage({ id: "Link your accounts" })}</h3> <h3>
{intl.formatMessage({
defaultMessage: "Link your accounts",
})}
</h3>
</Typography> </Typography>
</div> </div>
<div className={styles.dateOfBirth}> <div className={styles.dateOfBirth}>
@@ -70,19 +74,23 @@ export function LinkAccountForm({
{userDateOfBirth {userDateOfBirth
? intl.formatMessage( ? intl.formatMessage(
{ {
id: "Birth date: {dateOfBirth, date, ::MMMM d yyyy}", defaultMessage:
"Birth date: {dateOfBirth, date, ::MMMM d yyyy}",
}, },
{ {
dateOfBirth: new Date(userDateOfBirth), dateOfBirth: new Date(userDateOfBirth),
} }
) )
: intl.formatMessage({ id: "Birth date is missing" })} : intl.formatMessage({
defaultMessage: "Birth date is missing",
})}
</p> </p>
</Typography> </Typography>
<Typography variant="Label/xsRegular"> <Typography variant="Label/xsRegular">
<p className={styles.dateOfBirthDescription}> <p className={styles.dateOfBirthDescription}>
{intl.formatMessage({ {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> </p>
</Typography> </Typography>
@@ -93,7 +101,7 @@ export function LinkAccountForm({
variant="underscored" variant="underscored"
> >
{intl.formatMessage({ {intl.formatMessage({
id: "Edit your personal details", defaultMessage: "Edit your personal details",
})} })}
<MaterialIcon icon="arrow_forward" size={18} color="CurrentColor" /> <MaterialIcon icon="arrow_forward" size={18} color="CurrentColor" />
@@ -106,7 +114,7 @@ export function LinkAccountForm({
required: { required: {
value: true, value: true,
message: intl.formatMessage({ message: intl.formatMessage({
id: "You must accept the terms and conditions", defaultMessage: "You must accept the terms and conditions",
}), }),
}, },
disabled: !userDateOfBirth, disabled: !userDateOfBirth,
@@ -115,7 +123,7 @@ export function LinkAccountForm({
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<p> <p>
{intl.formatMessage({ {intl.formatMessage({
id: "I accept the terms and conditions", defaultMessage: "I accept the terms and conditions",
})} })}
</p> </p>
</Typography> </Typography>
@@ -124,7 +132,8 @@ export function LinkAccountForm({
<p className={styles.termsDescription}> <p className={styles.termsDescription}>
{intl.formatMessage( {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) => ( sasScandicTermsAndConditionsLink: (str) => (
@@ -150,7 +159,9 @@ export function LinkAccountForm({
type="submit" type="submit"
disabled={isPending || disableSubmit} disabled={isPending || disableSubmit}
> >
{intl.formatMessage({ id: "Link my accounts" })} {intl.formatMessage({
defaultMessage: "Link my accounts",
})}
</Button> </Button>
</div> </div>
</form> </form>

View File

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

View File

@@ -51,13 +51,16 @@ export default async function SASxScandicLoginPage({
const intentDescriptions: Record<Intent, string> = { const intentDescriptions: Record<Intent, string> = {
link: intl.formatMessage({ 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({ 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({ 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 }} style={{ marginTop: 16 }}
/> />
<Typography variant="Title/Subtitle/lg"> <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>
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<p style={{ textAlign: "center" }}> <p style={{ textAlign: "center" }}>
@@ -82,7 +89,8 @@ export default async function SASxScandicLoginPage({
<Footnote textAlign="center"> <Footnote textAlign="center">
{intl.formatMessage( {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) => ( loginLink: (str) => (

View File

@@ -56,7 +56,9 @@ export default function OneTimePasswordForm({
if (requestOtp.isError) { if (requestOtp.isError) {
const cause = requestOtp.error?.data?.cause as RequestOtpError 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) const body = getRequestErrorBody(intl, cause?.errorCode)
return ( return (
@@ -125,11 +127,12 @@ export default function OneTimePasswordForm({
const errorMessages: Record<OtpError, ReactNode> = { const errorMessages: Record<OtpError, ReactNode> = {
invalidCode: intl.formatMessage({ invalidCode: intl.formatMessage({
id: "The code you've entered is incorrect.", defaultMessage: "The code you've entered is incorrect.",
}), }),
expiredCode: intl.formatMessage( 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, resendOtpLink: getResendOtpLink,
@@ -189,7 +192,8 @@ export default function OneTimePasswordForm({
<p> <p>
{intl.formatMessage( {intl.formatMessage(
{ {
id: "Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>", defaultMessage:
"Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>",
}, },
{ {
resendOtpLink: getResendOtpLink, resendOtpLink: getResendOtpLink,
@@ -226,11 +230,11 @@ const getRequestErrorBody = (
switch (errorCode) { switch (errorCode) {
case "TOO_MANY_REQUESTS": case "TOO_MANY_REQUESTS":
return intl.formatMessage({ return intl.formatMessage({
id: "Too many requests. Please try again later.", defaultMessage: "Too many requests. Please try again later.",
}) })
default: default:
return intl.formatMessage({ 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"> <Typography variant="Body/Paragraph/mdRegular">
<p> <p>
{intl.formatMessage({ {intl.formatMessage({
id: "Hang tight...", defaultMessage: "Hang tight...",
})} })}
</p> </p>
</Typography> </Typography>

View File

@@ -110,19 +110,22 @@ export default async function SASxScandicOneTimePasswordPage({
const intentDescriptions: Record<Intent, ReactNode> = { const intentDescriptions: Record<Intent, ReactNode> = {
link: intl.formatMessage( 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 } { maskedContactInfo }
), ),
unlink: intl.formatMessage( 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 } { maskedContactInfo }
), ),
transfer: intl.formatMessage( 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 } { maskedContactInfo }
), ),
@@ -130,10 +133,12 @@ export default async function SASxScandicOneTimePasswordPage({
return ( return (
<OneTimePasswordForm <OneTimePasswordForm
heading={intl.formatMessage({ id: "Verification code" })} heading={intl.formatMessage({
defaultMessage: "Verification code",
})}
ingress={intentDescriptions[intent]} ingress={intentDescriptions[intent]}
footnote={intl.formatMessage({ footnote={intl.formatMessage({
id: "This verifcation is needed for additional security.", defaultMessage: "This verifcation is needed for additional security.",
})} })}
otpLength={6} otpLength={6}
onSubmit={handleOtpVerified} onSubmit={handleOtpVerified}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,7 +12,11 @@ export default async function HowItWorks({ dynamic_content }: HowItWorksProps) {
return ( return (
<SectionWrapper dynamic_content={dynamic_content}> <SectionWrapper dynamic_content={dynamic_content}>
<section className={styles.container}> <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> </section>
</SectionWrapper> </SectionWrapper>
) )

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,10 +18,16 @@ export default function CopyButton({ membershipNumber }: CopyButtonProps) {
try { try {
navigator.clipboard.writeText(membershipNumber) navigator.clipboard.writeText(membershipNumber)
toast.success( toast.success(
intl.formatMessage({ id: "Membership ID copied to clipboard" }) intl.formatMessage({
defaultMessage: "Membership ID copied to clipboard",
})
) )
} catch { } 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 ( return (
<div className={classNames}> <div className={classNames}>
<Caption color="pale"> <Caption color="pale">
{intl.formatMessage({ id: "Membership ID:" })} {intl.formatMessage({
defaultMessage: "Membership ID:",
})}
</Caption> </Caption>
<span className={styles.icon}> <span className={styles.icon}>
<Caption className={styles.icon} color="pale" asChild> <Caption className={styles.icon} color="pale" asChild>
<code data-hj-suppress> <code data-hj-suppress>
{membership?.membershipNumber ?? intl.formatMessage({ id: "N/A" })} {membership?.membershipNumber ??
intl.formatMessage({
defaultMessage: "N/A",
})}
</code> </code>
</Caption> </Caption>
{membership?.membershipNumber && ( {membership?.membershipNumber && (

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,17 +17,24 @@ export function UnlinkSAS() {
return ( return (
<Dialog <Dialog
titleText={intl.formatMessage({ 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({ 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`} proceedHref={`/${params.lang}/sas-x-scandic/login?intent=unlink`}
trigger={ trigger={
<Button intent="text" theme="base"> <Button intent="text" theme="base">
{intl.formatMessage({ id: "Unlink accounts" })} {intl.formatMessage({
defaultMessage: "Unlink accounts",
})}
<MaterialIcon icon="chevron_right" size={20} color="CurrentColor" /> <MaterialIcon icon="chevron_right" size={20} color="CurrentColor" />
</Button> </Button>
} }

View File

@@ -58,7 +58,8 @@ export default async function SASLinkedAccount({
<p className={styles.caption}> <p className={styles.caption}>
<MaterialIcon icon="info" size={20} /> <MaterialIcon icon="info" size={20} />
{intl.formatMessage({ {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> </p>
</Typography> </Typography>
@@ -96,20 +97,37 @@ async function MatchedAccountInfo() {
<section className={styles.matchedAccountSection}> <section className={styles.matchedAccountSection}>
<div className={styles.accountDetails}> <div className={styles.accountDetails}>
<div className={styles.stack}> <div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Linked account" })}</Label> <Label>
{intl.formatMessage({
defaultMessage: "Linked account",
})}
</Label>
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">
<p>{intl.formatMessage({ id: "SAS EuroBonus" })}</p> <p>
{intl.formatMessage({
defaultMessage: "SAS EuroBonus",
})}
</p>
</Typography> </Typography>
</div> </div>
<div className={styles.stack}> <div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Level" })}</Label> <Label>
{intl.formatMessage({
defaultMessage: "Level",
})}
</Label>
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">
<p>{sasLevelName}</p> <p>{sasLevelName}</p>
</Typography> </Typography>
</div> </div>
<div className={cx(styles.stack, styles.accountMemberNumber)}> <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"> <Typography variant="Body/Paragraph/mdBold">
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<p className={styles.textRight}>EB{sasMembershipNumber}</p> <p className={styles.textRight}>EB{sasMembershipNumber}</p>
</Typography> </Typography>
</div> </div>
@@ -137,17 +155,33 @@ async function MatchedAccountInfoSkeleton() {
<section className={styles.matchedAccountSection}> <section className={styles.matchedAccountSection}>
<div className={styles.accountDetails}> <div className={styles.accountDetails}>
<div className={styles.stack}> <div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Linked account" })}</Label> <Label>
{intl.formatMessage({
defaultMessage: "Linked account",
})}
</Label>
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">
<p>{intl.formatMessage({ id: "SAS EuroBonus" })}</p> <p>
{intl.formatMessage({
defaultMessage: "SAS EuroBonus",
})}
</p>
</Typography> </Typography>
</div> </div>
<div className={styles.stack}> <div className={styles.stack}>
<Label>{intl.formatMessage({ id: "Level" })}</Label> <Label>
{intl.formatMessage({
defaultMessage: "Level",
})}
</Label>
<SkeletonShimmer width="6ch" height="24px" /> <SkeletonShimmer width="6ch" height="24px" />
</div> </div>
<div className={cx(styles.stack, styles.accountMemberNumber)}> <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" /> <SkeletonShimmer width="10ch" height="24px" />
</div> </div>
</div> </div>
@@ -188,19 +222,22 @@ async function TierMatchMessage({
const messageMap: Record<MatchState, ReactNode> = { const messageMap: Record<MatchState, ReactNode> = {
boostedBySAS: intl.formatMessage( 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 messageValues
), ),
boostedByScandic: intl.formatMessage( 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 messageValues
), ),
noBoost: intl.formatMessage( 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 messageValues
), ),
@@ -214,7 +251,11 @@ async function TierMatchMessage({
return ( return (
<div className={styles.stack}> <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.tierMatchText}>
<div className={styles.iconWrapper}>{iconMap[matchState]}</div> <div className={styles.iconWrapper}>{iconMap[matchState]}</div>
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
@@ -230,7 +271,11 @@ async function TierMatchMessageSkeleton() {
return ( return (
<div className={styles.stack}> <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.tierMatchText}>
<SkeletonShimmer width="250px" height="24px" /> <SkeletonShimmer width="250px" height="24px" />
</div> </div>
@@ -256,7 +301,11 @@ async function TierMatchExpiration({
return ( return (
<div className={styles.stack}> <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"> <Typography variant="Body/Paragraph/mdBold">
<p> <p>
{matchState === "boostedBySAS" {matchState === "boostedBySAS"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -38,7 +38,8 @@ export default function CityListing() {
<Subtitle type="two"> <Subtitle type="two">
{intl.formatMessage( {intl.formatMessage(
{ {
id: "{count, plural, one {# Location} other {# Locations}}", defaultMessage:
"{count, plural, one {# Location} other {# Locations}}",
}, },
{ count: activeCities.length } { count: activeCities.length }
)} )}
@@ -48,9 +49,12 @@ export default function CityListing() {
{activeCities.length === 0 ? ( {activeCities.length === 0 ? (
<Alert <Alert
type={AlertTypeEnum.Info} type={AlertTypeEnum.Info}
heading={intl.formatMessage({ id: "No matching locations found" })} heading={intl.formatMessage({
defaultMessage: "No matching locations found",
})}
text={intl.formatMessage({ 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}> <div className={styles.header}>
<Body> <Body>
{intl.formatMessage( {intl.formatMessage(
{ id: "{count} hotels" }, {
defaultMessage: "{count} hotels",
},
{ count: visibleHotels.length } { count: visibleHotels.length }
)} )}
</Body> </Body>
@@ -72,9 +74,12 @@ export default function HotelList() {
{activeHotels.length === 0 ? ( {activeHotels.length === 0 ? (
<Alert <Alert
type={AlertTypeEnum.Info} type={AlertTypeEnum.Info}
heading={intl.formatMessage({ id: "No matching hotels found" })} heading={intl.formatMessage({
defaultMessage: "No matching hotels found",
})}
text={intl.formatMessage({ 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 fill
sizes="(min-width: 768px) 350px, 100vw" sizes="(min-width: 768px) 350px, 100vw"
title={intl.formatMessage( title={intl.formatMessage(
{ id: "{title} - Image gallery" }, {
defaultMessage: "{title} - Image gallery",
},
{ title: hotel.name } { title: hotel.name }
)} )}
/> />
@@ -93,7 +95,9 @@ export default function HotelListItem(data: DestinationPagesHotelData) {
<Divider variant="vertical" color="beige" /> <Divider variant="vertical" color="beige" />
<Caption color="uiTextPlaceholder"> <Caption color="uiTextPlaceholder">
{intl.formatMessage( {intl.formatMessage(
{ id: "{number} km to city center" }, {
defaultMessage: "{number} km to city center",
},
{ {
number: getSingleDecimal( number: getSingleDecimal(
hotel.location.distanceToCentre / 1000 hotel.location.distanceToCentre / 1000
@@ -120,7 +124,9 @@ export default function HotelListItem(data: DestinationPagesHotelData) {
<div className={styles.ctaWrapper}> <div className={styles.ctaWrapper}>
<Button intent="tertiary" theme="base" size="small" asChild> <Button intent="tertiary" theme="base" size="small" asChild>
<Link href={url}> <Link href={url}>
{intl.formatMessage({ id: "See hotel details" })} {intl.formatMessage({
defaultMessage: "See hotel details",
})}
</Link> </Link>
</Button> </Button>
</div> </div>

View File

@@ -29,7 +29,9 @@ export default function CityList() {
<div className={styles.header}> <div className={styles.header}>
<Body> <Body>
{intl.formatMessage( {intl.formatMessage(
{ id: "{count} destinations" }, {
defaultMessage: "{count} destinations",
},
{ count: activeCities.length } { count: activeCities.length }
)} )}
</Body> </Body>
@@ -38,9 +40,12 @@ export default function CityList() {
{activeCities.length === 0 ? ( {activeCities.length === 0 ? (
<Alert <Alert
type={AlertTypeEnum.Info} type={AlertTypeEnum.Info}
heading={intl.formatMessage({ id: "No matching locations found" })} heading={intl.formatMessage({
defaultMessage: "No matching locations found",
})}
text={intl.formatMessage({ 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} images={galleryImages}
fill fill
title={intl.formatMessage( title={intl.formatMessage(
{ id: "{title} - Image gallery" }, {
defaultMessage: "{title} - Image gallery",
},
{ title: city.cityName } { title: city.cityName }
)} )}
/> />
@@ -47,7 +49,9 @@ export default function CityListItem({ city }: CityListItemProps) {
<Button intent="tertiary" theme="base" size="small" asChild> <Button intent="tertiary" theme="base" size="small" asChild>
<Link href={city.url}> <Link href={city.url}>
{intl.formatMessage( {intl.formatMessage(
{ id: "Explore {city}" }, {
defaultMessage: "Explore {city}",
},
{ city: city.cityName } { city: city.cityName }
)} )}
</Link> </Link>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -45,13 +45,16 @@ export function Results({
<> <>
<Typography variant="Body/Paragraph/mdBold"> <Typography variant="Body/Paragraph/mdBold">
<Header className={styles.noResultsLabel}> <Header className={styles.noResultsLabel}>
{intl.formatMessage({ id: "No results" })} {intl.formatMessage({
defaultMessage: "No results",
})}
</Header> </Header>
</Typography> </Typography>
<Typography variant="Body/Paragraph/mdRegular"> <Typography variant="Body/Paragraph/mdRegular">
<span className={styles.noResultsDescription}> <span className={styles.noResultsDescription}>
{intl.formatMessage({ {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> </span>
</Typography> </Typography>

View File

@@ -158,7 +158,9 @@ export function JumpToClient<T extends JumpToData>({
type: "clearHistory", type: "clearHistory",
closesModal: false, closesModal: false,
icon: <MaterialIcon icon="delete" color="CurrentColor" />, 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 <input
type="text" type="text"
placeholder={intl.formatMessage({ placeholder={intl.formatMessage({
id: "Find hotels and destinations", defaultMessage: "Find hotels and destinations",
})} })}
className={styles.formInput} className={styles.formInput}
/> />

View File

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

View File

@@ -23,13 +23,22 @@ export default async function HotelDataContainer({
const sortItems: SortItem[] = [ const sortItems: SortItem[] = [
{ {
label: intl.formatMessage({ id: "Distance to city center" }), label: intl.formatMessage({
defaultMessage: "Distance to city center",
}),
value: SortOption.Distance, value: SortOption.Distance,
isDefault: true, 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, value: SortOption.TripAdvisorRating,
}, },
] ]

View File

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

View File

@@ -51,7 +51,7 @@ export default function HotelListing() {
<Subtitle type="two"> <Subtitle type="two">
{intl.formatMessage( {intl.formatMessage(
{ {
id: "{count, plural, one {# Hotel} other {# Hotels}}", defaultMessage: "{count, plural, one {# Hotel} other {# Hotels}}",
}, },
{ count: activeHotels.length } { count: activeHotels.length }
)} )}
@@ -68,7 +68,9 @@ export default function HotelListing() {
> >
<Link href={mapUrl}> <Link href={mapUrl}>
<MaterialIcon icon="map" color="CurrentColor" /> <MaterialIcon icon="map" color="CurrentColor" />
{intl.formatMessage({ id: "See on map" })} {intl.formatMessage({
defaultMessage: "See on map",
})}
</Link> </Link>
</Button> </Button>
)} )}
@@ -78,9 +80,12 @@ export default function HotelListing() {
{activeHotels.length === 0 ? ( {activeHotels.length === 0 ? (
<Alert <Alert
type={AlertTypeEnum.Info} type={AlertTypeEnum.Info}
heading={intl.formatMessage({ id: "No matching hotels found" })} heading={intl.formatMessage({
defaultMessage: "No matching hotels found",
})}
text={intl.formatMessage({ 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" variant="icon"
className={styles.closeButton} className={styles.closeButton}
onClick={handleClose} onClick={handleClose}
aria-label={intl.formatMessage({ id: "Close" })} aria-label={intl.formatMessage({
defaultMessage: "Close",
})}
> >
<MaterialIcon <MaterialIcon
icon="close" icon="close"
@@ -120,7 +122,9 @@ export default function HotelMapCard({
size="small" size="small"
> >
<Link href={url}> <Link href={url}>
{intl.formatMessage({ id: "See hotel information" })} {intl.formatMessage({
defaultMessage: "See hotel information",
})}
</Link> </Link>
</Button> </Button>
)} )}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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