Merged in feat/lokalise-rebuild (pull request #2993)

Feat/lokalise rebuild

* chore(lokalise): update translation ids

* chore(lokalise): easier to switch between projects

* chore(lokalise): update translation ids

* .

* .

* .

* .

* .

* .

* chore(lokalise): update translation ids

* chore(lokalise): update translation ids

* .

* .

* .

* chore(lokalise): update translation ids

* chore(lokalise): update translation ids

* .

* .

* chore(lokalise): update translation ids

* chore(lokalise): update translation ids

* chore(lokalise): new translations

* merge

* switch to errors for missing id's

* merge

* sync translations


Approved-by: Linus Flood
This commit is contained in:
Joakim Jäderberg
2025-10-22 11:00:03 +00:00
parent bdfe2ab213
commit aafad9781f
499 changed files with 93363 additions and 99164 deletions

View File

@@ -1,54 +0,0 @@
import { BookingWidget } from "@scandic-hotels/booking-flow/BookingWidget"
import { parseBookingWidgetSearchParams } from "@scandic-hotels/booking-flow/utils/url"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { bookingFlowConfig } from "@/constants/bookingFlowConfig"
import { serverClient } from "@/lib/trpc"
import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import { ClientComponent } from "../../../components/ClientComponent"
type SearchParams<S = object> = {
searchParams: Promise<S & { [key: string]: string }>
}
export default async function Debug(props: SearchParams) {
const searchParams = await props.searchParams
const intl = await getIntl()
const lang = await getLang()
const caller = await serverClient()
const destinations = await caller.autocomplete.destinations({
lang,
includeTypes: ["hotels"],
query: "Göteborg",
})
const hotel = destinations.hits.hotels[0].name
const booking = parseBookingWidgetSearchParams(searchParams)
return (
<div style={{ padding: "20px" }}>
<Typography>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<p>from booking-flow package:</p>
</Typography>
<BookingWidget booking={booking} lang={lang} config={bookingFlowConfig} />
<hr />
<Typography variant="Title/Decorative/lg">
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<p>hello world with data: {hotel}</p>
</Typography>
<Typography>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
<p>
translated:
{intl.formatMessage({ defaultMessage: "Map of the city" })}
</p>
</Typography>
<hr />
<ClientComponent />
</div>
)
}

View File

@@ -36,6 +36,7 @@ export async function generateMetadata({
const title = intl.formatMessage(
{
id: "alternativeHotels.title",
defaultMessage: "Alternatives for {value}",
},
{

View File

@@ -36,6 +36,7 @@ export async function generateMetadata({
const title = intl.formatMessage(
{
id: "alternativeHotels.title",
defaultMessage: "Alternatives for {value}",
},
{

View File

@@ -1,27 +0,0 @@
/* eslint-disable formatjs/no-literal-string-in-jsx */
"use client"
import { useIntl } from "react-intl"
import { Lang } from "@scandic-hotels/common/constants/language"
import { trpc } from "@scandic-hotels/trpc/client"
export function ClientComponent() {
const intl = useIntl()
const { data, isLoading } = trpc.autocomplete.destinations.useQuery({
lang: Lang.en,
includeTypes: ["hotels"],
query: "Malmö",
})
return (
<div>
<p>client component</p>
<p>Data: {JSON.stringify(data?.hits?.hotels[0]?.name)}</p>
<p>Is loading: {isLoading ? "Yes" : "No"}</p>
<p>Translated text: </p>
{intl.formatMessage({
defaultMessage: "All-day breakfast",
})}
</div>
)
}

View File

@@ -33,6 +33,7 @@ export async function Footer() {
<p>
{intl.formatMessage(
{
id: "footer.copyright",
defaultMessage:
"© {currentYear} Scandic Hotels all rights reserved",
},

View File

@@ -180,6 +180,7 @@ function LanguageSwitcherContent({
<Typography variant={"Title/Subtitle/md"}>
<h3 className={styles.title}>
{intl.formatMessage({
id: "common.selectYourLanguage",
defaultMessage: "Select your language",
})}
</h3>

View File

@@ -38,17 +38,26 @@ export function FooterMenu() {
href={routeToScandicWeb(customerService)[lang]}
className={styles.link}
>
{intl.formatMessage({ defaultMessage: "Contact us" })}
{intl.formatMessage({
id: "common.contactUs",
defaultMessage: "Contact us",
})}
</Link>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<Link href={routeToScandicWeb(faq)[lang]} className={styles.link}>
{intl.formatMessage({ defaultMessage: "FAQ" })}
{intl.formatMessage({
id: "common.faq",
defaultMessage: "FAQ",
})}
</Link>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
<Link href={routeToScandicWeb(rates)[lang]} className={styles.link}>
{intl.formatMessage({ defaultMessage: "Rates" })}
{intl.formatMessage({
id: "partnerSas.footer.rates",
defaultMessage: "Rates",
})}
</Link>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
@@ -56,7 +65,10 @@ export function FooterMenu() {
href={routeToScandicWeb(policies)[lang]}
className={styles.link}
>
{intl.formatMessage({ defaultMessage: "Policies" })}
{intl.formatMessage({
id: "partnerSas.footer.policies",
defaultMessage: "Policies",
})}
</Link>
</Typography>
<Typography variant="Body/Paragraph/mdRegular">
@@ -64,7 +76,10 @@ export function FooterMenu() {
href={routeToScandicWeb(sasEuroBonus)[lang]}
className={styles.link}
>
{intl.formatMessage({ defaultMessage: "SAS EuroBonus" })}
{intl.formatMessage({
id: "partnerSas.sasEuroBonus",
defaultMessage: "SAS EuroBonus",
})}
</Link>
</Typography>
</div>

View File

@@ -14,9 +14,11 @@ export function MobileMenu({ children }: React.PropsWithChildren) {
const intl = useIntl()
const closeMsg = intl.formatMessage({
id: "header.closeMenu",
defaultMessage: "Close menu",
})
const openMsg = intl.formatMessage({
id: "header.openMenu",
defaultMessage: "Open menu",
})
@@ -41,6 +43,7 @@ export function MobileMenu({ children }: React.PropsWithChildren) {
<Dialog
className={styles.dialog}
aria-label={intl.formatMessage({
id: "header.menu",
defaultMessage: "Menu",
})}
>

View File

@@ -38,7 +38,10 @@ export function NavigationMenu({ isMobile = false }: { isMobile?: boolean }) {
{isMobile ? null : (
<MaterialIcon icon="call" size={16} color={"CurrentColor"} />
)}
{intl.formatMessage({ defaultMessage: "Contact us" })}
{intl.formatMessage({
id: "common.contactUs",
defaultMessage: "Contact us",
})}
</Link>
</Typography>

View File

@@ -65,7 +65,12 @@ export function UserMenu({ isMobile = false }: { isMobile?: boolean }) {
: "Body/Supporting text (caption)/smRegular"
}
>
<span>{intl.formatMessage({ defaultMessage: "Log in" })}</span>
<span>
{intl.formatMessage({
id: "partnerSas.menu.login",
defaultMessage: "Log in",
})}
</span>
</Typography>
)}
</a>
@@ -102,6 +107,7 @@ export function UserMenu({ isMobile = false }: { isMobile?: boolean }) {
type="button"
className={styles.closeModalBtn}
aria-label={intl.formatMessage({
id: "header.closeMenu",
defaultMessage: "Close menu",
})}
onPress={close}
@@ -158,7 +164,10 @@ function UserMenuContent({
<Typography variant={"Title/Subtitle/md"}>
<h3>
{intl.formatMessage(
{ defaultMessage: `Hi {fName} {lName}!` },
{
id: "partnerSas.mobileMenu.greeting",
defaultMessage: `Hi {fName} {lName}!`,
},
{ fName: firstName, lName: lastName }
)}
</h3>
@@ -166,7 +175,12 @@ function UserMenuContent({
)}
<p className={styles.pointsDetails}>
<Typography variant="Title/Overline/sm">
<span>{intl.formatMessage({ defaultMessage: "EB Points" })}</span>
<span>
{intl.formatMessage({
id: "partnerSas.menu.ebPointsTitle",
defaultMessage: "EB Points",
})}
</span>
</Typography>
<Typography variant="Title/Overline/sm">
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
@@ -176,6 +190,7 @@ function UserMenuContent({
<span>
{intl.formatMessage(
{
id: "partnerSas.menu.ebPoints",
defaultMessage: "{points} points",
},
{
@@ -190,7 +205,10 @@ function UserMenuContent({
<Typography variant={"Link/md"} className={styles.logoutLink}>
{/* Link triggers rsc which doesn't reload complete page and shows logged in even after logout */}
<a href={`/${lang}/logout`}>
{intl.formatMessage({ defaultMessage: "Log out" })}
{intl.formatMessage({
id: "common.logOut",
defaultMessage: "Log out",
})}
</a>
</Typography>
</>

View File

@@ -100,7 +100,7 @@ export default defineConfig([
"formatjs/no-multiple-whitespaces": ["error"],
"formatjs/no-multiple-plurals": ["error"],
"formatjs/no-invalid-icu": ["error"],
"formatjs/no-id": ["error"],
"formatjs/enforce-id": ["error"],
"formatjs/no-complex-selectors": ["error"],
"formatjs/no-useless-message": ["error"],
"formatjs/prefer-pound-in-plural": ["error"],

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{
"WcBpRV": [
"selectRate.roomSizeMinToMax": [
{
"type": 1,
"value": "roomSizeMin"

File diff suppressed because it is too large Load Diff

View File

@@ -63,6 +63,7 @@ export const editProfile = protectedServerActionProcedure
message: issue.message,
})),
message: intl.formatMessage({
id: "editProfile.validationErrorMessage",
defaultMessage:
"An error occurred when trying to update your profile.",
}),
@@ -84,6 +85,7 @@ export const editProfile = protectedServerActionProcedure
data: input,
issues: [],
message: intl.formatMessage({
id: "editProfile.validationErrorMessage",
defaultMessage:
"An error occurred when trying to update your profile.",
}),
@@ -143,6 +145,7 @@ export const editProfile = protectedServerActionProcedure
return {
data: input,
message: intl.formatMessage({
id: "editProfile.successMessage",
defaultMessage: "Successfully updated profile!",
}),
status: Status.success,
@@ -179,6 +182,7 @@ export const editProfile = protectedServerActionProcedure
data: input,
issues: [],
message: intl.formatMessage({
id: "editProfile.validationErrorMessage",
defaultMessage:
"An error occurred when trying to update your profile.",
}),
@@ -216,6 +220,7 @@ export const editProfile = protectedServerActionProcedure
message: issue.message,
})),
message: intl.formatMessage({
id: "editProfile.validationErrorMessage",
defaultMessage:
"An error occurred when trying to update your profile.",
}),
@@ -226,6 +231,7 @@ export const editProfile = protectedServerActionProcedure
return {
data: validatedData.data,
message: intl.formatMessage({
id: "editProfile.successMessage",
defaultMessage: "Successfully updated profile!",
}),
status: Status.success,

View File

@@ -25,6 +25,7 @@ export default function Error({
<strong>
{intl.formatMessage(
{
id: "myPages.breadcrumbsError",
defaultMessage: "Breadcrumbs failed for this page ({errorId})",
},
{

View File

@@ -25,6 +25,7 @@ export default function Error({
<strong>
{intl.formatMessage(
{
id: "myPages.errorLoadingPage",
defaultMessage: "Error loading this page ({errorId})",
},
{

View File

@@ -64,6 +64,7 @@ export default async function MyPages({}: PageArgs<
) : (
<p>
{intl.formatMessage({
id: "myPages.missingContent",
defaultMessage: "No content published",
})}
</p>

View File

@@ -28,6 +28,7 @@ export default function Error({
<strong>
{intl.formatMessage(
{
id: "destinationOverviewPage.errorOccurred",
defaultMessage: "An error occurred ({errorId})",
},
{

View File

@@ -38,6 +38,7 @@ export async function generateMetadata({
const title = intl.formatMessage(
{
id: "alternativeHotels.title",
defaultMessage: "Alternatives for {value}",
},
{

View File

@@ -40,6 +40,7 @@ export async function generateMetadata({
const title = intl.formatMessage(
{
id: "alternativeHotels.title",
defaultMessage: "Alternatives for {value}",
},
{

View File

@@ -17,6 +17,9 @@ export async function generateMetadata() {
const intl = await getIntl()
return {
title: intl.formatMessage({ defaultMessage: "Find booking" }),
title: intl.formatMessage({
id: "booking.findBooking",
defaultMessage: "Find booking",
}),
}
}

View File

@@ -63,6 +63,7 @@ export default function Error({
<section className={styles.layout}>
<div className={styles.content}>
{intl.formatMessage({
id: "errorMessage.somethingWentWrong",
defaultMessage: "Something went wrong!",
})}
{env.NEXT_PUBLIC_NODE_ENV === "development" && (

View File

@@ -47,6 +47,7 @@ export default function LinkEmploymentErrorPage() {
/>
<span className={styles.navBackText}>
{intl.formatMessage({
id: "common.goBack",
defaultMessage: "Go back",
})}
</span>
@@ -69,6 +70,7 @@ export default function LinkEmploymentErrorPage() {
<Typography variant="Title/Subtitle/md">
<h3>
{intl.formatMessage({
id: "linkEmploymentError.contactHeading",
defaultMessage: "Contact our member service",
})}
</h3>
@@ -102,9 +104,11 @@ export default function LinkEmploymentErrorPage() {
const getErrorContent = (error: string | null, intl: IntlShape) => {
const defaultErrorContent = {
heading: intl.formatMessage({
id: "linkEmploymentError.genericHeading",
defaultMessage: "Your account could not be connected",
}),
message: intl.formatMessage({
id: "linkEmploymentError.genericMessage",
defaultMessage:
"We couldn't connect your accounts. Please contact us and we'll help you resolve this.",
}),
@@ -114,9 +118,11 @@ const getErrorContent = (error: string | null, intl: IntlShape) => {
case "unable_to_verify_employee_id":
return {
heading: intl.formatMessage({
id: "linkEmploymentError.notEligibleHeading",
defaultMessage: "You're not eligible for employee benefits",
}),
message: intl.formatMessage({
id: "linkEmploymentError.notEligibleMessage",
defaultMessage:
"This may be because your employment has not yet started, has ended, or you are a consultant. If you believe this is an error, please contact us for assistance.",
}),
@@ -124,10 +130,12 @@ const getErrorContent = (error: string | null, intl: IntlShape) => {
case "employee_id_already_linked":
return {
heading: intl.formatMessage({
id: "linkEmploymentError.alreadyLinkedHeading",
defaultMessage:
"Employee number already linked to another Scandic Friends membership.",
}),
message: intl.formatMessage({
id: "linkEmploymentError.alreadyLinkedMessage",
defaultMessage:
"If you believe this is an error, please contact us for assistance.",
}),

View File

@@ -25,6 +25,7 @@ export function AlreadyLinkedError() {
<Typography variant="Title/Subtitle/lg">
<h1>
{intl.formatMessage({
id: "linkEuroBonusAccount.alreadyLinkedHeading",
defaultMessage:
"Your Scandic Friends and SAS EuroBonus accounts are already connected",
})}
@@ -33,6 +34,7 @@ export function AlreadyLinkedError() {
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "linkEuroBonusAccount.alreadyLinkedMessage",
defaultMessage:
"Great news! You can go straight to My Pages to explore all your perks and rewards!",
})}
@@ -46,6 +48,7 @@ export function AlreadyLinkedError() {
size={"Large"}
>
{intl.formatMessage({
id: "linkEuroBonusAccount.viewLinkedAccountsButton",
defaultMessage: "View your linked accounts",
})}
</ButtonLink>

View File

@@ -26,6 +26,7 @@ export function DateOfBirthError() {
<Typography variant="Title/Subtitle/lg">
<h1>
{intl.formatMessage({
id: "linkEuroBonusAccount.dateOfBirthErrorHeading",
defaultMessage: "Date of birth not matching",
})}
</h1>
@@ -33,6 +34,7 @@ export function DateOfBirthError() {
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "linkEuroBonusAccount.couldntConnectAccountsErrorMessage",
defaultMessage:
"We couldnt connect your accounts. Please contact us and well help you resolve this.",
})}
@@ -41,6 +43,7 @@ export function DateOfBirthError() {
<Button theme="base" asChild>
<Link href={profile[lang]}>
{intl.formatMessage({
id: "linkEuroBonusAccount.viewYourDetailsButton",
defaultMessage: "View your details",
})}
</Link>

View File

@@ -19,6 +19,7 @@ export function SASModalDivider() {
<Typography variant="Body/Paragraph/mdRegular">
<span>
{intl.formatMessage({
id: "common.or",
defaultMessage: "or",
})}
</span>
@@ -31,6 +32,7 @@ export function SASModalContactBlock() {
const intl = useIntl()
const phone = intl.formatMessage({
id: "linkEuroBonusAccount.contactPhoneNumber",
defaultMessage: "+46 8 517 517 00",
})
@@ -39,6 +41,7 @@ export function SASModalContactBlock() {
<Typography variant="Title/Subtitle/md">
<h4 className={styles.contactBlockTitle}>
{intl.formatMessage({
id: "common.contactUs",
defaultMessage: "Contact us",
})}
</h4>

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,9 +13,10 @@ import { UnlinkError } from "../components/UnlinkError"
import type { LangParams, PageArgs, SearchParams } from "@/types/params"
export default async function Page(
props: PageArgs<LangParams> & SearchParams<{ errorCode?: "dateOfBirthMismatch" }>
props: PageArgs<LangParams> &
SearchParams<{ errorCode?: "dateOfBirthMismatch" }>
) {
const searchParams = await props.searchParams;
const searchParams = await props.searchParams
const intl = await getIntl()
const { errorCode } = searchParams
@@ -43,12 +44,14 @@ export default async function Page(
return (
<GenericError
title={intl.formatMessage({
id: "linkEuroBonusAccount.genericErrorTitle",
defaultMessage: "We could not connect your accounts",
})}
>
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "linkEuroBonusAccount.couldntConnectAccountsErrorMessage",
defaultMessage:
"We couldnt connect your accounts. Please contact us and well help you resolve this.",
})}

View File

@@ -33,11 +33,13 @@ export default async function SasXScandicLayout(
<ArrowLeft height={20} width={20} />
<span className={styles.long}>
{intl.formatMessage({
id: "header.backToScandicHotelsCom",
defaultMessage: "Back to scandichotels.com",
})}
</span>
<span className={styles.short}>
{intl.formatMessage({
id: "common.back",
defaultMessage: "Back",
})}
</span>
@@ -55,6 +57,7 @@ async function MainMenuLogo() {
return (
<Logo
alt={intl.formatMessage({
id: "header.backToScandicHotelsCom",
defaultMessage: "Back to scandichotels.com",
})}
/>

View File

@@ -62,6 +62,7 @@ export function LinkAccountForm({
<Typography variant="Title/Subtitle/lg">
<h3>
{intl.formatMessage({
id: "linkEuroBonusAccount.linkYourAccountsTitle",
defaultMessage: "Link your accounts",
})}
</h3>
@@ -73,6 +74,7 @@ export function LinkAccountForm({
{userDateOfBirth
? intl.formatMessage(
{
id: "linkEuroBonusAccount.birthDate",
defaultMessage:
"Birth date: {dateOfBirth, date, ::MMMM d yyyy}",
},
@@ -81,6 +83,7 @@ export function LinkAccountForm({
}
)
: intl.formatMessage({
id: "linkEuroBonusAccount.birthDateMissing",
defaultMessage: "Birth date is missing",
})}
</p>
@@ -88,6 +91,7 @@ export function LinkAccountForm({
<Typography variant="Label/xsRegular">
<p className={styles.dateOfBirthDescription}>
{intl.formatMessage({
id: "linkEuroBonusAccount.birthDateDescription",
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.",
})}
@@ -100,6 +104,7 @@ export function LinkAccountForm({
textDecoration="underline"
>
{intl.formatMessage({
id: "linkEuroBonusAccount.editPersonalDetailsLink",
defaultMessage: "Edit your personal details",
})}
@@ -113,6 +118,7 @@ export function LinkAccountForm({
required: {
value: true,
message: intl.formatMessage({
id: "common.mustAcceptTermsError",
defaultMessage: "You must accept the terms and conditions",
}),
},
@@ -122,6 +128,7 @@ export function LinkAccountForm({
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "booking.acceptBookingTerms",
defaultMessage: "I accept the booking and cancellation terms",
})}
</p>
@@ -131,6 +138,7 @@ export function LinkAccountForm({
<p className={styles.termsDescription}>
{intl.formatMessage(
{
id: "linkEuroBonusAccount.termsAndConditionsDescription",
defaultMessage:
"By linking your accounts you accept the <sasScandicTermsAndConditionsLink>Terms & Conditions for Scandic Friends and SAS EuroBonus Account Linking</sasScandicTermsAndConditionsLink>.",
},
@@ -158,6 +166,7 @@ export function LinkAccountForm({
disabled={isPending || disableSubmit}
>
{intl.formatMessage({
id: "linkEuroBonusAccount.linkMyAccountsButton",
defaultMessage: "Link my accounts",
})}
</Button>

View File

@@ -26,6 +26,7 @@ export default async function SASxScandicLinkPage(props: PageArgs<LangParams>) {
<Typography variant="Title/Subtitle/lg">
<h1>
{intl.formatMessage({
id: "linkEuroBonusAccount.successTitle",
defaultMessage: "Your accounts are linked",
})}
</h1>
@@ -34,6 +35,7 @@ export default async function SASxScandicLinkPage(props: PageArgs<LangParams>) {
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "linkEuroBonusAccount.successMessage",
defaultMessage: "We successfully connected your accounts!",
})}
</p>
@@ -41,6 +43,7 @@ export default async function SASxScandicLinkPage(props: PageArgs<LangParams>) {
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "linkEuroBonusAccount.redirectMyPagesMessage",
defaultMessage: "Redirecting you to My Pages.",
})}
</p>

View File

@@ -54,14 +54,17 @@ export default async function SASxScandicLoginPage(
const intentDescriptions: Record<Intent, string> = {
link: intl.formatMessage({
id: "linkEuroBonusAccount.loginRequired",
defaultMessage:
"Log in to your SAS EuroBonus account to confirm account linking.",
}),
unlink: intl.formatMessage({
id: "unlinkEuroBonusAccount.loginRequired",
defaultMessage:
"Log in to your SAS EuroBonus account to confirm account unlinking.",
}),
transfer: intl.formatMessage({
id: "transferEuroBonusPoints.loginRequired",
defaultMessage:
"In order to exchange your points, we'll ask you to sign in to your SAS EuroBonus account.",
}),
@@ -80,6 +83,7 @@ export default async function SASxScandicLoginPage(
<Typography variant="Title/Subtitle/lg">
<h1>
{intl.formatMessage({
id: "linkEuroBonusAccount.redirectingToSAS",
defaultMessage: "Redirecting you to SAS",
})}
</h1>
@@ -92,6 +96,7 @@ export default async function SASxScandicLoginPage(
<Footnote textAlign="center">
{intl.formatMessage(
{
id: "linkEuroBonusAccount.manualRedirectLinkMessage",
defaultMessage:
"If you are not redirected automatically, please <loginLink>click here</loginLink>.",
},

View File

@@ -56,6 +56,7 @@ export default function OneTimePasswordForm({
const cause = requestOtp.error?.data?.cause as RequestOtpError
const title = intl.formatMessage({
id: "linkEuroBonusAccount.oneTimePasswordGenericError",
defaultMessage: "Error requesting OTP",
})
const body = getRequestErrorBody(intl, cause?.errorCode)
@@ -123,10 +124,12 @@ export default function OneTimePasswordForm({
const errorMessages: Record<OtpError, ReactNode> = {
invalidCode: intl.formatMessage({
id: "linkEuroBonusAccount.invalidOtpCodeMessage",
defaultMessage: "The code you've entered is incorrect.",
}),
expiredCode: intl.formatMessage(
{
id: "linkEuroBonusAccount.expiredOtpCodeMessage",
defaultMessage:
"This code has expired. <resendOtpLink>Send new code.</resendOtpLink>",
},
@@ -188,6 +191,7 @@ export default function OneTimePasswordForm({
<p>
{intl.formatMessage(
{
id: "linkEuroBonusAccount.didNotReceiveCodeMessage",
defaultMessage:
"Didn't receive a code? <resendOtpLink>Resend code</resendOtpLink>",
},
@@ -226,10 +230,12 @@ const getRequestErrorBody = (
switch (errorCode) {
case "TOO_MANY_REQUESTS":
return intl.formatMessage({
id: "linkEuroBonusAccount.tooManyOtpRequestsMessage",
defaultMessage: "Too many requests. Please try again later.",
})
default:
return intl.formatMessage({
id: "linkEuroBonusAccount.requestOtpGenericErrorMessage",
defaultMessage: "An error occurred while requesting a new OTP",
})
}

View File

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

View File

@@ -115,6 +115,7 @@ export default async function SASxScandicOneTimePasswordPage(
const intentDescriptions: Record<Intent, ReactNode> = {
link: intl.formatMessage(
{
id: "linkEuroBonusAccount.oneTimePasswordInputDescription",
defaultMessage:
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to confirm your account linking.",
},
@@ -122,6 +123,7 @@ export default async function SASxScandicOneTimePasswordPage(
),
unlink: intl.formatMessage(
{
id: "unlinkEuroBonusAccount.oneTimePasswordInputDescription",
defaultMessage:
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to unlink your accounts.",
},
@@ -129,6 +131,7 @@ export default async function SASxScandicOneTimePasswordPage(
),
transfer: intl.formatMessage(
{
id: "transferEuroBonusPoints.oneTimePasswordInputDescription",
defaultMessage:
"Please enter the code sent to <maskedContactInfo></maskedContactInfo> in order to transfer your points.",
},
@@ -139,10 +142,12 @@ export default async function SASxScandicOneTimePasswordPage(
return (
<OneTimePasswordForm
heading={intl.formatMessage({
id: "linkEuroBonusAccount.oneTimePasswordHeading",
defaultMessage: "Verification code",
})}
ingress={intentDescriptions[intent]}
footnote={intl.formatMessage({
id: "linkEuroBonusAccount.oneTimePasswordDescription",
defaultMessage: "This verification is needed for additional security.",
})}
otpLength={6}

View File

@@ -48,6 +48,7 @@ export default async function SASxScandicTransferSuccessPage(
<Typography variant="Title/Subtitle/lg">
<h1>
{intl.formatMessage({
id: "transferEuroBonusPoints.successMessage",
defaultMessage: "Point exchange completed!",
})}
</h1>
@@ -62,6 +63,7 @@ export default async function SASxScandicTransferSuccessPage(
>
<Link href={partnerSas[params.lang]} color="none">
{intl.formatMessage({
id: "transferEuroBonusPoints.backToMyPagesButton",
defaultMessage: "Go back to My Pages",
})}
</Link>
@@ -91,6 +93,7 @@ async function TransactionCard({
<Typography variant="Title/Subtitle/md">
<h2>
{intl.formatMessage({
id: "transferEuroBonusPoints.transactionTitle",
defaultMessage: "Your transaction",
})}
</h2>
@@ -100,6 +103,7 @@ async function TransactionCard({
<Typography variant="Title/Overline/sm">
<h3>
{intl.formatMessage({
id: "transferEuroBonusPoints.pointsAddedTitle",
defaultMessage: "Points added",
})}
</h3>
@@ -113,6 +117,7 @@ async function TransactionCard({
<Typography variant="Title/Overline/sm">
<h3>
{intl.formatMessage({
id: "transferEuroBonusPoints.newTotalTitle",
defaultMessage: "Your new total",
})}
</h3>
@@ -128,6 +133,7 @@ async function TransactionCard({
<Typography variant="Body/Paragraph/mdBold">
<h3>
{intl.formatMessage({
id: "transferEuroBonusPoints.bonusNightAvailableTitle",
defaultMessage: "You have enough points for a reward night!",
})}
</h3>
@@ -135,6 +141,7 @@ async function TransactionCard({
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "transferEuroBonusPoints.bonusNightAvailableDescription",
defaultMessage:
"Redeem your points for cozy stays, delicious meals at our restaurants, or hotel extras to elevate your stay!",
})}
@@ -152,6 +159,7 @@ async function TransactionCard({
{/* TODO correct link */}
<Link href={hotelreservation(lang)} color="none">
{intl.formatMessage({
id: "transferEuroBonusPoints.bookNowButton",
defaultMessage: "Book now",
})}
{
@@ -183,6 +191,7 @@ async function TotalPoints() {
{"= "}
{intl.formatMessage(
{
id: "transferEuroBonusPoints.pointsResultText",
defaultMessage: "{points, number} points",
},
{

View File

@@ -28,6 +28,7 @@ export default async function SASxScandicUnlinkSuccessPage(
<Typography variant="Title/Subtitle/lg">
<h1>
{intl.formatMessage({
id: "unlinkEuroBonusAccount.successTitle",
defaultMessage: "Your accounts are now unlinked",
})}
</h1>
@@ -36,6 +37,7 @@ export default async function SASxScandicUnlinkSuccessPage(
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "linkEuroBonusAccount.redirectMyPagesMessage",
defaultMessage: "Redirecting you to My Pages.",
})}
</p>

View File

@@ -24,6 +24,7 @@ export default async function Layout(
return (
<p>
{intl.formatMessage({
id: "webview.missingUserError",
defaultMessage: "Error: No user could be loaded",
})}
</p>
@@ -46,6 +47,7 @@ export default async function Layout(
return (
<p>
{intl.formatMessage({
id: "webview.userNotFoundError",
defaultMessage: "Error: user not found",
})}
</p>
@@ -54,6 +56,7 @@ export default async function Layout(
return (
<p>
{intl.formatMessage({
id: "webview.genericUserError",
defaultMessage: "Unknown error occurred loading user",
})}
</p>

View File

@@ -83,17 +83,20 @@ export default function CampaignHotelListingClient({
}
let buttonText = intl.formatMessage({
id: "common.showMore",
defaultMessage: "Show more",
})
let iconDirection: MaterialIconProps["icon"] = "keyboard_arrow_down"
if (visibleCount === activeHotels.length) {
buttonText = intl.formatMessage({
id: "common.showLess",
defaultMessage: "Show less",
})
iconDirection = "keyboard_arrow_up"
} else if (canShowAll) {
buttonText = intl.formatMessage({
id: "common.showAll",
defaultMessage: "Show all",
})
}

View File

@@ -40,6 +40,7 @@ export default function HotelListingItem({
sizes="(min-width: 768px) 450px, 100vw"
title={intl.formatMessage(
{
id: "common.imageGalleryWithTitle",
defaultMessage: "{title} - Image gallery",
},
{ title: hotel.name }
@@ -77,6 +78,7 @@ export default function HotelListingItem({
<span>
{intl.formatMessage(
{
id: "common.kmToCityCenter",
defaultMessage: "{number} km to city center",
},
{
@@ -121,6 +123,7 @@ export default function HotelListingItem({
typography="Body/Supporting text (caption)/smBold"
>
{intl.formatMessage({
id: "destination.seeHotelDetails",
defaultMessage: "See hotel details",
})}
</ButtonLink>

View File

@@ -46,13 +46,15 @@ export default async function CampaignHotelListing({
const sortItems: HotelSortItem[] = [
{
label: intl.formatMessage({
id: "common.name",
defaultMessage: "Name",
}),
value: HotelSortOption.Name,
},
{
label: intl.formatMessage({
defaultMessage: "Tripadvisor rating",
id: "common.tripAdvisorRating",
defaultMessage: "TripAdvisor rating",
}),
value: HotelSortOption.TripAdvisorRating,
},

View File

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

View File

@@ -34,9 +34,11 @@ export default function Filter({
items={countryFilters}
defaultSelectedKey={""}
label={intl.formatMessage({
id: "common.country",
defaultMessage: "Country",
})}
aria-label={intl.formatMessage({
id: "common.country",
defaultMessage: "Country",
})}
name="country"
@@ -46,9 +48,11 @@ export default function Filter({
items={cityFilters}
defaultSelectedKey={""}
label={intl.formatMessage({
id: "jobylonFeed.cityFilterLabel",
defaultMessage: "Location (shown in local language)",
})}
aria-label={intl.formatMessage({
id: "jobylonFeed.cityFilterLabel",
defaultMessage: "Location (shown in local language)",
})}
name="city"
@@ -58,9 +62,11 @@ export default function Filter({
items={departmentFilters}
defaultSelectedKey={""}
label={intl.formatMessage({
id: "jobylonFeed.departmentFilterLabel",
defaultMessage: "Hotel or office",
})}
aria-label={intl.formatMessage({
id: "jobylonFeed.departmentFilterLabel",
defaultMessage: "Hotel or office",
})}
name="department"
@@ -70,9 +76,11 @@ export default function Filter({
items={categoryFilters}
defaultSelectedKey={""}
label={intl.formatMessage({
id: "jobylonFeed.categoryFilterLabel",
defaultMessage: "Category",
})}
aria-label={intl.formatMessage({
id: "jobylonFeed.categoryFilterLabel",
defaultMessage: "Category",
})}
name="category"

View File

@@ -33,6 +33,7 @@ export default function JobList({ allJobs }: JobListProps) {
const countryFilters = [
{
label: intl.formatMessage({
id: "jobylonFeed.allCountriesFilter",
defaultMessage: "All countries",
}),
value: "",
@@ -42,6 +43,7 @@ export default function JobList({ allJobs }: JobListProps) {
const cityFilters = [
{
label: intl.formatMessage({
id: "jobylonFeed.allLocationsFilter",
defaultMessage: "All locations",
}),
value: "",
@@ -51,6 +53,7 @@ export default function JobList({ allJobs }: JobListProps) {
const departmentFilters = [
{
label: intl.formatMessage({
id: "jobylonFeed.allDepartmentsFilter",
defaultMessage: "All hotels and offices",
}),
value: "",
@@ -60,6 +63,7 @@ export default function JobList({ allJobs }: JobListProps) {
const categoryFilters = [
{
label: intl.formatMessage({
id: "jobylonFeed.allCategoriesFilter",
defaultMessage: "All categories",
}),
value: "",
@@ -79,6 +83,7 @@ export default function JobList({ allJobs }: JobListProps) {
<Subtitle type="two">
{intl.formatMessage(
{
id: "jobylonFeed.resultCount",
defaultMessage: "{count, plural, one {# Result} other {# Results}}",
},
{ count: state.jobs.length }

View File

@@ -24,11 +24,13 @@ export default function JobylonCard({ job }: JobylonCardProps) {
const deadlineText = job.toDate
? intl.formatMessage(
{
id: "jobylonFeed.card.deadlineText",
defaultMessage: "Deadline: {date}",
},
{ date: dt(job.toDate).locale(lang).format("Do MMMM") }
)
: intl.formatMessage({
id: "jobylonFeed.card.openForApplication",
defaultMessage: "Open for application",
})
@@ -57,6 +59,7 @@ export default function JobylonCard({ job }: JobylonCardProps) {
>
<a href={job.url} target="_blank" rel="noopener noreferrer">
{intl.formatMessage({
id: "jobylonFeed.card.viewAndApplyButton",
defaultMessage: "View & apply",
})}
<MaterialIcon icon="open_in_new" size={20} />

View File

@@ -39,6 +39,7 @@ async function LevelCard({ level }: LevelCardProps) {
let pointsMsg: React.ReactNode = intl.formatMessage(
{
id: "common.pointsAmountPoints",
defaultMessage: "{pointsAmount, number} points",
},
{ pointsAmount: level.required_points }
@@ -47,6 +48,7 @@ async function LevelCard({ level }: LevelCardProps) {
if (level.required_nights) {
pointsMsg = intl.formatMessage(
{
id: "loyaltyLevelsBlock.pointsOrNights",
defaultMessage:
"{pointsAmount, number} points <highlight>or {nightsAmount, number} nights</highlight>",
},
@@ -68,6 +70,7 @@ async function LevelCard({ level }: LevelCardProps) {
>
{intl.formatMessage(
{
id: "common.membershipLevelWithValue",
defaultMessage: "Level {level}",
},
{ level: level.user_facing_tag }

View File

@@ -28,6 +28,7 @@ export function CookieConsentButton() {
className={styles.cookieConsentButton}
>
{intl.formatMessage({
id: "cookieConsent.manageCookiesButton",
defaultMessage: "Manage cookies",
})}
<MaterialIcon icon="chevron_right" size={24} color="CurrentColor" />

View File

@@ -18,27 +18,35 @@ export default async function MyPagesOverviewShortcuts() {
const MyPagesLinkTranslationMap: Record<MyPagesLinkKey, string> = {
overview: intl.formatMessage({
id: "common.overview",
defaultMessage: "Overview",
}),
points: intl.formatMessage({
id: "common.myPoints",
defaultMessage: "My points",
}),
stays: intl.formatMessage({
id: "common.myStays",
defaultMessage: "My stays",
}),
benefits: intl.formatMessage({
id: "common.myBenefits",
defaultMessage: "My benefits",
}),
partnerSas: intl.formatMessage({
id: "partnerSas.sasEuroBonus",
defaultMessage: "SAS EuroBonus",
}),
teamMemberCard: intl.formatMessage({
id: "common.teamMemberCard",
defaultMessage: "Team Member Card",
}),
scandicFriends: intl.formatMessage({
id: "common.aboutScandicFriends",
defaultMessage: "About Scandic Friends",
}),
profile: intl.formatMessage({
id: "common.myProfile",
defaultMessage: "My profile",
}),
}
@@ -82,14 +90,26 @@ export default async function MyPagesOverviewShortcuts() {
})),
{
openInNewTab: false,
text: intl.formatMessage({ defaultMessage: "Partner benefits" }),
title: intl.formatMessage({ defaultMessage: "Partner benefits" }),
text: intl.formatMessage({
id: "myPagesOverviewShortcuts.partnerBenefits",
defaultMessage: "Partner benefits",
}),
title: intl.formatMessage({
id: "myPagesOverviewShortcuts.partnerBenefits",
defaultMessage: "Partner benefits",
}),
url: webHrefs.partners[lang],
},
{
openInNewTab: false,
text: intl.formatMessage({ defaultMessage: "Scandic Friends FAQ" }),
title: intl.formatMessage({ defaultMessage: "Scandic Friends FAQ" }),
text: intl.formatMessage({
id: "common.scandicFriendsFaq",
defaultMessage: "Scandic Friends FAQ",
}),
title: intl.formatMessage({
id: "common.scandicFriendsFaq",
defaultMessage: "Scandic Friends FAQ",
}),
url: webHrefs.faq[lang],
},
]
@@ -103,6 +123,7 @@ export default async function MyPagesOverviewShortcuts() {
<div className={styles.column}>
<SectionHeader
title={intl.formatMessage({
id: "myPagesOverviewShortcuts.yourMembership",
defaultMessage: "Your membership",
})}
headingAs="h4"
@@ -116,6 +137,7 @@ export default async function MyPagesOverviewShortcuts() {
<div className={styles.column}>
<SectionHeader
title={intl.formatMessage({
id: "myPagesOverviewShortcuts.scandicFriendsLinks",
defaultMessage: "Scandic Friends Links",
})}
headingAs="h4"

View File

@@ -18,12 +18,14 @@ export default function CopyButton({ membershipNumber }: CopyButtonProps) {
navigator.clipboard.writeText(membershipNumber)
toast.success(
intl.formatMessage({
id: "myPages.membershipIdCopied",
defaultMessage: "Membership ID copied to clipboard",
})
)
} catch {
toast.error(
intl.formatMessage({
id: "errorMessage.copyFailed",
defaultMessage: "Failed to copy",
})
)

View File

@@ -21,6 +21,7 @@ export default async function MembershipNumber({
<div className={classNames}>
<Caption color="pale">
{intl.formatMessage({
id: "overview.membershipNumber.label",
defaultMessage: "Membership ID:",
})}
</Caption>
@@ -29,6 +30,7 @@ export default async function MembershipNumber({
<code data-hj-suppress>
{membership?.membershipNumber ??
intl.formatMessage({
id: "common.NA",
defaultMessage: "N/A",
})}
</code>

View File

@@ -24,13 +24,12 @@ export default async function Friend({
const isHighestLevel = isHighestMembership(membership.membershipLevel)
const lvlMessageHighest = intl.formatMessage({
id: "overview.friend.highestLevel",
defaultMessage: "Highest level",
})
const lvlMessageLevel = intl.formatMessage(
{
defaultMessage: "Level {level}",
},
{ id: "common.membershipLevelWithValue", defaultMessage: "Level {level}" },
{ level: membershipLevels[membership.membershipLevel] }
)

View File

@@ -28,6 +28,7 @@ export default async function SasBoostStatus({
const sasBoostExpiryText = intl.formatMessage(
{
id: "membershipOverViewCard.sasBoostedUntilDate",
defaultMessage: "Boosted by SAS until {date}",
},
{

View File

@@ -32,7 +32,7 @@ export default async function MembershipOverviewCard({
const pointsToSpendText =
typeof user.membership.currentPoints === "number"
? intl.formatNumber(user.membership.currentPoints)
: intl.formatMessage({ defaultMessage: "N/A" })
: intl.formatMessage({ id: "common.NA", defaultMessage: "N/A" })
const sasMembership = user.loyalty
? getEurobonusMembership(user.loyalty)
@@ -53,6 +53,7 @@ export default async function MembershipOverviewCard({
<h2 className={styles.levelText} id="membership-level">
{intl.formatMessage(
{
id: "common.membershipLevelWithValue",
defaultMessage: "Level {level}",
},
{ level: membershipLevels[user.membership.membershipLevel] }
@@ -79,6 +80,7 @@ export default async function MembershipOverviewCard({
<Typography variant="Title/Overline/sm">
<h3 className={styles.pointsLabel}>
{intl.formatMessage({
id: "common.pointsToSpend",
defaultMessage: "Points to spend",
})}
</h3>

View File

@@ -26,6 +26,7 @@ export default async function ExpiringPoints({ user }: UserProps) {
<Body color="white" textTransform="bold" textAlign="center">
{intl.formatMessage(
{
id: "overview.stats.expiringPoints",
defaultMessage: "{points} spendable points expiring by {date}",
},
{

View File

@@ -28,9 +28,11 @@ export default async function Points({ user }: UserProps) {
<PointsColumn
value={membership?.currentPoints}
title={intl.formatMessage({
id: "stats.points.yourPointsToSpend",
defaultMessage: "Your points to spend",
})}
subtitle={intl.formatMessage({
id: "stats.points.asOfToday",
defaultMessage: "as of today",
})}
/>
@@ -38,10 +40,12 @@ export default async function Points({ user }: UserProps) {
<PointsColumn
value={membership?.pointsRequiredToNextlevel}
title={intl.formatMessage({
id: "stats.points.pointsNeededToLevelUp",
defaultMessage: "Points needed to level up",
})}
subtitle={intl.formatMessage(
{
id: "stats.points.nextLevel",
defaultMessage: "next level: {nextLevel}",
},
{ nextLevel: nextLevel.name }

View File

@@ -34,6 +34,7 @@ export default async function UserBaseInfo({ user }: UserBaseInfoProps) {
<Typography variant="Body/Supporting text (caption)/smBold">
<span>
{intl.formatMessage({
id: "overview.membershipNumber.label",
defaultMessage: "Membership ID:",
})}
</span>
@@ -46,6 +47,7 @@ export default async function UserBaseInfo({ user }: UserBaseInfoProps) {
<Typography variant="Body/Supporting text (caption)/smBold">
<span className={styles.noMembership}>
{intl.formatMessage({
id: "common.NA",
defaultMessage: "N/A",
})}
</span>

View File

@@ -55,6 +55,7 @@ export default async function Overview({
<>
<MaterialIcon icon="id_card" size={24} color="CurrentColor" />
{intl.formatMessage({
id: "dtmc.showTeamMemberCard",
defaultMessage: "Show Team Member Card",
})}
</>

View File

@@ -98,10 +98,12 @@ export default function OverviewTableClient({
/>
<DeprecatedSelect
aria-label={intl.formatMessage({
id: "common.level",
defaultMessage: "Level",
})}
name={`reward` + column}
label={intl.formatMessage({
id: "common.level",
defaultMessage: "Level",
})}
items={levelOptions}
@@ -137,10 +139,12 @@ export default function OverviewTableClient({
return (
<DeprecatedSelect
aria-label={intl.formatMessage({
id: "common.level",
defaultMessage: "Level",
})}
name={`reward` + column}
label={intl.formatMessage({
id: "common.level",
defaultMessage: "Level",
})}
items={levelOptions}

View File

@@ -13,6 +13,7 @@ export default function LevelSummary({
const pointsMsg: React.ReactNode = level.required_nights
? intl.formatMessage(
{
id: "overviewTable.levelSummary.pointsOrNights",
defaultMessage:
"{pointsAmount, number} points or {nightsAmount, number} nights",
},
@@ -23,6 +24,7 @@ export default function LevelSummary({
)
: intl.formatMessage(
{
id: "common.pointsAmountPoints",
defaultMessage: "{pointsAmount, number} points",
},
{ pointsAmount: level.required_points }

View File

@@ -14,6 +14,7 @@ export default function YourLevel() {
textAlign={"center"}
>
{intl.formatMessage({
id: "overviewTable.yourLevel",
defaultMessage: "Your level",
})}
</BiroScript>

View File

@@ -21,6 +21,7 @@ export default function ClaimPoints() {
<Typography variant="Body/Supporting text (caption)/smRegular">
<p>
{intl.formatMessage({
id: "points.claimPoints.missingPreviousStay",
defaultMessage: "Missing a previous stay?",
})}
</p>
@@ -32,7 +33,10 @@ export default function ClaimPoints() {
size="Small"
typography="Body/Supporting text (caption)/smBold"
>
{intl.formatMessage({ defaultMessage: "Claim points" })}
{intl.formatMessage({
id: "points.claimPoints.cta",
defaultMessage: "Claim points",
})}
<MaterialIcon icon="open_in_new" size={20} color="CurrentColor" />
</ButtonLink>
</div>

View File

@@ -36,6 +36,7 @@ export default function AwardPoints({
{isCalculated
? intl.formatNumber(awardPoints)
: intl.formatMessage({
id: "earnAndBurn.awardPoints.pointsBeingCalculated",
defaultMessage: "Points being calculated",
})}
</span>

View File

@@ -25,6 +25,7 @@ export default function Row({ transaction }: RowProps) {
const nightsMsg = intl.formatMessage(
{
id: "booking.numberOfNights",
defaultMessage: "{totalNights, plural, one {# night} other {# nights}}",
},
{
@@ -34,7 +35,10 @@ export default function Row({ transaction }: RowProps) {
let description =
transaction.confirmationNumber === "non-transactional" &&
transaction.nights === 0
? intl.formatMessage({ defaultMessage: "Points activity" })
? intl.formatMessage({
id: "earnAndBurn.journeyTable.pointsActivity",
defaultMessage: "Points activity",
})
: transaction.hotelName && transaction.city
? `${transaction.hotelName}, ${transaction.city} ${nightsMsg}`
: `${nightsMsg}`
@@ -44,37 +48,44 @@ export default function Row({ transaction }: RowProps) {
case Transactions.rewardType.stayAdj:
if (transaction.hotelId === "ORS") {
description = intl.formatMessage({
id: "earnAndBurn.journeyTable.formerScandicHotel",
defaultMessage: "Former Scandic Hotel",
})
}
if (transaction.confirmationNumber === "BALFWD") {
description = intl.formatMessage({
id: "earnAndBurn.journeyTable.pointsEarnedPriorMay2021",
defaultMessage: "Points earned prior to May 1, 2021",
})
}
break
case Transactions.rewardType.ancillary:
description = intl.formatMessage({
id: "earnAndBurn.journeyTable.extrasToBooking",
defaultMessage: "Extras to your booking",
})
break
case Transactions.rewardType.enrollment:
description = intl.formatMessage({
id: "earnAndBurn.journeyTable.signUpBonus",
defaultMessage: "Sign up bonus",
})
break
case Transactions.rewardType.mastercard_points:
description = intl.formatMessage({
id: "earnAndBurn.journeyTable.scandicFriendsMastercard",
defaultMessage: "Scandic Friends Mastercard",
})
break
case Transactions.rewardType.tui_points:
description = intl.formatMessage({
id: "earnAndBurn.journeyTable.tuiPoints",
defaultMessage: "TUI Points",
})
case Transactions.rewardType.pointShop:
description = intl.formatMessage({
id: "earnAndBurn.journeyTable.pointShop",
defaultMessage: "Scandic Friends Point Shop",
})
break

View File

@@ -16,15 +16,19 @@ export default function ClientTable({ transactions }: ClientTableProps) {
const tableHeadings = [
intl.formatMessage({
id: "common.points",
defaultMessage: "Points",
}),
intl.formatMessage({
id: "earnAndBurn.journeyTable.description",
defaultMessage: "Description",
}),
intl.formatMessage({
id: "earnAndBurn.journeyTable.bookingNumberReference",
defaultMessage: "Booking number / Reference",
}),
intl.formatMessage({
id: "earnAndBurn.journeyTable.date",
defaultMessage: "Date",
}),
]
@@ -55,6 +59,7 @@ export default function ClientTable({ transactions }: ClientTableProps) {
<Table.TR className={styles.placeholder}>
<Table.TD colSpan={tableHeadings.length}>
{intl.formatMessage({
id: "earnAndBurn.journeyTable.noTransactions",
defaultMessage: "No transactions available",
})}
</Table.TD>

View File

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

View File

@@ -32,11 +32,14 @@ export default function ExpiringPointsSeeAllButton({
typography="Body/Paragraph/mdBold"
onPress={() => setIsOpen(true)}
>
{intl.formatMessage({ defaultMessage: "See all" })}
{intl.formatMessage({ id: "common.seeAll", defaultMessage: "See all" })}
<MaterialIcon icon="chevron_right" color="CurrentColor" />
</Button>
<SidePeekSelfControlled
title={intl.formatMessage({ defaultMessage: "Expiring points" })}
title={intl.formatMessage({
id: "points.pointsToSpendCard.expiringPointsTitle",
defaultMessage: "Expiring points",
})}
isOpen={isOpen}
onClose={() => setIsOpen(false)}
>
@@ -44,6 +47,7 @@ export default function ExpiringPointsSeeAllButton({
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "points.pointsToSpendCard.expiringPointsInfo",
defaultMessage:
"Points expire three years after they are earned, on the last day of that month. Expiring points do not affect your level.",
})}

View File

@@ -38,6 +38,7 @@
.pointsLabel {
color: var(--Text-Brand-OnPrimary-1-Heading);
text-transform: capitalize;
}
.description {

View File

@@ -52,6 +52,7 @@ export default async function PointsToSpendCard({
<Typography variant="Title/xs">
<h2 id="points-to-spend-card-title" className={styles.title}>
{intl.formatMessage({
id: "common.pointsToSpend",
defaultMessage: "Points to spend",
})}
</h2>
@@ -66,6 +67,7 @@ export default async function PointsToSpendCard({
<Typography variant="Title/Overline/sm">
<span className={styles.pointsLabel}>
{intl.formatMessage({
id: "common.points",
defaultMessage: "Points",
})}
</span>
@@ -75,6 +77,7 @@ export default async function PointsToSpendCard({
{hasPointsToSpend && (
<ButtonLink href={spendPoints[lang]} target="_blank" variant="Text">
{intl.formatMessage({
id: "points.pointsToSpendCard.howToSpendCta",
defaultMessage: "How to spend points",
})}
<MaterialIcon
@@ -91,6 +94,7 @@ export default async function PointsToSpendCard({
<Typography variant="Body/Paragraph/mdRegular">
<p className={styles.descriptionText}>
{intl.formatMessage({
id: "points.pointsToSpendCard.description",
defaultMessage:
"Earn points by staying at Scandic. Turn your points into free nights and memorable experiences.",
})}
@@ -108,6 +112,7 @@ export default async function PointsToSpendCard({
<p className={styles.expiringPoints}>
{intl.formatMessage(
{
id: "points.pointsToSpendCard.expiringPointsText",
defaultMessage: "{expiringPoints} points expiring",
},
{

View File

@@ -24,6 +24,7 @@ export function getExpiryLabel(
if (daysUntilExpiry <= DAYS_UNTIL_EXPIRY_WARNING) {
return intl.formatMessage(
{
id: "points.pointsToSpendCard.inDays",
defaultMessage: "In {days} days",
},
{
@@ -33,6 +34,7 @@ export function getExpiryLabel(
} else {
return intl.formatMessage(
{
id: "points.pointsToSpendCard.onDate",
defaultMessage: "on {expiryDate}",
},
{

View File

@@ -50,6 +50,7 @@ export default async function NextLevelRewardsBlock({
<Chip>
<Lock height={16} />
{intl.formatMessage({
id: "rewards.nextLevel.levelUpToUnlock",
defaultMessage: "Level up to unlock",
})}
</Chip>
@@ -57,6 +58,7 @@ export default async function NextLevelRewardsBlock({
<Body color="peach50" textAlign="center">
{intl.formatMessage(
{
id: "rewards.nextLevel.asOurLevel",
defaultMessage: "As our {level}",
},
{ level: nextLevelRewards.level?.name }

View File

@@ -27,6 +27,7 @@ export default function ActiveRedeemedBadge() {
</motion.div>
<Caption>
{intl.formatMessage({
id: "rewards.active",
defaultMessage: "Active",
})}
</Caption>

View File

@@ -17,16 +17,19 @@ export function ConfirmClose({ close }: { close: () => void }) {
<div className={styles.modalContent}>
<Title level="h3" textAlign="center" textTransform="regular">
{intl.formatMessage({
id: "redeem.confirmClose.title",
defaultMessage: "If you close this your benefit will be removed",
})}
</Title>
<Body>
{intl.formatMessage({
id: "redeem.confirmClose.question",
defaultMessage: "Have you showed this benefit to the hotel staff?",
})}
</Body>
<Body>
{intl.formatMessage({
id: "redeem.confirmClose.info",
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.",
})}
@@ -39,11 +42,13 @@ export function ConfirmClose({ close }: { close: () => void }) {
theme="base"
>
{intl.formatMessage({
id: "redeem.confirmClose.goBack",
defaultMessage: "No, go back",
})}
</Button>
<Button onClick={close} intent="secondary" theme="base">
{intl.formatMessage({
id: "redeem.confirmClose.confirm",
defaultMessage: "Yes, close and remove benefit",
})}
</Button>

View File

@@ -29,6 +29,7 @@ export default function Campaign({ reward }: { reward: Campaign }) {
<div className={styles.rewardBadge}>
<Caption textAlign="center" color="uiTextHighContrast" type="bold">
{intl.formatMessage({
id: "redeemFlow.promoCode",
defaultMessage: "Promo code",
})}
</Caption>
@@ -44,12 +45,14 @@ export default function Campaign({ reward }: { reward: Campaign }) {
navigator.clipboard.writeText(reward.operaRewardId)
toast.success(
intl.formatMessage({
id: "redeemFlow.copiedToClipboard",
defaultMessage: "Copied to clipboard",
})
)
} catch {
toast.error(
intl.formatMessage({
id: "errorMessage.copyFailed",
defaultMessage: "Failed to copy",
})
)
@@ -63,6 +66,7 @@ export default function Campaign({ reward }: { reward: Campaign }) {
>
<MaterialIcon icon="content_copy" color="CurrentColor" />
{intl.formatMessage({
id: "redeemFlow.copyPromotionCode",
defaultMessage: "Copy promotion code",
})}
</Button>

View File

@@ -88,6 +88,7 @@ export default function Tier({
theme="base"
>
{intl.formatMessage({
id: "redeemFlow.redeemBenefit",
defaultMessage: "Redeem benefit",
})}
</Button>
@@ -103,6 +104,7 @@ export default function Tier({
theme="base"
>
{intl.formatMessage({
id: "redeemFlow.yesRedeem",
defaultMessage: "Yes, redeem",
})}
</Button>
@@ -112,6 +114,7 @@ export default function Tier({
theme="base"
>
{intl.formatMessage({
id: "common.goBack",
defaultMessage: "Go back",
})}
</Button>

View File

@@ -16,6 +16,7 @@ export default function MembershipNumberBadge({
<Caption textAlign="center" color="uiTextHighContrast">
{intl.formatMessage(
{
id: "rewards.membershipId",
defaultMessage: "Membership ID: {id}",
},
{ id: membershipNumber }

View File

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

View File

@@ -78,9 +78,11 @@ export default function Redeem({ reward, membershipNumber }: RedeemProps) {
>
{reward.redeemLocation === "Non-redeemable"
? intl.formatMessage({
id: "rewards.howToUse",
defaultMessage: "How to use",
})
: intl.formatMessage({
id: "common.open",
defaultMessage: "Open",
})}
</Button>

View File

@@ -23,14 +23,17 @@ export default function ScriptedRewardText({
}
case "Campaign":
return intl.formatMessage({
id: "booking.campaign",
defaultMessage: "Campaign",
})
case "Surprise":
return intl.formatMessage({
id: "rewards.surprise",
defaultMessage: "Surprise!",
})
case "Member-voucher":
return intl.formatMessage({
id: "rewards.voucher",
defaultMessage: "Voucher",
})
default:

View File

@@ -28,12 +28,14 @@ export default async function SASLinkAccountBanner(
}
const headingText = intl.formatMessage({
id: "sas.linkAccountBanner.earnFlightsWithNights",
defaultMessage: "Earn flights with nights",
})
const buttonText =
props.link?.text ||
intl.formatMessage({
id: "sas.linkAccountBanner.readMoreAndLinkAccounts",
defaultMessage: "Read more and link accounts",
})
@@ -41,6 +43,7 @@ export default async function SASLinkAccountBanner(
return (
<span key="scandic-friends" className={styles.brandName}>
{intl.formatMessage({
id: "common.scandicFriends",
defaultMessage: "Scandic Friends",
})}
</span>
@@ -51,6 +54,7 @@ export default async function SASLinkAccountBanner(
return (
<span key="sas-eurobonus" className={styles.brandName}>
{intl.formatMessage({
id: "partnerSas.sasEuroBonus",
defaultMessage: "SAS EuroBonus",
})}
</span>
@@ -59,6 +63,7 @@ export default async function SASLinkAccountBanner(
const descriptionText = intl.formatMessage(
{
id: "sas.linkAccountBanner.description",
defaultMessage:
"Link your <scandicFriends></scandicFriends> and <sasEuroBonus></sasEuroBonus> accounts to earn, use and exchange points between memberships",
},

View File

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

View File

@@ -52,6 +52,7 @@ export default async function SASLinkedAccount({
<p className={styles.caption}>
<MaterialIcon icon="info" size={20} />
{intl.formatMessage({
id: "sas.linkedAccounts.changeDelayInfo",
defaultMessage:
"Changes in your level match can take up to 24 hours to be displayed.",
})}
@@ -96,12 +97,14 @@ async function MatchedAccountInfo() {
<div className={styles.stack}>
<Label>
{intl.formatMessage({
id: "sas.linkedAccounts.linkedAccount",
defaultMessage: "Linked account",
})}
</Label>
<Typography variant="Body/Paragraph/mdBold">
<p>
{intl.formatMessage({
id: "partnerSas.sasEuroBonus",
defaultMessage: "SAS EuroBonus",
})}
</p>
@@ -110,6 +113,7 @@ async function MatchedAccountInfo() {
<div className={styles.stack}>
<Label>
{intl.formatMessage({
id: "common.level",
defaultMessage: "Level",
})}
</Label>
@@ -120,6 +124,7 @@ async function MatchedAccountInfo() {
<div className={cx(styles.stack, styles.accountMemberNumber)}>
<Label>
{intl.formatMessage({
id: "common.membershipId",
defaultMessage: "Membership ID",
})}
</Label>
@@ -154,12 +159,14 @@ async function MatchedAccountInfoSkeleton() {
<div className={styles.stack}>
<Label>
{intl.formatMessage({
id: "sas.linkedAccounts.linkedAccount",
defaultMessage: "Linked account",
})}
</Label>
<Typography variant="Body/Paragraph/mdBold">
<p>
{intl.formatMessage({
id: "partnerSas.sasEuroBonus",
defaultMessage: "SAS EuroBonus",
})}
</p>
@@ -168,6 +175,7 @@ async function MatchedAccountInfoSkeleton() {
<div className={styles.stack}>
<Label>
{intl.formatMessage({
id: "common.level",
defaultMessage: "Level",
})}
</Label>
@@ -176,6 +184,7 @@ async function MatchedAccountInfoSkeleton() {
<div className={cx(styles.stack, styles.accountMemberNumber)}>
<Label>
{intl.formatMessage({
id: "common.membershipId",
defaultMessage: "Membership ID",
})}
</Label>
@@ -219,6 +228,7 @@ async function TierMatchMessage({
const messageMap: Record<MatchState, ReactNode> = {
boostedBySAS: intl.formatMessage(
{
id: "sas.linkedAccounts.euroBonusSasUpgradedText",
defaultMessage:
"<sasMark>EuroBonus {sasLevelName}</sasMark> has upgraded your Scandic Friends level to <scandicMark>{scandicLevelName}</scandicMark>.",
},
@@ -226,6 +236,7 @@ async function TierMatchMessage({
),
boostedByScandic: intl.formatMessage(
{
id: "sas.linkedAccounts.scandicFriendsUpgradedText",
defaultMessage:
"Your Scandic Friends level <scandicMark>{scandicLevelName}</scandicMark> has upgraded you to <sasMark>EuroBonus {sasLevelName}</sasMark>.",
},
@@ -233,6 +244,7 @@ async function TierMatchMessage({
),
noBoost: intl.formatMessage(
{
id: "sas.linkedAccounts.euroBonusSasText",
defaultMessage:
"<sasMark>EuroBonus {sasLevelName}</sasMark> and <scandicMark>{scandicLevelName}</scandicMark> are equally matched. Level up in one of your memberships to qualify for an upgrade!",
},
@@ -250,6 +262,7 @@ async function TierMatchMessage({
<div className={styles.stack}>
<Label>
{intl.formatMessage({
id: "sas.linkedAccounts.levelMatchStatus",
defaultMessage: "Level match status",
})}
</Label>
@@ -270,6 +283,7 @@ async function TierMatchMessageSkeleton() {
<div className={styles.stack}>
<Label>
{intl.formatMessage({
id: "sas.linkedAccounts.levelMatchStatus",
defaultMessage: "Level match status",
})}
</Label>
@@ -307,6 +321,7 @@ async function TierMatchExpiration({
<div className={cx(styles.stack, styles.textRight)}>
<Label>
{intl.formatMessage({
id: "sas.linkedAccounts.upgradeValidUntil",
defaultMessage: "Upgrade valid until",
})}
</Label>

View File

@@ -80,6 +80,7 @@ async function TransferPointsFormContent({
<Typography variant="Tag/sm">
<p>
{intl.formatMessage({
id: "partnerSas.exchangeFrom",
defaultMessage: "Exchange from",
})}
</p>
@@ -89,6 +90,7 @@ async function TransferPointsFormContent({
<Typography variant="Title/Subtitle/md">
<p>
{intl.formatMessage({
id: "partnerSas.sasEuroBonus",
defaultMessage: "SAS EuroBonus",
})}
</p>
@@ -98,6 +100,7 @@ async function TransferPointsFormContent({
<Typography variant="Tag/sm">
<p className={styles.balanceLabel}>
{intl.formatMessage({
id: "partnerSas.balance",
defaultMessage: "Balance",
})}
</p>
@@ -113,6 +116,7 @@ async function TransferPointsFormContent({
<p>
{intl.formatMessage(
{
id: "partnerSas.pointsWithValue",
defaultMessage: "{points, number} p",
},
{ points: sasPoints }
@@ -127,6 +131,7 @@ async function TransferPointsFormContent({
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "partnerSas.noPointsToTransfer",
defaultMessage: "You have no points to transfer.",
})}
</p>
@@ -140,6 +145,7 @@ async function TransferPointsFormContent({
<Typography variant="Tag/sm">
<p>
{intl.formatMessage({
id: "partnerSas.exchangeTo",
defaultMessage: "Exchange to",
})}
</p>
@@ -148,6 +154,7 @@ async function TransferPointsFormContent({
<Typography variant="Title/Subtitle/md">
<p>
{intl.formatMessage({
id: "common.scandicFriends",
defaultMessage: "Scandic Friends",
})}
</p>
@@ -157,6 +164,7 @@ async function TransferPointsFormContent({
<Typography variant="Tag/sm">
<p className={styles.balanceLabel}>
{intl.formatMessage({
id: "partnerSas.balance",
defaultMessage: "Balance",
})}
</p>
@@ -172,6 +180,7 @@ async function TransferPointsFormContent({
<p>
{intl.formatMessage(
{
id: "partnerSas.pointsWithValue",
defaultMessage: "{points, number} p",
},
{ points: scandicPoints }
@@ -191,6 +200,7 @@ async function TransferPointsFormContent({
<Typography variant="Body/Supporting text (caption)/smRegular">
<p style={{ color: "var(--Text-Tertiary)" }}>
{intl.formatMessage({
id: "partnerSas.exchangedPointsNotLevelQualifying",
defaultMessage: "Exchanged points will not be level-qualifying.",
})}
</p>

View File

@@ -67,6 +67,7 @@ export function TransferPointsFormClient({
// Set max value to 1 if sasPoints is 0 since slider requires a range
maxValue={hasNoSasPoints ? 1 : sasPoints}
aria-label={intl.formatMessage({
id: "partnerSas.ebPointsToExchange",
defaultMessage: "EB points to exchange",
})}
formatOptions={{
@@ -88,6 +89,7 @@ export function TransferPointsFormClient({
<TextField type="number" isDisabled={disabled}>
<Input
label={intl.formatMessage({
id: "partnerSas.ebPointsToExchange",
defaultMessage: "EB points to exchange",
})}
type="number"
@@ -109,6 +111,7 @@ export function TransferPointsFormClient({
<p className={styles.conversionRate}>
{/* TODO maybe dynamic string based on exchange rate */}
{intl.formatMessage({
id: "partnerSas.conversionRateInfo",
defaultMessage: "1 EuroBonus point = 2 Scandic Friends points",
})}
</p>
@@ -117,6 +120,7 @@ export function TransferPointsFormClient({
<Typography variant="Label/xsRegular">
<p>
{intl.formatMessage({
id: "partnerSas.sfPointsToReceive",
defaultMessage: "SF points to receive",
})}
</p>
@@ -183,6 +187,7 @@ function ConfirmModal({
disabled={disabled}
>
{intl.formatMessage({
id: "partnerSas.exchangePoints",
defaultMessage: "Exchange points",
})}
</Button>
@@ -197,6 +202,7 @@ function ConfirmModal({
<Typography variant="Title/Subtitle/lg">
<h3>
{intl.formatMessage({
id: "partnerSas.proceedWithPointExchange",
defaultMessage: "Proceed with point exchange?",
})}
</h3>
@@ -205,6 +211,7 @@ function ConfirmModal({
<Typography variant="Body/Paragraph/mdRegular">
<p>
{intl.formatMessage({
id: "partnerSas.youAreAboutToExchange",
defaultMessage: "You are about to exchange:",
})}
</p>
@@ -213,6 +220,7 @@ function ConfirmModal({
<p>
{intl.formatMessage(
{
id: "partnerSas.exchangePointsDetails",
defaultMessage:
"<bold>{sasPoints, number} EuroBonus points</bold> to <bold>{scandicPoints, number} Scandic Friends points</bold>",
},
@@ -232,6 +240,7 @@ function ConfirmModal({
<Typography variant="Body/Supporting text (caption)/smRegular">
<p className={styles.expiryText}>
{intl.formatMessage({
id: "partnerSas.exchangePointsExpiry",
defaultMessage:
"Your exchanged points will retain their original expiry date with a maximum validity of 12 months.",
})}
@@ -245,6 +254,7 @@ function ConfirmModal({
color="none"
>
{intl.formatMessage({
id: "partnerSas.yesIWantToExchangeMyPoints",
defaultMessage: "Yes, I want to exchange my points",
})}
</Link>
@@ -256,6 +266,7 @@ function ConfirmModal({
onClick={() => handleToggle(false)}
>
{intl.formatMessage({
id: "common.cancel",
defaultMessage: "Cancel",
})}
</Button>

View File

@@ -24,10 +24,12 @@ export default async function EmptyUpcomingStaysBlock() {
textAlign="center"
>
{intl.formatMessage({
id: "stays.noUpcomingStays",
defaultMessage: "You have no upcoming stays.",
})}
<span className={styles.burgundyTitle}>
{intl.formatMessage({
id: "stays.whereToGoNext",
defaultMessage: "Where should you go next?",
})}
</span>
@@ -39,6 +41,7 @@ export default async function EmptyUpcomingStaysBlock() {
color="Text/Interactive/Secondary"
>
{intl.formatMessage({
id: "stays.getInspired",
defaultMessage: "Get inspired",
})}
<MaterialIcon icon="arrow_forward" color="CurrentColor" />

View File

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

View File

@@ -27,6 +27,7 @@ export default async function HotelListingItem({
url: hotelData.meetingUrl,
openInNewTab: true,
text: intl.formatMessage({
id: "meetingPackage.bookAMeeting",
defaultMessage: "Book a meeting",
}),
}
@@ -34,6 +35,7 @@ export default async function HotelListingItem({
url: hotelData.url,
openInNewTab: false,
text: intl.formatMessage({
id: "destination.seeHotelDetails",
defaultMessage: "See hotel details",
}),
}
@@ -61,6 +63,7 @@ export default async function HotelListingItem({
<span>
{intl.formatMessage(
{
id: "common.kmToCityCenter",
defaultMessage: "{number} km to city center",
},
{

View File

@@ -26,6 +26,7 @@ export function CarouselPrevious({ className }: CarouselButtonProps) {
style="Elevated"
onPress={scrollPrev}
aria-label={intl.formatMessage({
id: "carousel.previousSlide",
defaultMessage: "Previous slide",
})}
>
@@ -49,6 +50,7 @@ export function CarouselNext({ className }: CarouselButtonProps) {
style="Elevated"
onPress={scrollNext}
aria-label={intl.formatMessage({
id: "carousel.nextSlide",
defaultMessage: "Next slide",
})}
>

View File

@@ -18,7 +18,10 @@ export default async function TopCampaign({ topCampaign }: TopCampaignProps) {
const intl = await getIntl()
const { campaign, heading } = topCampaign
const buttonData = {
cta: intl.formatMessage({ defaultMessage: "Explore the offer" }),
cta: intl.formatMessage({
id: "campaign.exploreTheOffer",
defaultMessage: "Explore the offer",
}),
url: `/${lang}${campaign.url}`,
}
@@ -34,6 +37,7 @@ export default async function TopCampaign({ topCampaign }: TopCampaignProps) {
heading={
campaign.hotel_listing.heading ||
intl.formatMessage({
id: "campaignPage.hotelsIncludedInOffer",
defaultMessage: "Hotels included in this offer",
})
}

View File

@@ -26,9 +26,11 @@ export default function HotelListContent({
<Alert
type={AlertTypeEnum.Info}
heading={intl.formatMessage({
id: "filter.noMatchingHotelsFound",
defaultMessage: "No matching hotels found",
})}
text={intl.formatMessage({
id: "filter.noMatchingHotelsFoundDescription",
defaultMessage:
"It looks like no hotels match your filters. Try adjusting your search to find the perfect stay.",
})}

View File

@@ -73,6 +73,7 @@ export default function HotelList() {
<p>
{intl.formatMessage(
{
id: "destinationPage.hotelsCount",
defaultMessage: "{count} hotels",
},
{ count: visibleHotels.length }

View File

@@ -73,6 +73,7 @@ export default function HotelListItem({ hotel, url }: HotelListItemProps) {
sizes="(min-width: 768px) 700px, 100vw"
title={intl.formatMessage(
{
id: "common.imageGalleryWithTitle",
defaultMessage: "{title} - Image gallery",
},
{ title: hotel.name }
@@ -111,6 +112,7 @@ export default function HotelListItem({ hotel, url }: HotelListItemProps) {
<span>
{intl.formatMessage(
{
id: "common.kmToCityCenter",
defaultMessage: "{number} km to city center",
},
{
@@ -149,6 +151,7 @@ export default function HotelListItem({ hotel, url }: HotelListItemProps) {
typography="Body/Paragraph/mdBold"
>
{intl.formatMessage({
id: "destination.seeHotelDetails",
defaultMessage: "See hotel details",
})}
</ButtonLink>

View File

@@ -69,7 +69,10 @@ export default function CityMap({
>
<MaterialIcon icon="arrow_back" color="CurrentColor" size={24} />
<span>
{intl.formatMessage({ defaultMessage: "Back to cities" })}
{intl.formatMessage({
id: "destination.backToCities",
defaultMessage: "Back to cities",
})}
</span>
</Button>
) : null}

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