Merged in feature/refactor-lang (pull request #387)

feat: SW-238 Avoid prop drilling of lang

Approved-by: Michael Zetterberg
This commit is contained in:
Niclas Edenvin
2024-08-14 11:00:20 +00:00
parent 35128dbf44
commit e67212bd94
94 changed files with 378 additions and 322 deletions

View File

@@ -5,13 +5,11 @@ import { overview } from "@/constants/routes/myPages"
import { serverClient } from "@/lib/trpc/server" import { serverClient } from "@/lib/trpc/server"
import { auth } from "@/auth" import { auth } from "@/auth"
import { getLang } from "@/i18n/serverContext"
import type { LangParams, LayoutArgs } from "@/types/params"
export default async function ProtectedLayout({ export default async function ProtectedLayout({
children, children,
params, }: React.PropsWithChildren) {
}: React.PropsWithChildren<LayoutArgs<LangParams>>) {
const session = await auth() const session = await auth()
/** /**
* Fallback to make sure every route nested in the * Fallback to make sure every route nested in the
@@ -19,16 +17,16 @@ export default async function ProtectedLayout({
*/ */
const h = headers() const h = headers()
const redirectTo = encodeURIComponent( const redirectTo = encodeURIComponent(
h.get("x-url") ?? h.get("x-pathname") ?? overview[params.lang] h.get("x-url") ?? h.get("x-pathname") ?? overview[getLang()]
) )
if (!session) { if (!session) {
redirect(`/${params.lang}/login?redirectTo=${redirectTo}`) redirect(`/${getLang()}/login?redirectTo=${redirectTo}`)
} }
const user = await serverClient().user.get() const user = await serverClient().user.get()
if (!user || "error" in user) { if (!user || "error" in user) {
redirect(`/${params.lang}/login?redirectTo=${redirectTo}`) redirect(`/${getLang()}/login?redirectTo=${redirectTo}`)
} }
return children return children

View File

@@ -1,5 +1,10 @@
import Breadcrumbs from "@/components/MyPages/Breadcrumbs" import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
import { setLang } from "@/i18n/serverContext"
import { LangParams, PageArgs } from "@/types/params"
export default function AllBreadcrumbs({ params }: PageArgs<LangParams>) {
setLang(params.lang)
export default function AllBreadcrumbs() {
return <Breadcrumbs /> return <Breadcrumbs />
} }

View File

@@ -4,6 +4,7 @@ import Content from "@/components/MyPages/AccountPage/Content"
import Title from "@/components/TempDesignSystem/Text/Title" import Title from "@/components/TempDesignSystem/Text/Title"
import TrackingSDK from "@/components/TrackingSDK" import TrackingSDK from "@/components/TrackingSDK"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { setLang } from "@/i18n/serverContext"
import styles from "./page.module.css" import styles from "./page.module.css"
@@ -12,6 +13,8 @@ import type { LangParams, PageArgs } from "@/types/params"
export default async function MyPages({ export default async function MyPages({
params, params,
}: PageArgs<LangParams & { path: string[] }>) { }: PageArgs<LangParams & { path: string[] }>) {
setLang(params.lang)
const accountPageRes = await serverClient().contentstack.accountPage.get() const accountPageRes = await serverClient().contentstack.accountPage.get()
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
@@ -26,7 +29,7 @@ export default async function MyPages({
<main className={styles.blocks}> <main className={styles.blocks}>
<Title>{accountPage.heading}</Title> <Title>{accountPage.heading}</Title>
{accountPage.content.length ? ( {accountPage.content.length ? (
<Content lang={params.lang} content={accountPage.content} /> <Content content={accountPage.content} />
) : ( ) : (
<p>{formatMessage({ id: "No content published" })}</p> <p>{formatMessage({ id: "No content published" })}</p>
)} )}

View File

@@ -2,20 +2,17 @@ import Sidebar from "@/components/MyPages/Sidebar"
import styles from "./layout.module.css" import styles from "./layout.module.css"
import { LangParams, LayoutArgs } from "@/types/params"
export default async function MyPagesLayout({ export default async function MyPagesLayout({
breadcrumbs, breadcrumbs,
children, children,
params, }: React.PropsWithChildren & {
}: React.PropsWithChildren<LayoutArgs<LangParams>> & {
breadcrumbs: React.ReactNode breadcrumbs: React.ReactNode
}) { }) {
return ( return (
<section className={styles.layout}> <section className={styles.layout}>
{breadcrumbs} {breadcrumbs}
<section className={styles.content}> <section className={styles.content}>
<Sidebar lang={params.lang} /> <Sidebar />
{children} {children}
</section> </section>
</section> </section>

View File

@@ -3,10 +3,17 @@ import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body" import Body from "@/components/TempDesignSystem/Text/Body"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { setLang } from "@/i18n/serverContext"
import styles from "./page.module.css" import styles from "./page.module.css"
export default async function CommunicationSlot() { import { LangParams, PageArgs } from "@/types/params"
export default async function CommunicationSlot({
params,
}: PageArgs<LangParams>) {
setLang(params.lang)
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
return ( return (
<section className={styles.container}> <section className={styles.container}>

View File

@@ -7,10 +7,14 @@ import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption" import Caption from "@/components/TempDesignSystem/Text/Caption"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { setLang } from "@/i18n/serverContext"
import styles from "./page.module.css" import styles from "./page.module.css"
export default async function CreditCardSlot() { import { LangParams, PageArgs } from "@/types/params"
export default async function CreditCardSlot({ params }: PageArgs<LangParams>) {
setLang(params.lang)
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
const creditCards = await serverClient().user.creditCards() const creditCards = await serverClient().user.creditCards()

View File

@@ -5,10 +5,16 @@ import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body" import Body from "@/components/TempDesignSystem/Text/Body"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { setLang } from "@/i18n/serverContext"
import styles from "./page.module.css" import styles from "./page.module.css"
export default async function MembershipCardSlot() { import { LangParams, PageArgs } from "@/types/params"
export default async function MembershipCardSlot({
params,
}: PageArgs<LangParams>) {
setLang(params.lang)
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
const membershipCards = await serverClient().user.membershipCards() const membershipCards = await serverClient().user.membershipCards()

View File

@@ -1,8 +1,15 @@
import { serverClient } from "@/lib/trpc/server" import { serverClient } from "@/lib/trpc/server"
import Form from "@/components/Forms/Edit/Profile" import Form from "@/components/Forms/Edit/Profile"
import { setLang } from "@/i18n/serverContext"
import { LangParams, PageArgs } from "@/types/params"
export default async function EditProfileSlot({
params,
}: PageArgs<LangParams>) {
setLang(params.lang)
export default async function EditProfileSlot() {
const user = await serverClient().user.get({ mask: false }) const user = await serverClient().user.get({ mask: false })
if (!user || "error" in user) { if (!user || "error" in user) {
return null return null

View File

@@ -16,12 +16,14 @@ import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body" import Body from "@/components/TempDesignSystem/Text/Body"
import Title from "@/components/TempDesignSystem/Text/Title" import Title from "@/components/TempDesignSystem/Text/Title"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { getLang, setLang } from "@/i18n/serverContext"
import styles from "./page.module.css" import styles from "./page.module.css"
import type { LangParams, PageArgs } from "@/types/params" import { LangParams, PageArgs } from "@/types/params"
export default async function Profile({ params }: PageArgs<LangParams>) { export default async function Profile({ params }: PageArgs<LangParams>) {
setLang(params.lang)
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
const user = await serverClient().user.get() const user = await serverClient().user.get()
if (!user || "error" in user) { if (!user || "error" in user) {
@@ -40,7 +42,7 @@ export default async function Profile({ params }: PageArgs<LangParams>) {
</Title> </Title>
</hgroup> </hgroup>
<Button asChild intent="primary" size="small" theme="base"> <Button asChild intent="primary" size="small" theme="base">
<Link color="none" href={profileEdit[params.lang]}> <Link color="none" href={profileEdit[getLang()]}>
{formatMessage({ id: "Edit profile" })} {formatMessage({ id: "Edit profile" })}
</Link> </Link>
</Button> </Button>

View File

@@ -3,8 +3,12 @@ import "./profileLayout.css"
import { serverClient } from "@/lib/trpc/server" import { serverClient } from "@/lib/trpc/server"
import TrackingSDK from "@/components/TrackingSDK" import TrackingSDK from "@/components/TrackingSDK"
import { setLang } from "@/i18n/serverContext"
export default async function ProfilePage() { import { LangParams, PageArgs } from "@/types/params"
export default async function ProfilePage({ params }: PageArgs<LangParams>) {
setLang(params.lang)
const accountPage = await serverClient().contentstack.accountPage.get() const accountPage = await serverClient().contentstack.accountPage.get()
if (!accountPage) { if (!accountPage) {

View File

@@ -1,5 +1,10 @@
import Breadcrumbs from "@/components/MyPages/Breadcrumbs" import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
import { setLang } from "@/i18n/serverContext"
import { LangParams, PageArgs } from "@/types/params"
export default function PageBreadcrumbs({ params }: PageArgs<LangParams>) {
setLang(params.lang)
export default function PageBreadcrumbs() {
return <Breadcrumbs /> return <Breadcrumbs />
} }

View File

@@ -3,6 +3,7 @@ import { notFound } from "next/navigation"
import ContentPage from "@/components/ContentType/ContentPage" import ContentPage from "@/components/ContentType/ContentPage"
import HotelPage from "@/components/ContentType/HotelPage/HotelPage" import HotelPage from "@/components/ContentType/HotelPage/HotelPage"
import LoyaltyPage from "@/components/ContentType/LoyaltyPage/LoyaltyPage" import LoyaltyPage from "@/components/ContentType/LoyaltyPage/LoyaltyPage"
import { setLang } from "@/i18n/serverContext"
import { import {
ContentTypeParams, ContentTypeParams,
@@ -14,13 +15,15 @@ import {
export default async function ContentTypePage({ export default async function ContentTypePage({
params, params,
}: PageArgs<LangParams & ContentTypeParams & UIDParams, {}>) { }: PageArgs<LangParams & ContentTypeParams & UIDParams, {}>) {
setLang(params.lang)
switch (params.contentType) { switch (params.contentType) {
case "content-page": case "content-page":
return <ContentPage /> return <ContentPage />
case "loyalty-page": case "loyalty-page":
return <LoyaltyPage lang={params.lang} /> return <LoyaltyPage />
case "hotel-page": case "hotel-page":
return <HotelPage lang={params.lang} /> return <HotelPage />
default: default:
const type: never = params.contentType const type: never = params.contentType
console.error(`Unsupported content type given: ${type}`) console.error(`Unsupported content type given: ${type}`)

View File

@@ -1,5 +1,9 @@
import { setLang } from "@/i18n/serverContext"
import { LangParams, PageArgs } from "@/types/params" import { LangParams, PageArgs } from "@/types/params"
export default function HotelReservationPage({ params }: PageArgs<LangParams>) { export default function HotelReservationPage({ params }: PageArgs<LangParams>) {
setLang(params.lang)
return null return null
} }

View File

@@ -6,6 +6,7 @@ import { ChevronRightIcon } from "@/components/Icons"
import StaticMap from "@/components/Maps/StaticMap" import StaticMap from "@/components/Maps/StaticMap"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { getLang, setLang } from "@/i18n/serverContext"
import styles from "./page.module.css" import styles from "./page.module.css"
@@ -15,10 +16,11 @@ export default async function SelectHotelPage({
params, params,
}: PageArgs<LangParams>) { }: PageArgs<LangParams>) {
const intl = await getIntl() const intl = await getIntl()
setLang(params.lang)
const { attributes } = await serverClient().hotel.getHotel({ const { attributes } = await serverClient().hotel.getHotel({
hotelId: "d98c7ab1-ebaa-4102-b351-758daf1ddf55", hotelId: "d98c7ab1-ebaa-4102-b351-758daf1ddf55",
language: params.lang, language: getLang(),
}) })
const hotels = [attributes] const hotels = [attributes]

View File

@@ -5,16 +5,19 @@ import BedSelection from "@/components/HotelReservation/SelectRate/BedSelection"
import BreakfastSelection from "@/components/HotelReservation/SelectRate/BreakfastSelection" import BreakfastSelection from "@/components/HotelReservation/SelectRate/BreakfastSelection"
import FlexibilitySelection from "@/components/HotelReservation/SelectRate/FlexibilitySelection" import FlexibilitySelection from "@/components/HotelReservation/SelectRate/FlexibilitySelection"
import RoomSelection from "@/components/HotelReservation/SelectRate/RoomSelection" import RoomSelection from "@/components/HotelReservation/SelectRate/RoomSelection"
import { getLang, setLang } from "@/i18n/serverContext"
import styles from "./page.module.css" import styles from "./page.module.css"
import { LangParams, PageArgs } from "@/types/params" import { LangParams, PageArgs } from "@/types/params"
export default async function SelectRate({ params }: PageArgs<LangParams>) { export default async function SelectRate({ params }: PageArgs<LangParams>) {
setLang(params.lang)
// TODO: pass the correct hotel ID // TODO: pass the correct hotel ID
const { attributes: hotel } = await serverClient().hotel.getHotel({ const { attributes: hotel } = await serverClient().hotel.getHotel({
hotelId: "d98c7ab1-ebaa-4102-b351-758daf1ddf55", hotelId: "d98c7ab1-ebaa-4102-b351-758daf1ddf55",
language: params.lang, language: getLang(),
}) })
const rooms = await serverClient().hotel.getRates({ const rooms = await serverClient().hotel.getRates({
// TODO: pass the correct hotel ID and all other parameters that should be included in the search // TODO: pass the correct hotel ID and all other parameters that should be included in the search

View File

@@ -1,14 +1,9 @@
"use client" "use client"
import { useParams } from "next/navigation"
import { baseUrls } from "@/constants/routes/baseUrls" import { baseUrls } from "@/constants/routes/baseUrls"
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher" import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
import { LangParams } from "@/types/params"
export default function Error() { export default function Error() {
const params = useParams<LangParams>() return <LanguageSwitcher urls={baseUrls} />
return <LanguageSwitcher urls={baseUrls} lang={params.lang} />
} }

View File

@@ -1,11 +1,18 @@
import { serverClient } from "@/lib/trpc/server" import { serverClient } from "@/lib/trpc/server"
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher" import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
import { setLang } from "@/i18n/serverContext"
import { LangParams, PageArgs } from "@/types/params"
export default async function LanguageSwitcherRoute({
params,
}: PageArgs<LangParams>) {
setLang(params.lang)
export default async function LanguageSwitcherRoute() {
const data = await serverClient().contentstack.languageSwitcher.get() const data = await serverClient().contentstack.languageSwitcher.get()
if (!data) { if (!data) {
return null return null
} }
return <LanguageSwitcher urls={data.urls} lang={data.lang} /> return <LanguageSwitcher urls={data.urls} />
} }

View File

@@ -1,13 +1,15 @@
import { serverClient } from "@/lib/trpc/server" import { serverClient } from "@/lib/trpc/server"
import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown" import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown"
import { setLang } from "@/i18n/serverContext"
import { LangParams, PageArgs } from "@/types/params" import { LangParams, PageArgs } from "@/types/params"
export default async function MyPagesMobileDropdownPage({ export default async function MyPagesMobileDropdownPage({
params, params,
}: PageArgs<LangParams>) { }: PageArgs<LangParams>) {
setLang(params.lang)
const navigation = await serverClient().contentstack.myPages.navigation.get() const navigation = await serverClient().contentstack.myPages.navigation.get()
if (!navigation) return null if (!navigation) return null
return <MyPagesMobileDropdown navigation={navigation} lang={params.lang} /> return <MyPagesMobileDropdown navigation={navigation} />
} }

View File

@@ -1,17 +1,13 @@
import Header from "@/components/Current/Header" import Header from "@/components/Current/Header"
import { LangParams, PageArgs } from "@/types/params"
export default function HeaderLayout({ export default function HeaderLayout({
params,
languageSwitcher, languageSwitcher,
myPagesMobileDropdown, myPagesMobileDropdown,
}: PageArgs<LangParams> & { }: {
languageSwitcher: React.ReactNode languageSwitcher: React.ReactNode
} & { myPagesMobileDropdown: React.ReactNode }) { } & { myPagesMobileDropdown: React.ReactNode }) {
return ( return (
<Header <Header
lang={params.lang}
myPagesMobileDropdown={myPagesMobileDropdown} myPagesMobileDropdown={myPagesMobileDropdown}
languageSwitcher={languageSwitcher} languageSwitcher={languageSwitcher}
/> />

View File

@@ -4,18 +4,18 @@ import { serverClient } from "@/lib/trpc/server"
import Header from "@/components/Current/Header" import Header from "@/components/Current/Header"
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher" import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown" import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown"
import { setLang } from "@/i18n/serverContext"
import { LangParams, PageArgs } from "@/types/params" import { LangParams, PageArgs } from "@/types/params"
export default async function HeaderPage({ params }: PageArgs<LangParams>) { export default async function HeaderPage({ params }: PageArgs<LangParams>) {
setLang(params.lang)
const navigation = await serverClient().contentstack.myPages.navigation.get() const navigation = await serverClient().contentstack.myPages.navigation.get()
return ( return (
<Header <Header
lang={params.lang} myPagesMobileDropdown={<MyPagesMobileDropdown navigation={navigation} />}
myPagesMobileDropdown={ languageSwitcher={<LanguageSwitcher urls={baseUrls} />}
<MyPagesMobileDropdown navigation={navigation} lang={params.lang} />
}
languageSwitcher={<LanguageSwitcher urls={baseUrls} lang={params.lang} />}
/> />
) )
} }

View File

@@ -11,6 +11,7 @@ import VwoScript from "@/components/Current/VwoScript"
import { preloadUserTracking } from "@/components/TrackingSDK" import { preloadUserTracking } from "@/components/TrackingSDK"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import ServerIntlProvider from "@/i18n/Provider" import ServerIntlProvider from "@/i18n/Provider"
import { getLang, setLang } from "@/i18n/serverContext"
import type { Metadata } from "next" import type { Metadata } from "next"
@@ -30,11 +31,12 @@ export default async function RootLayout({
header: React.ReactNode header: React.ReactNode
} }
>) { >) {
setLang(params.lang)
preloadUserTracking() preloadUserTracking()
const { defaultLocale, locale, messages } = await getIntl() const { defaultLocale, locale, messages } = await getIntl()
return ( return (
<html lang={params.lang}> <html lang={getLang()}>
<head> <head>
<AdobeSDKScript /> <AdobeSDKScript />
<Script data-cookieconsent="ignore" src="/_static/js/cookie-bot.js" /> <Script data-cookieconsent="ignore" src="/_static/js/cookie-bot.js" />
@@ -53,10 +55,10 @@ export default async function RootLayout({
</head> </head>
<body> <body>
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}> <ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
<TrpcProvider lang={params.lang}> <TrpcProvider>
{header} {header}
{children} {children}
<Footer lang={params.lang} /> <Footer />
</TrpcProvider> </TrpcProvider>
</ServerIntlProvider> </ServerIntlProvider>
</body> </body>

View File

@@ -1,11 +1,10 @@
import { Lang } from "@/constants/languages"
import NotFound from "@/components/Current/NotFound" import NotFound from "@/components/Current/NotFound"
import { setLang } from "@/i18n/serverContext"
import { LangParams, PageArgs } from "@/types/params" import { LangParams, PageArgs } from "@/types/params"
export default function NotFoundPage({ params }: PageArgs<LangParams>) { export default function NotFoundPage({ params }: PageArgs<LangParams>) {
const lang = params.lang || Lang.en setLang(params.lang)
return <NotFound lang={lang} /> return <NotFound />
} }

View File

@@ -1,3 +1,5 @@
import { getLang, setLang } from "@/i18n/serverContext"
import styles from "./page.module.css" import styles from "./page.module.css"
import { LangParams, LayoutArgs, StatusParams } from "@/types/params" import { LangParams, LayoutArgs, StatusParams } from "@/types/params"
@@ -5,9 +7,11 @@ import { LangParams, LayoutArgs, StatusParams } from "@/types/params"
export default function MiddlewareError({ export default function MiddlewareError({
params, params,
}: LayoutArgs<LangParams & StatusParams>) { }: LayoutArgs<LangParams & StatusParams>) {
setLang(params.lang)
return ( return (
<div className={styles.layout}> <div className={styles.layout}>
Middleware Error {params.lang}: {params.status} Middleware Error {getLang()}: {params.status}
</div> </div>
) )
} }

View File

@@ -1,6 +1,10 @@
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { setLang } from "@/i18n/serverContext"
export default async function NotFound() { import { LangParams, PageArgs } from "@/types/params"
export default async function NotFound({ params }: PageArgs<LangParams>) {
setLang(params.lang)
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
return ( return (
<main> <main>

View File

@@ -1,11 +1,18 @@
import { serverClient } from "@/lib/trpc/server" import { serverClient } from "@/lib/trpc/server"
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher" import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
import { setLang } from "@/i18n/serverContext"
import { LangParams, PageArgs } from "@/types/params"
export default async function LanguageSwitcherRoute({
params,
}: PageArgs<LangParams>) {
setLang(params.lang)
export default async function LanguageSwitcherRoute() {
const data = await serverClient().contentstack.languageSwitcher.get() const data = await serverClient().contentstack.languageSwitcher.get()
if (!data) { if (!data) {
return null return null
} }
return <LanguageSwitcher urls={data.urls} lang={data.lang} /> return <LanguageSwitcher urls={data.urls} />
} }

View File

@@ -1,14 +1,9 @@
"use client" "use client"
import { useParams } from "next/navigation"
import { baseUrls } from "@/constants/routes/baseUrls" import { baseUrls } from "@/constants/routes/baseUrls"
import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher" import LanguageSwitcher from "@/components/Current/Header/LanguageSwitcher"
import { LangParams } from "@/types/params"
export default function Error() { export default function Error() {
const params = useParams<LangParams>() return <LanguageSwitcher urls={baseUrls} />
return <LanguageSwitcher urls={baseUrls} lang={params.lang} />
} }

View File

@@ -1,15 +1,18 @@
import { serverClient } from "@/lib/trpc/server" import { serverClient } from "@/lib/trpc/server"
import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown" import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown"
import { setLang } from "@/i18n/serverContext"
import { LangParams, PageArgs } from "@/types/params" import { LangParams, PageArgs } from "@/types/params"
export default async function MyPagesMobileDropdownPage({ export default async function MyPagesMobileDropdownPage({
params, params,
}: PageArgs<LangParams>) { }: PageArgs<LangParams>) {
setLang(params.lang)
const navigation = await serverClient().contentstack.myPages.navigation.get() const navigation = await serverClient().contentstack.myPages.navigation.get()
if (!navigation) { if (!navigation) {
return null return null
} }
return <MyPagesMobileDropdown navigation={navigation} lang={params.lang} /> return <MyPagesMobileDropdown navigation={navigation} />
} }

View File

@@ -1,12 +1,7 @@
"use client" "use client"
import { useParams } from "next/navigation"
import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown" import MyPagesMobileDropdown from "@/components/Current/Header/MyPagesMobileDropdown"
import { LangParams } from "@/types/params"
export default function Error() { export default function Error() {
const params = useParams<LangParams>() return <MyPagesMobileDropdown navigation={null} />
return <MyPagesMobileDropdown navigation={null} lang={params.lang} />
} }

View File

@@ -6,6 +6,7 @@ import { request } from "@/lib/graphql/request"
import ContentPage from "@/components/Current/ContentPage" import ContentPage from "@/components/Current/ContentPage"
import Tracking from "@/components/Current/Tracking" import Tracking from "@/components/Current/Tracking"
import { getLang, setLang } from "@/i18n/serverContext"
import type { LangParams, PageArgs, UriParams } from "@/types/params" import type { LangParams, PageArgs, UriParams } from "@/types/params"
import type { GetCurrentBlockPageData } from "@/types/requests/currentBlockPage" import type { GetCurrentBlockPageData } from "@/types/requests/currentBlockPage"
@@ -15,6 +16,8 @@ export default async function CurrentContentPage({
params, params,
searchParams, searchParams,
}: PageArgs<LangParams, UriParams>) { }: PageArgs<LangParams, UriParams>) {
setLang(params.lang)
try { try {
if (!searchParams.uri) { if (!searchParams.uri) {
throw new Error("Bad URI") throw new Error("Bad URI")
@@ -23,10 +26,10 @@ export default async function CurrentContentPage({
const response = await request<GetCurrentBlockPageData>( const response = await request<GetCurrentBlockPageData>(
GetCurrentBlockPage, GetCurrentBlockPage,
{ {
locale: params.lang, locale: getLang(),
url: searchParams.uri, url: searchParams.uri,
}, },
{ tags: [`${searchParams.uri}-${params.lang}`] } { tags: [`${searchParams.uri}-${getLang()}`] }
) )
if (!response.data?.all_current_blocks_page?.total) { if (!response.data?.all_current_blocks_page?.total) {
@@ -46,7 +49,7 @@ export default async function CurrentContentPage({
const pageData = response.data.all_current_blocks_page.items[0] const pageData = response.data.all_current_blocks_page.items[0]
const trackingData = { const trackingData = {
lang: params.lang, lang: getLang(),
publishedDate: pageData.system.updated_at, publishedDate: pageData.system.updated_at,
createdDate: pageData.system.created_at, createdDate: pageData.system.created_at,
pageId: pageData.system.uid, pageId: pageData.system.uid,

View File

@@ -10,6 +10,7 @@ import LangPopup from "@/components/Current/LangPopup"
import SkipToMainContent from "@/components/SkipToMainContent" import SkipToMainContent from "@/components/SkipToMainContent"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import ServerIntlProvider from "@/i18n/Provider" import ServerIntlProvider from "@/i18n/Provider"
import { getLang, setLang } from "@/i18n/serverContext"
import type { Metadata } from "next" import type { Metadata } from "next"
@@ -31,9 +32,11 @@ export default async function RootLayout({
myPagesMobileDropdown: React.ReactNode myPagesMobileDropdown: React.ReactNode
} }
>) { >) {
setLang(params.lang)
const { defaultLocale, locale, messages } = await getIntl() const { defaultLocale, locale, messages } = await getIntl()
return ( return (
<html lang={params.lang}> <html lang={getLang()}>
<head> <head>
{/* eslint-disable-next-line @next/next/no-css-tags */} {/* eslint-disable-next-line @next/next/no-css-tags */}
<link rel="stylesheet" href="/_static/css/core.css" /> <link rel="stylesheet" href="/_static/css/core.css" />
@@ -44,7 +47,7 @@ export default async function RootLayout({
strategy="beforeInteractive" strategy="beforeInteractive"
data-blockingmode="auto" data-blockingmode="auto"
data-cbid="6d539de8-3e67-4f0f-a0df-8cef9070f712" data-cbid="6d539de8-3e67-4f0f-a0df-8cef9070f712"
data-culture={params.lang} data-culture={getLang()}
id="Cookiebot" id="Cookiebot"
src="https://consent.cookiebot.com/uc.js" src="https://consent.cookiebot.com/uc.js"
/> />
@@ -60,16 +63,15 @@ export default async function RootLayout({
/> />
</head> </head>
<body className="theme-00Corecolours theme-X0Oldcorecolours"> <body className="theme-00Corecolours theme-X0Oldcorecolours">
<LangPopup lang={params.lang} /> <LangPopup />
<SkipToMainContent /> <SkipToMainContent />
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}> <ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
<Header <Header
lang={params.lang}
myPagesMobileDropdown={myPagesMobileDropdown} myPagesMobileDropdown={myPagesMobileDropdown}
languageSwitcher={languageSwitcher} languageSwitcher={languageSwitcher}
/> />
{children} {children}
<Footer lang={params.lang} /> <Footer />
</ServerIntlProvider> </ServerIntlProvider>
<Script id="page-tracking">{` <Script id="page-tracking">{`
typeof _satellite !== "undefined" && _satellite.pageBottom(); typeof _satellite !== "undefined" && _satellite.pageBottom();

View File

@@ -1,13 +1,5 @@
"use client"
import { useParams } from "next/navigation"
import NotFound from "@/components/Current/NotFound" import NotFound from "@/components/Current/NotFound"
import { LangParams } from "@/types/params"
export default function NotFoundPage() { export default function NotFoundPage() {
const { lang } = useParams<LangParams>() return <NotFound />
return <NotFound lang={lang} />
} }

View File

@@ -1,4 +1,5 @@
import InitLivePreview from "@/components/Current/LivePreview" import InitLivePreview from "@/components/Current/LivePreview"
import { getLang, setLang } from "@/i18n/serverContext"
import type { Metadata } from "next" import type { Metadata } from "next"
@@ -13,8 +14,10 @@ export default function RootLayout({
children, children,
params, params,
}: React.PropsWithChildren<LayoutArgs<LangParams>>) { }: React.PropsWithChildren<LayoutArgs<LangParams>>) {
setLang(params.lang)
return ( return (
<html lang={params.lang}> <html lang={getLang()}>
<body> <body>
<InitLivePreview /> <InitLivePreview />
{children} {children}

View File

@@ -1,3 +1,5 @@
import { getLang, setLang } from "@/i18n/serverContext"
import { import {
ContentTypeParams, ContentTypeParams,
LangParams, LangParams,
@@ -9,11 +11,13 @@ export default async function PreviewPage({
params, params,
searchParams, searchParams,
}: PageArgs<LangParams & ContentTypeParams & UIDParams, {}>) { }: PageArgs<LangParams & ContentTypeParams & UIDParams, {}>) {
setLang(params.lang)
return ( return (
<div> <div>
<p> <p>
Preview for {params.contentType}:{params.uid} in {params.lang} with Preview for {params.contentType}:{params.uid} in {getLang()} with params{" "}
params <pre>{JSON.stringify(searchParams, null, 2)}</pre> goes here <pre>{JSON.stringify(searchParams, null, 2)}</pre> goes here
</p> </p>
</div> </div>
) )

View File

@@ -4,6 +4,7 @@ import Footer from "@/components/Current/Footer"
import LangPopup from "@/components/Current/LangPopup" import LangPopup from "@/components/Current/LangPopup"
import InitLivePreview from "@/components/Current/LivePreview" import InitLivePreview from "@/components/Current/LivePreview"
import SkipToMainContent from "@/components/SkipToMainContent" import SkipToMainContent from "@/components/SkipToMainContent"
import { getLang, setLang } from "@/i18n/serverContext"
import type { Metadata } from "next" import type { Metadata } from "next"
@@ -20,8 +21,10 @@ export default function RootLayout({
children, children,
params, params,
}: React.PropsWithChildren<LayoutArgs<LangParams>>) { }: React.PropsWithChildren<LayoutArgs<LangParams>>) {
setLang(params.lang)
return ( return (
<html lang={params.lang}> <html lang={getLang()}>
<head> <head>
{/* eslint-disable-next-line @next/next/no-css-tags */} {/* eslint-disable-next-line @next/next/no-css-tags */}
<link rel="stylesheet" href="/_static/css/core.css" /> <link rel="stylesheet" href="/_static/css/core.css" />
@@ -30,10 +33,10 @@ export default function RootLayout({
</head> </head>
<body> <body>
<InitLivePreview /> <InitLivePreview />
<LangPopup lang={params.lang} /> <LangPopup />
<SkipToMainContent /> <SkipToMainContent />
{children} {children}
<Footer lang={params.lang} /> <Footer />
</body> </body>
</html> </html>
) )

View File

@@ -5,14 +5,18 @@ import { GetCurrentBlockPage } from "@/lib/graphql/Query/CurrentBlockPage.graphq
import ContentPage from "@/components/Current/ContentPage" import ContentPage from "@/components/Current/ContentPage"
import LoadingSpinner from "@/components/Current/LoadingSpinner" import LoadingSpinner from "@/components/Current/LoadingSpinner"
import { getLang, setLang } from "@/i18n/serverContext"
import type { LangParams, PageArgs, PreviewParams } from "@/types/params" import type { PageArgs, PreviewParams } from "@/types/params"
import { LangParams } from "@/types/params"
import type { GetCurrentBlockPageData } from "@/types/requests/currentBlockPage" import type { GetCurrentBlockPageData } from "@/types/requests/currentBlockPage"
export default async function CurrentPreviewPage({ export default async function CurrentPreviewPage({
params, params,
searchParams, searchParams,
}: PageArgs<LangParams, PreviewParams>) { }: PageArgs<LangParams, PreviewParams>) {
setLang(params.lang)
try { try {
ContentstackLivePreview.setConfigFromParams(searchParams) ContentstackLivePreview.setConfigFromParams(searchParams)
@@ -22,7 +26,7 @@ export default async function CurrentPreviewPage({
const response = await previewRequest<GetCurrentBlockPageData>( const response = await previewRequest<GetCurrentBlockPageData>(
GetCurrentBlockPage, GetCurrentBlockPage,
{ locale: params.lang, url: searchParams.uri } { locale: getLang(), url: searchParams.uri }
) )
if (!response.data?.all_current_blocks_page?.total) { if (!response.data?.all_current_blocks_page?.total) {

View File

@@ -4,6 +4,7 @@ import { serverClient } from "@/lib/trpc/server"
import AccountPage from "@/components/ContentType/Webviews/AccountPage" import AccountPage from "@/components/ContentType/Webviews/AccountPage"
import LoyaltyPage from "@/components/ContentType/Webviews/LoyaltyPage" import LoyaltyPage from "@/components/ContentType/Webviews/LoyaltyPage"
import { getLang, setLang } from "@/i18n/serverContext"
import { import {
ContentTypeWebviewParams, ContentTypeWebviewParams,
@@ -15,6 +16,7 @@ import {
export default async function ContentTypePage({ export default async function ContentTypePage({
params, params,
}: PageArgs<LangParams & ContentTypeWebviewParams & UIDParams, {}>) { }: PageArgs<LangParams & ContentTypeWebviewParams & UIDParams, {}>) {
setLang(params.lang)
const user = await serverClient().user.get() const user = await serverClient().user.get()
if (!user) { if (!user) {
@@ -26,15 +28,15 @@ export default async function ContentTypePage({
case "unauthorized": // fall through case "unauthorized": // fall through
case "forbidden": // fall through case "forbidden": // fall through
case "token_expired": case "token_expired":
redirect(`/${params.lang}/webview/refresh`) redirect(`/${getLang()}/webview/refresh`)
} }
} }
switch (params.contentType) { switch (params.contentType) {
case "loyalty-page": case "loyalty-page":
return <LoyaltyPage lang={params.lang} /> return <LoyaltyPage />
case "account-page": case "account-page":
return <AccountPage lang={params.lang} /> return <AccountPage />
default: default:
const type: never = params.contentType const type: never = params.contentType
console.error(`Unsupported content type given: ${type}`) console.error(`Unsupported content type given: ${type}`)

View File

@@ -8,6 +8,7 @@ import TrpcProvider from "@/lib/trpc/Provider"
import AdobeSDKScript from "@/components/Current/AdobeSDKScript" import AdobeSDKScript from "@/components/Current/AdobeSDKScript"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import ServerIntlProvider from "@/i18n/Provider" import ServerIntlProvider from "@/i18n/Provider"
import { getLang, setLang } from "@/i18n/serverContext"
import styles from "./layout.module.css" import styles from "./layout.module.css"
@@ -23,9 +24,11 @@ export default async function RootLayout({
children, children,
params, params,
}: React.PropsWithChildren<LayoutArgs<LangParams>>) { }: React.PropsWithChildren<LayoutArgs<LangParams>>) {
setLang(params.lang)
const { defaultLocale, locale, messages } = await getIntl() const { defaultLocale, locale, messages } = await getIntl()
return ( return (
<html lang={params.lang}> <html lang={getLang()}>
<head> <head>
<AdobeSDKScript /> <AdobeSDKScript />
<Script id="ensure-adobeDataLayer">{` <Script id="ensure-adobeDataLayer">{`
@@ -34,7 +37,7 @@ export default async function RootLayout({
</head> </head>
<body className={styles.layout}> <body className={styles.layout}>
<ServerIntlProvider intl={{ defaultLocale, locale, messages }}> <ServerIntlProvider intl={{ defaultLocale, locale, messages }}>
<TrpcProvider lang={params.lang}>{children}</TrpcProvider> <TrpcProvider>{children}</TrpcProvider>
</ServerIntlProvider> </ServerIntlProvider>
</body> </body>
</html> </html>

View File

@@ -1,8 +1,13 @@
import LoadingSpinner from "@/components/LoadingSpinner" import LoadingSpinner from "@/components/LoadingSpinner"
import { setLang } from "@/i18n/serverContext"
import styles from "./page.module.css" import styles from "./page.module.css"
export default function Refresh() { import { LangParams, PageArgs } from "@/types/params"
export default function Refresh({ params }: PageArgs<LangParams>) {
setLang(params.lang)
return ( return (
<div className={styles.container}> <div className={styles.container}>
<LoadingSpinner /> <LoadingSpinner />

View File

@@ -1,5 +1,7 @@
import { serverClient } from "@/lib/trpc/server" import { serverClient } from "@/lib/trpc/server"
import { getLang } from "@/i18n/serverContext"
import AmenitiesList from "./AmenitiesList" import AmenitiesList from "./AmenitiesList"
import IntroSection from "./IntroSection" import IntroSection from "./IntroSection"
import { Rooms } from "./Rooms" import { Rooms } from "./Rooms"
@@ -7,9 +9,7 @@ import TabNavigation from "./TabNavigation"
import styles from "./hotelPage.module.css" import styles from "./hotelPage.module.css"
import type { LangParams } from "@/types/params" export default async function HotelPage() {
export default async function HotelPage({ lang }: LangParams) {
const hotelPageIdentifierData = const hotelPageIdentifierData =
await serverClient().contentstack.hotelPage.get() await serverClient().contentstack.hotelPage.get()
@@ -19,7 +19,7 @@ export default async function HotelPage({ lang }: LangParams) {
const { attributes, roomCategories } = await serverClient().hotel.getHotel({ const { attributes, roomCategories } = await serverClient().hotel.getHotel({
hotelId: hotelPageIdentifierData.hotel_page_id, hotelId: hotelPageIdentifierData.hotel_page_id,
language: lang, language: getLang(),
include: ["RoomCategories"], include: ["RoomCategories"],
}) })

View File

@@ -8,9 +8,7 @@ import TrackingSDK from "@/components/TrackingSDK"
import styles from "./loyaltyPage.module.css" import styles from "./loyaltyPage.module.css"
import type { LangParams } from "@/types/params" export default async function LoyaltyPage() {
export default async function LoyaltyPage({ lang }: LangParams) {
const loyaltyPageRes = await serverClient().contentstack.loyaltyPage.get() const loyaltyPageRes = await serverClient().contentstack.loyaltyPage.get()
if (!loyaltyPageRes) { if (!loyaltyPageRes) {
@@ -23,14 +21,12 @@ export default async function LoyaltyPage({ lang }: LangParams) {
<> <>
<section className={styles.content}> <section className={styles.content}>
{loyaltyPage.sidebar.length ? ( {loyaltyPage.sidebar.length ? (
<Sidebar blocks={loyaltyPage.sidebar} lang={lang} /> <Sidebar blocks={loyaltyPage.sidebar} />
) : null} ) : null}
<MaxWidth className={styles.blocks} tag="main"> <MaxWidth className={styles.blocks} tag="main">
<Title>{loyaltyPage.heading}</Title> <Title>{loyaltyPage.heading}</Title>
{loyaltyPage.blocks ? ( {loyaltyPage.blocks ? <Blocks blocks={loyaltyPage.blocks} /> : null}
<Blocks blocks={loyaltyPage.blocks} lang={lang} />
) : null}
</MaxWidth> </MaxWidth>
</section> </section>

View File

@@ -8,12 +8,11 @@ import MaxWidth from "@/components/MaxWidth"
import Content from "@/components/MyPages/AccountPage/Webview/Content" import Content from "@/components/MyPages/AccountPage/Webview/Content"
import TrackingSDK from "@/components/TrackingSDK" import TrackingSDK from "@/components/TrackingSDK"
import LinkToOverview from "@/components/Webviews/LinkToOverview" import LinkToOverview from "@/components/Webviews/LinkToOverview"
import { getLang } from "@/i18n/serverContext"
import styles from "./accountPage.module.css" import styles from "./accountPage.module.css"
import { LangParams } from "@/types/params" export default async function MyPages() {
export default async function MyPages({ lang }: LangParams) {
const accountPageRes = await serverClient().contentstack.accountPage.get() const accountPageRes = await serverClient().contentstack.accountPage.get()
if (!accountPageRes) { if (!accountPageRes) {
@@ -22,13 +21,14 @@ export default async function MyPages({ lang }: LangParams) {
const { tracking, accountPage } = accountPageRes const { tracking, accountPage } = accountPageRes
const linkToOverview = `/${lang}/webview${accountPage.url}` !== overview[lang] const linkToOverview =
`/${getLang()}/webview${accountPage.url}` !== overview[getLang()]
return ( return (
<> <>
<MaxWidth className={styles.blocks} tag="main"> <MaxWidth className={styles.blocks} tag="main">
{linkToOverview ? <LinkToOverview lang={lang} /> : null} {linkToOverview ? <LinkToOverview /> : null}
<Content lang={lang} content={accountPage.content} /> <Content content={accountPage.content} />
</MaxWidth> </MaxWidth>
<TrackingSDK pageData={tracking} /> <TrackingSDK pageData={tracking} />

View File

@@ -8,9 +8,7 @@ import LinkToOverview from "@/components/Webviews/LinkToOverview"
import styles from "./loyaltyPage.module.css" import styles from "./loyaltyPage.module.css"
import { LangParams } from "@/types/params" export default async function AboutScandicFriends() {
export default async function AboutScandicFriends({ lang }: LangParams) {
const loyaltyPageRes = await serverClient().contentstack.loyaltyPage.get() const loyaltyPageRes = await serverClient().contentstack.loyaltyPage.get()
if (!loyaltyPageRes) { if (!loyaltyPageRes) {
@@ -21,10 +19,10 @@ export default async function AboutScandicFriends({ lang }: LangParams) {
return ( return (
<> <>
<section className={styles.content}> <section className={styles.content}>
<LinkToOverview lang={lang} /> <LinkToOverview />
<MaxWidth tag="main" className={styles.blocks}> <MaxWidth tag="main" className={styles.blocks}>
<Title>{loyaltyPage.heading}</Title> <Title>{loyaltyPage.heading}</Title>
<Blocks blocks={loyaltyPage.blocks} lang={lang} /> <Blocks blocks={loyaltyPage.blocks} />
</MaxWidth> </MaxWidth>
</section> </section>

View File

@@ -1,15 +1,16 @@
import { serverClient } from "@/lib/trpc/server" import { serverClient } from "@/lib/trpc/server"
import Image from "@/components/Image" import Image from "@/components/Image"
import { getLang } from "@/i18n/serverContext"
import Navigation from "./Navigation" import Navigation from "./Navigation"
import styles from "./footer.module.css" import styles from "./footer.module.css"
import { LangParams } from "@/types/params" export default async function Footer() {
const footerData = await serverClient().contentstack.base.footer({
export default async function Footer({ lang }: LangParams) { lang: getLang(),
const footerData = await serverClient().contentstack.base.footer({ lang }) })
if (!footerData) { if (!footerData) {
return null return null
} }

View File

@@ -4,15 +4,14 @@ import { useCallback, useEffect, useRef, useState } from "react"
import { Lang, languages } from "@/constants/languages" import { Lang, languages } from "@/constants/languages"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import useLang from "@/hooks/useLang"
import styles from "./desktop.module.css" import styles from "./desktop.module.css"
import type { LanguageSwitcherProps } from "@/types/components/current/languageSwitcher" import type { LanguageSwitcherProps } from "@/types/components/current/languageSwitcher"
export default function Desktop({ export default function Desktop({ urls }: LanguageSwitcherProps) {
currentLanguage, const currentLanguage = useLang()
urls,
}: LanguageSwitcherProps) {
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
const divRef = useRef<HTMLDivElement>(null) const divRef = useRef<HTMLDivElement>(null)

View File

@@ -3,14 +3,14 @@ import { useState } from "react"
import { Lang, languages } from "@/constants/languages" import { Lang, languages } from "@/constants/languages"
import useLang from "@/hooks/useLang"
import styles from "./mobile.module.css" import styles from "./mobile.module.css"
import type { LanguageSwitcherProps } from "@/types/components/current/languageSwitcher" import type { LanguageSwitcherProps } from "@/types/components/current/languageSwitcher"
export default function Mobile({ export default function Mobile({ urls }: LanguageSwitcherProps) {
currentLanguage, const currentLanguage = useLang()
urls,
}: LanguageSwitcherProps) {
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
function toggleOpen() { function toggleOpen() {

View File

@@ -1,19 +1,15 @@
import Desktop from "./Desktop" import Desktop from "./Desktop"
import Mobile from "./Mobile" import Mobile from "./Mobile"
import { LangParams } from "@/types/params"
import { LanguageSwitcherData } from "@/types/requests/languageSwitcher" import { LanguageSwitcherData } from "@/types/requests/languageSwitcher"
type LanguageSwitcherProps = LangParams & { urls: LanguageSwitcherData } type LanguageSwitcherProps = { urls: LanguageSwitcherData }
export default function LanguageSwitcher({ export default function LanguageSwitcher({ urls }: LanguageSwitcherProps) {
urls,
lang,
}: LanguageSwitcherProps) {
return ( return (
<> <>
<Desktop currentLanguage={lang} urls={urls} /> <Desktop urls={urls} />
<Mobile currentLanguage={lang} urls={urls} /> <Mobile urls={urls} />
</> </>
) )
} }

View File

@@ -7,26 +7,24 @@ import { login } from "@/constants/routes/handleAuth"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import { LinkProps } from "@/components/TempDesignSystem/Link/link" import { LinkProps } from "@/components/TempDesignSystem/Link/link"
import useLang from "@/hooks/useLang"
import { trackLoginClick } from "@/utils/tracking" import { trackLoginClick } from "@/utils/tracking"
import { TrackingPosition } from "@/types/components/tracking" import { TrackingPosition } from "@/types/components/tracking"
import { LangParams } from "@/types/params"
export default function LoginButton({ export default function LoginButton({
className, className,
position, position,
trackingId, trackingId,
lang,
children, children,
color = "black", color = "black",
}: PropsWithChildren< }: PropsWithChildren<{
LangParams & { className: string
className: string trackingId: string
trackingId: string position: TrackingPosition
position: TrackingPosition color?: LinkProps["color"]
color?: LinkProps["color"] }>) {
} const lang = useLang()
>) {
const pathName = usePathname() const pathName = usePathname()
useEffect(() => { useEffect(() => {

View File

@@ -8,6 +8,7 @@ import useDropdownStore from "@/stores/main-menu"
import Image from "@/components/Image" import Image from "@/components/Image"
import Avatar from "@/components/MyPages/Avatar" import Avatar from "@/components/MyPages/Avatar"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import useLang from "@/hooks/useLang"
import { trackClick } from "@/utils/tracking" import { trackClick } from "@/utils/tracking"
import BookingButton from "../BookingButton" import BookingButton from "../BookingButton"
@@ -27,9 +28,9 @@ export function MainMenu({
myPagesMobileDropdown, myPagesMobileDropdown,
bookingHref, bookingHref,
user, user,
lang,
}: MainMenuProps) { }: MainMenuProps) {
const intl = useIntl() const intl = useIntl()
const lang = useLang()
const { const {
isHamburgerMenuOpen, isHamburgerMenuOpen,
@@ -111,7 +112,6 @@ export function MainMenu({
position="hamburger menu" position="hamburger menu"
trackingId="loginStartHamburgerMenu" trackingId="loginStartHamburgerMenu"
className={styles.mobileLinkButton} className={styles.mobileLinkButton}
lang={lang}
> >
{intl.formatMessage({ id: "Log in" })} {intl.formatMessage({ id: "Log in" })}
</LoginButton> </LoginButton>

View File

@@ -2,7 +2,6 @@
import { Fragment } from "react" import { Fragment } from "react"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { Lang } from "@/constants/languages"
import { logout } from "@/constants/routes/handleAuth" import { logout } from "@/constants/routes/handleAuth"
import { navigationQueryRouter } from "@/server/routers/contentstack/myPages/navigation/query" import { navigationQueryRouter } from "@/server/routers/contentstack/myPages/navigation/query"
import useDropdownStore from "@/stores/main-menu" import useDropdownStore from "@/stores/main-menu"
@@ -10,6 +9,7 @@ import useDropdownStore from "@/stores/main-menu"
import Divider from "@/components/TempDesignSystem/Divider" import Divider from "@/components/TempDesignSystem/Divider"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import Title from "@/components/TempDesignSystem/Text/Title" import Title from "@/components/TempDesignSystem/Text/Title"
import useLang from "@/hooks/useLang"
import styles from "./my-pages-mobile-dropdown.module.css" import styles from "./my-pages-mobile-dropdown.module.css"
@@ -17,12 +17,11 @@ type Navigation = Awaited<ReturnType<(typeof navigationQueryRouter)["get"]>>
export default function MyPagesMobileDropdown({ export default function MyPagesMobileDropdown({
navigation, navigation,
lang,
}: { }: {
navigation: Navigation navigation: Navigation
lang: Lang | null
}) { }) {
const { formatMessage } = useIntl() const { formatMessage } = useIntl()
const lang = useLang()
const { toggleMyPagesMobileMenu, isMyPagesMobileMenuOpen } = const { toggleMyPagesMobileMenu, isMyPagesMobileMenuOpen } =
useDropdownStore() useDropdownStore()

View File

@@ -3,6 +3,7 @@ import { serverClient } from "@/lib/trpc/server"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import LoginButton from "../LoginButton" import LoginButton from "../LoginButton"
@@ -19,7 +20,6 @@ export default async function TopMenu({
homeHref, homeHref,
links, links,
languageSwitcher, languageSwitcher,
lang,
}: TopMenuProps) { }: TopMenuProps) {
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
const user = await serverClient().user.name() const user = await serverClient().user.name()
@@ -47,7 +47,7 @@ export default async function TopMenu({
<> <>
{user ? ( {user ? (
<Link <Link
href={logout[lang]} href={logout[getLang()]}
className={styles.sessionLink} className={styles.sessionLink}
prefetch={false} prefetch={false}
> >
@@ -56,7 +56,7 @@ export default async function TopMenu({
) : null} ) : null}
<div className={styles.loginSeparator} /> <div className={styles.loginSeparator} />
<Link <Link
href={logout[lang]} href={logout[getLang()]}
className={styles.sessionLink} className={styles.sessionLink}
prefetch={false} prefetch={false}
> >
@@ -68,7 +68,6 @@ export default async function TopMenu({
position="hamburger menu" position="hamburger menu"
trackingId="loginStartTopMeny" trackingId="loginStartTopMeny"
className={`${styles.sessionLink} ${styles.loginLink}`} className={`${styles.sessionLink} ${styles.loginLink}`}
lang={lang}
> >
{formatMessage({ id: "Log in" })} {formatMessage({ id: "Log in" })}
</LoginButton> </LoginButton>

View File

@@ -4,6 +4,7 @@ import { serverClient } from "@/lib/trpc/server"
import { auth } from "@/auth" import { auth } from "@/auth"
import { BookingWidget } from "@/components/BookingWidget" import { BookingWidget } from "@/components/BookingWidget"
import { getLang } from "@/i18n/serverContext"
import { MainMenu } from "./MainMenu" import { MainMenu } from "./MainMenu"
import OfflineBanner from "./OfflineBanner" import OfflineBanner from "./OfflineBanner"
@@ -11,17 +12,15 @@ import TopMenu from "./TopMenu"
import styles from "./header.module.css" import styles from "./header.module.css"
import { LangParams } from "@/types/params"
export default async function Header({ export default async function Header({
lang,
languageSwitcher, languageSwitcher,
myPagesMobileDropdown, myPagesMobileDropdown,
}: LangParams & { languageSwitcher: React.ReactNode } & { }: {
languageSwitcher: React.ReactNode
myPagesMobileDropdown: React.ReactNode myPagesMobileDropdown: React.ReactNode
}) { }) {
const data = await serverClient().contentstack.base.header({ const data = await serverClient().contentstack.base.header({
lang, lang: getLang(),
}) })
const user = await serverClient().user.name() const user = await serverClient().user.name()
@@ -35,7 +34,7 @@ export default async function Header({
return null return null
} }
const homeHref = homeHrefs[env.NODE_ENV][lang] const homeHref = homeHrefs[env.NODE_ENV][getLang()]
const { frontpage_link_text, logo, menu, top_menu } = data const { frontpage_link_text, logo, menu, top_menu } = data
const topMenuMobileLinks = top_menu.links const topMenuMobileLinks = top_menu.links
@@ -50,7 +49,6 @@ export default async function Header({
homeHref={homeHref} homeHref={homeHref}
links={top_menu.links} links={top_menu.links}
languageSwitcher={languageSwitcher} languageSwitcher={languageSwitcher}
lang={lang}
/> />
<MainMenu <MainMenu
frontpageLinkText={frontpage_link_text} frontpageLinkText={frontpage_link_text}
@@ -62,7 +60,6 @@ export default async function Header({
myPagesMobileDropdown={myPagesMobileDropdown} myPagesMobileDropdown={myPagesMobileDropdown}
bookingHref={homeHref} bookingHref={homeHref}
user={user} user={user}
lang={lang}
/> />
{hideBookingWidget ? null : <BookingWidget />} {hideBookingWidget ? null : <BookingWidget />}
</header> </header>

View File

@@ -2,7 +2,9 @@ import { headers } from "next/headers"
import { Lang, localeToLang } from "@/constants/languages" import { Lang, localeToLang } from "@/constants/languages"
export default function LangPopup({ lang }: { lang: Lang }) { import { getLang } from "@/i18n/serverContext"
export default function LangPopup() {
const headersList = headers() const headersList = headers()
const preferedLang = headersList.get("Accept-Language") ?? "" const preferedLang = headersList.get("Accept-Language") ?? ""
@@ -14,7 +16,7 @@ export default function LangPopup({ lang }: { lang: Lang }) {
const langOfChoice: Lang = localeToLang[preferedLang as Lang] const langOfChoice: Lang = localeToLang[preferedLang as Lang]
if (langOfChoice === lang) { if (langOfChoice === getLang()) {
return null return null
} }

View File

@@ -1,11 +1,11 @@
import { getLang } from "@/i18n/serverContext"
import { texts } from "./Texts" import { texts } from "./Texts"
import styles from "./notFound.module.css" import styles from "./notFound.module.css"
import { LangParams } from "@/types/params" export default function NotFound() {
const infoTexts = texts[getLang()]
export default function NotFound({ lang }: LangParams) {
const infoTexts = texts[lang]
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div className={styles.content}> <div className={styles.content}>

View File

@@ -8,6 +8,7 @@ import { membershipLevels } from "@/constants/membershipLevels"
import Image from "@/components/Image" import Image from "@/components/Image"
import Select from "@/components/TempDesignSystem/Select" import Select from "@/components/TempDesignSystem/Select"
import useLang from "@/hooks/useLang"
import { getMembership } from "@/utils/user" import { getMembership } from "@/utils/user"
import DA from "./data/DA.json" import DA from "./data/DA.json"
@@ -33,7 +34,6 @@ import {
OverviewTableProps, OverviewTableProps,
OverviewTableReducerAction, OverviewTableReducerAction,
} from "@/types/components/loyalty/blocks" } from "@/types/components/loyalty/blocks"
import { LangParams } from "@/types/params"
import type { User } from "@/types/user" import type { User } from "@/types/user"
const levelsTranslations = { const levelsTranslations = {
@@ -127,9 +127,9 @@ function reducer(state: any, action: OverviewTableReducerAction) {
export default function OverviewTable({ export default function OverviewTable({
activeMembership, activeMembership,
lang, }: OverviewTableProps) {
}: OverviewTableProps & LangParams) {
const intl = useIntl() const intl = useIntl()
const lang = useLang()
const levelsData = levelsTranslations[lang] const levelsData = levelsTranslations[lang]
const [selectionState, dispatch] = useReducer( const [selectionState, dispatch] = useReducer(
reducer, reducer,

View File

@@ -17,12 +17,8 @@ import type {
DynamicContentProps, DynamicContentProps,
} from "@/types/components/loyalty/blocks" } from "@/types/components/loyalty/blocks"
import { LoyaltyComponentEnum } from "@/types/components/loyalty/enums" import { LoyaltyComponentEnum } from "@/types/components/loyalty/enums"
import { LangParams } from "@/types/params"
async function DynamicComponentBlock({ async function DynamicComponentBlock({ component }: DynamicComponentProps) {
component,
lang,
}: DynamicComponentProps & LangParams) {
const membershipLevel = await serverClient().user.membershipLevel() const membershipLevel = await serverClient().user.membershipLevel()
switch (component) { switch (component) {
case LoyaltyComponentEnum.how_it_works: case LoyaltyComponentEnum.how_it_works:
@@ -30,7 +26,7 @@ async function DynamicComponentBlock({
case LoyaltyComponentEnum.loyalty_levels: case LoyaltyComponentEnum.loyalty_levels:
return <LoyaltyLevels /> return <LoyaltyLevels />
case LoyaltyComponentEnum.overview_table: case LoyaltyComponentEnum.overview_table:
return <OverviewTable activeMembership={membershipLevel} lang={lang} /> return <OverviewTable activeMembership={membershipLevel} />
default: default:
return null return null
} }
@@ -39,8 +35,7 @@ async function DynamicComponentBlock({
export default function DynamicContent({ export default function DynamicContent({
dynamicContent, dynamicContent,
firstItem, firstItem,
lang, }: DynamicContentProps) {
}: DynamicContentProps & LangParams) {
const displayHeader = !!( const displayHeader = !!(
dynamicContent.link || dynamicContent.link ||
dynamicContent.subtitle || dynamicContent.subtitle ||
@@ -63,7 +58,7 @@ export default function DynamicContent({
topTitle={firstItem} topTitle={firstItem}
/> />
) : null} ) : null}
<DynamicComponentBlock component={dynamicContent.component} lang={lang} /> <DynamicComponentBlock component={dynamicContent.component} />
{displayHeader && ( {displayHeader && (
<SectionLink link={dynamicContent.link} variant="mobile" /> <SectionLink link={dynamicContent.link} variant="mobile" />
)} )}

View File

@@ -1,15 +1,15 @@
import JsonToHtml from "@/components/JsonToHtml" import JsonToHtml from "@/components/JsonToHtml"
import DynamicContentBlock from "@/components/Loyalty/Blocks/DynamicContent" import DynamicContentBlock from "@/components/Loyalty/Blocks/DynamicContent"
import Shortcuts from "@/components/MyPages/Blocks/Shortcuts" import Shortcuts from "@/components/MyPages/Blocks/Shortcuts"
import { getLang } from "@/i18n/serverContext"
import { modWebviewLink } from "@/utils/webviews" import { modWebviewLink } from "@/utils/webviews"
import CardsGrid from "../CardsGrid" import CardsGrid from "../CardsGrid"
import type { BlocksProps } from "@/types/components/loyalty/blocks" import type { BlocksProps } from "@/types/components/loyalty/blocks"
import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums" import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums"
import { LangParams } from "@/types/params"
export function Blocks({ lang, blocks }: BlocksProps & LangParams) { export function Blocks({ blocks }: BlocksProps) {
return blocks.map((block, idx) => { return blocks.map((block, idx) => {
const firstItem = idx === 0 const firstItem = idx === 0
switch (block.__typename) { switch (block.__typename) {
@@ -35,7 +35,10 @@ export function Blocks({ lang, blocks }: BlocksProps & LangParams) {
link: block.dynamic_content.link link: block.dynamic_content.link
? { ? {
...block.dynamic_content.link, ...block.dynamic_content.link,
href: modWebviewLink(block.dynamic_content.link.href, lang), href: modWebviewLink(
block.dynamic_content.link.href,
getLang()
),
} }
: undefined, : undefined,
} }
@@ -45,13 +48,12 @@ export function Blocks({ lang, blocks }: BlocksProps & LangParams) {
dynamicContent={dynamicContent} dynamicContent={dynamicContent}
firstItem={firstItem} firstItem={firstItem}
key={`${block.dynamic_content.title}-${idx}`} key={`${block.dynamic_content.title}-${idx}`}
lang={lang}
/> />
) )
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts: case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts:
const shortcuts = block.shortcuts.shortcuts.map((shortcut) => ({ const shortcuts = block.shortcuts.shortcuts.map((shortcut) => ({
...shortcut, ...shortcut,
url: modWebviewLink(shortcut.url, lang), url: modWebviewLink(shortcut.url, getLang()),
})) }))
return ( return (
<Shortcuts <Shortcuts

View File

@@ -6,9 +6,8 @@ import CardsGrid from "./CardsGrid"
import type { BlocksProps } from "@/types/components/loyalty/blocks" import type { BlocksProps } from "@/types/components/loyalty/blocks"
import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums" import { LoyaltyBlocksTypenameEnum } from "@/types/components/loyalty/enums"
import { LangParams } from "@/types/params"
export function Blocks({ blocks, lang }: BlocksProps & LangParams) { export function Blocks({ blocks }: BlocksProps) {
return blocks.map((block, idx) => { return blocks.map((block, idx) => {
const firstItem = idx === 0 const firstItem = idx === 0
switch (block.__typename) { switch (block.__typename) {
@@ -27,7 +26,6 @@ export function Blocks({ blocks, lang }: BlocksProps & LangParams) {
dynamicContent={block.dynamic_content} dynamicContent={block.dynamic_content}
firstItem={firstItem} firstItem={firstItem}
key={`${block.dynamic_content.title}-${idx}`} key={`${block.dynamic_content.title}-${idx}`}
lang={lang}
/> />
) )
case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts: case LoyaltyBlocksTypenameEnum.LoyaltyPageBlocksShortcuts:

View File

@@ -14,12 +14,10 @@ import Contact from "./Contact"
import styles from "./joinLoyalty.module.css" import styles from "./joinLoyalty.module.css"
import type { JoinLoyaltyContactProps } from "@/types/components/loyalty/sidebar" import type { JoinLoyaltyContactProps } from "@/types/components/loyalty/sidebar"
import { LangParams } from "@/types/params"
export default async function JoinLoyaltyContact({ export default async function JoinLoyaltyContact({
block, block,
lang, }: JoinLoyaltyContactProps) {
}: JoinLoyaltyContactProps & LangParams) {
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
const user = await serverClient().user.name() const user = await serverClient().user.name()
@@ -55,7 +53,6 @@ export default async function JoinLoyaltyContact({
<Body>{formatMessage({ id: "Already a friend?" })}</Body> <Body>{formatMessage({ id: "Already a friend?" })}</Body>
<LoginButton <LoginButton
className={styles.link} className={styles.link}
lang={lang}
trackingId="loginJoinLoyalty" trackingId="loginJoinLoyalty"
position="join scandic friends sidebar" position="join scandic friends sidebar"
color="burgundy" color="burgundy"

View File

@@ -10,12 +10,8 @@ import {
SidebarTypenameEnum, SidebarTypenameEnum,
} from "@/types/components/loyalty/enums" } from "@/types/components/loyalty/enums"
import { SidebarProps } from "@/types/components/loyalty/sidebar" import { SidebarProps } from "@/types/components/loyalty/sidebar"
import { LangParams } from "@/types/params"
export default function SidebarLoyalty({ export default function SidebarLoyalty({ blocks }: SidebarProps) {
blocks,
lang,
}: SidebarProps & LangParams) {
return ( return (
<aside className={styles.aside}> <aside className={styles.aside}>
{blocks.map((block, idx) => { {blocks.map((block, idx) => {
@@ -37,18 +33,12 @@ export default function SidebarLoyalty({
<JoinLoyaltyContact <JoinLoyaltyContact
block={block.join_loyalty_contact} block={block.join_loyalty_contact}
key={`${block.__typename}-${idx}`} key={`${block.__typename}-${idx}`}
lang={lang}
/> />
) )
case SidebarTypenameEnum.LoyaltyPageSidebarDynamicContent: case SidebarTypenameEnum.LoyaltyPageSidebarDynamicContent:
switch (block.dynamic_content.component) { switch (block.dynamic_content.component) {
case LoyaltySidebarDynamicComponentEnum.my_pages_navigation: case LoyaltySidebarDynamicComponentEnum.my_pages_navigation:
return ( return <SidebarMyPages key={`${block.__typename}-${idx}`} />
<SidebarMyPages
key={`${block.__typename}-${idx}`}
lang={lang}
/>
)
default: default:
return null return null
} }

View File

@@ -7,6 +7,7 @@ import Shortcuts from "@/components/MyPages/Blocks/Shortcuts"
import PreviousStays from "@/components/MyPages/Blocks/Stays/Previous" import PreviousStays from "@/components/MyPages/Blocks/Stays/Previous"
import SoonestStays from "@/components/MyPages/Blocks/Stays/Soonest" import SoonestStays from "@/components/MyPages/Blocks/Stays/Soonest"
import UpcomingStays from "@/components/MyPages/Blocks/Stays/Upcoming" import UpcomingStays from "@/components/MyPages/Blocks/Stays/Upcoming"
import { getLang } from "@/i18n/serverContext"
import { removeMultipleSlashes } from "@/utils/url" import { removeMultipleSlashes } from "@/utils/url"
import PointsOverview from "../Blocks/Points/Overview" import PointsOverview from "../Blocks/Points/Overview"
@@ -47,7 +48,7 @@ function DynamicComponent({ component, props }: AccountPageContentProps) {
} }
} }
export default function Content({ lang, content }: ContentProps) { export default function Content({ content }: ContentProps) {
return content.map((item, idx) => { return content.map((item, idx) => {
switch (item.__typename) { switch (item.__typename) {
case ContentEntries.AccountPageContentDynamicContent: case ContentEntries.AccountPageContentDynamicContent:
@@ -57,14 +58,13 @@ export default function Content({ lang, content }: ContentProps) {
item.dynamic_content.link.linkConnection.edges[0].node item.dynamic_content.link.linkConnection.edges[0].node
.original_url || .original_url ||
removeMultipleSlashes( removeMultipleSlashes(
`/${lang}/${item.dynamic_content.link.linkConnection.edges[0].node.url}` `/${getLang()}/${item.dynamic_content.link.linkConnection.edges[0].node.url}`
), ),
text: item.dynamic_content.link.link_text, text: item.dynamic_content.link.link_text,
} }
: null : null
const componentProps = { const componentProps = {
lang,
title: item.dynamic_content.title, title: item.dynamic_content.title,
// TODO: rename preamble to subtitle in Contentstack? // TODO: rename preamble to subtitle in Contentstack?
subtitle: item.dynamic_content.preamble, subtitle: item.dynamic_content.preamble,

View File

@@ -1,6 +1,7 @@
import JsonToHtml from "@/components/JsonToHtml" import JsonToHtml from "@/components/JsonToHtml"
import Overview from "@/components/MyPages/Blocks/Overview" import Overview from "@/components/MyPages/Blocks/Overview"
import Shortcuts from "@/components/MyPages/Blocks/Shortcuts" import Shortcuts from "@/components/MyPages/Blocks/Shortcuts"
import { getLang } from "@/i18n/serverContext"
import { removeMultipleSlashes } from "@/utils/url" import { removeMultipleSlashes } from "@/utils/url"
import { modWebviewLink } from "@/utils/webviews" import { modWebviewLink } from "@/utils/webviews"
@@ -39,7 +40,7 @@ function DynamicComponent({ component, props }: AccountPageContentProps) {
} }
} }
export default function Content({ lang, content }: ContentProps) { export default function Content({ content }: ContentProps) {
return ( return (
<> <>
{content.map((item, idx) => { {content.map((item, idx) => {
@@ -62,7 +63,6 @@ export default function Content({ lang, content }: ContentProps) {
: null : null
const componentProps = { const componentProps = {
lang,
title: item.dynamic_content.title, title: item.dynamic_content.title,
// TODO: rename preamble to subtitle in Contentstack? // TODO: rename preamble to subtitle in Contentstack?
subtitle: item.dynamic_content.preamble, subtitle: item.dynamic_content.preamble,
@@ -79,7 +79,7 @@ export default function Content({ lang, content }: ContentProps) {
const shortcuts = item.shortcuts.shortcuts.map((shortcut) => { const shortcuts = item.shortcuts.shortcuts.map((shortcut) => {
return { return {
...shortcut, ...shortcut,
url: modWebviewLink(shortcut.url, lang), url: modWebviewLink(shortcut.url, getLang()),
} }
}) })
return ( return (

View File

@@ -6,20 +6,19 @@ import SectionHeader from "@/components/Section/Header"
import SectionLink from "@/components/Section/Link" import SectionLink from "@/components/Section/Link"
import Grids from "@/components/TempDesignSystem/Grids" import Grids from "@/components/TempDesignSystem/Grids"
import Title from "@/components/TempDesignSystem/Text/Title" import Title from "@/components/TempDesignSystem/Text/Title"
import { getLang } from "@/i18n/serverContext"
import { getMembershipLevelObject } from "@/utils/membershipLevel" import { getMembershipLevelObject } from "@/utils/membershipLevel"
import { getMembership } from "@/utils/user" import { getMembership } from "@/utils/user"
import styles from "./current.module.css" import styles from "./current.module.css"
import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage" import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage"
import type { LangParams } from "@/types/params"
export default async function CurrentBenefitsBlock({ export default async function CurrentBenefitsBlock({
title, title,
subtitle, subtitle,
link, link,
lang, }: AccountPageComponentProps) {
}: AccountPageComponentProps & LangParams) {
const user = await serverClient().user.get() const user = await serverClient().user.get()
// TAKE NOTE: we need clarification on how benefits stack from different levels // TAKE NOTE: we need clarification on how benefits stack from different levels
// in order to determine if a benefit is specific to a level or if it is a cumulative benefit // in order to determine if a benefit is specific to a level or if it is a cumulative benefit
@@ -35,7 +34,7 @@ export default async function CurrentBenefitsBlock({
const currentLevel = getMembershipLevelObject( const currentLevel = getMembershipLevelObject(
user.memberships[0].membershipLevel as MembershipLevelEnum, user.memberships[0].membershipLevel as MembershipLevelEnum,
lang getLang()
) )
if (!currentLevel) { if (!currentLevel) {
// TODO: handle this case? // TODO: handle this case?

View File

@@ -11,6 +11,7 @@ import Grids from "@/components/TempDesignSystem/Grids"
import Body from "@/components/TempDesignSystem/Text/Body" import Body from "@/components/TempDesignSystem/Text/Body"
import Title from "@/components/TempDesignSystem/Text/Title" import Title from "@/components/TempDesignSystem/Text/Title"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import { getMembershipLevelObject } from "@/utils/membershipLevel" import { getMembershipLevelObject } from "@/utils/membershipLevel"
import styles from "./next.module.css" import styles from "./next.module.css"
@@ -20,7 +21,6 @@ import { AccountPageComponentProps } from "@/types/components/myPages/myPage/acc
export default async function NextLevelBenefitsBlock({ export default async function NextLevelBenefitsBlock({
title, title,
subtitle, subtitle,
lang,
link, link,
}: AccountPageComponentProps) { }: AccountPageComponentProps) {
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
@@ -30,7 +30,7 @@ export default async function NextLevelBenefitsBlock({
} }
const nextLevel = getMembershipLevelObject( const nextLevel = getMembershipLevelObject(
user.memberships[0].nextLevel as MembershipLevelEnum, user.memberships[0].nextLevel as MembershipLevelEnum,
lang getLang()
) )
if (!nextLevel) { if (!nextLevel) {
// TODO: handle this case, when missing or when user is top level? // TODO: handle this case, when missing or when user is top level?

View File

@@ -1,5 +1,6 @@
import { MembershipLevelEnum } from "@/constants/membershipLevels" import { MembershipLevelEnum } from "@/constants/membershipLevels"
import { getLang } from "@/i18n/serverContext"
import { getMembershipLevelObject } from "@/utils/membershipLevel" import { getMembershipLevelObject } from "@/utils/membershipLevel"
import { getMembership } from "@/utils/user" import { getMembership } from "@/utils/user"
@@ -7,13 +8,12 @@ import PointsContainer from "./Container"
import { NextLevelPointsColumn, YourPointsColumn } from "./PointsColumn" import { NextLevelPointsColumn, YourPointsColumn } from "./PointsColumn"
import { UserProps } from "@/types/components/myPages/user" import { UserProps } from "@/types/components/myPages/user"
import { LangParams } from "@/types/params"
export default async function Points({ user, lang }: UserProps & LangParams) { export default async function Points({ user }: UserProps) {
const membership = getMembership(user.memberships) const membership = getMembership(user.memberships)
const nextLevel = getMembershipLevelObject( const nextLevel = getMembershipLevelObject(
membership?.nextLevel as MembershipLevelEnum, membership?.nextLevel as MembershipLevelEnum,
lang getLang()
) )
return ( return (

View File

@@ -6,12 +6,11 @@ import Points from "./Points"
import styles from "./stats.module.css" import styles from "./stats.module.css"
import type { UserProps } from "@/types/components/myPages/user" import type { UserProps } from "@/types/components/myPages/user"
import type { LangParams } from "@/types/params"
export default function Stats({ user, lang }: UserProps & LangParams) { export default function Stats({ user }: UserProps) {
return ( return (
<section className={styles.stats}> <section className={styles.stats}>
<Points user={user} lang={lang} /> <Points user={user} />
<Divider variant="default" color="pale" /> <Divider variant="default" color="pale" />
<ExpiringPoints user={user} /> <ExpiringPoints user={user} />
</section> </section>

View File

@@ -4,6 +4,7 @@ import SectionContainer from "@/components/Section/Container"
import SectionHeader from "@/components/Section/Header" import SectionHeader from "@/components/Section/Header"
import SectionLink from "@/components/Section/Link" import SectionLink from "@/components/Section/Link"
import Divider from "@/components/TempDesignSystem/Divider" import Divider from "@/components/TempDesignSystem/Divider"
import { getLang } from "@/i18n/serverContext"
import Hero from "./Friend/Hero" import Hero from "./Friend/Hero"
import Friend from "./Friend" import Friend from "./Friend"
@@ -12,14 +13,12 @@ import Stats from "./Stats"
import styles from "./overview.module.css" import styles from "./overview.module.css"
import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage" import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage"
import type { LangParams } from "@/types/params"
export default async function Overview({ export default async function Overview({
link, link,
subtitle, subtitle,
title, title,
lang, }: AccountPageComponentProps) {
}: AccountPageComponentProps & LangParams) {
const user = await serverClient().user.get() const user = await serverClient().user.get()
if (!user || "error" in user) { if (!user || "error" in user) {
return null return null
@@ -31,7 +30,7 @@ export default async function Overview({
<Hero color="red"> <Hero color="red">
<Friend user={user} color="burgundy" /> <Friend user={user} color="burgundy" />
<Divider className={styles.divider} color="peach" /> <Divider className={styles.divider} color="peach" />
<Stats user={user} lang={lang} /> <Stats user={user} />
</Hero> </Hero>
<SectionLink link={link} variant="mobile" /> <SectionLink link={link} variant="mobile" />
</SectionContainer> </SectionContainer>

View File

@@ -1,6 +1,7 @@
import { dt } from "@/lib/dt" import { dt } from "@/lib/dt"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import AwardPoints from "./AwardPoints" import AwardPoints from "./AwardPoints"
@@ -8,15 +9,17 @@ import styles from "./row.module.css"
import type { RowProps } from "@/types/components/myPages/myPage/earnAndBurn" import type { RowProps } from "@/types/components/myPages/myPage/earnAndBurn"
export default async function Row({ transaction, lang }: RowProps) { export default async function Row({ transaction }: RowProps) {
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
const description = const description =
transaction.hotelName && transaction.city transaction.hotelName && transaction.city
? `${transaction.hotelName}, ${transaction.city} ${transaction.nights} ${formatMessage({ id: "nights" })}` ? `${transaction.hotelName}, ${transaction.city} ${transaction.nights} ${formatMessage({ id: "nights" })}`
: `${transaction.nights} ${formatMessage({ id: "nights" })}` : `${transaction.nights} ${formatMessage({ id: "nights" })}`
const arrival = dt(transaction.checkinDate).locale(lang).format("DD MMM YYYY") const arrival = dt(transaction.checkinDate)
.locale(getLang())
.format("DD MMM YYYY")
const departure = dt(transaction.checkoutDate) const departure = dt(transaction.checkoutDate)
.locale(lang) .locale(getLang())
.format("DD MMM YYYY") .format("DD MMM YYYY")
return ( return (
<tr className={styles.tr}> <tr className={styles.tr}>

View File

@@ -15,7 +15,7 @@ const tableHeadings = [
"Points", "Points",
] ]
export default async function DesktopTable({ lang, transactions }: TableProps) { export default async function DesktopTable({ transactions }: TableProps) {
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
return ( return (
<div className={styles.container}> <div className={styles.container}>
@@ -35,7 +35,6 @@ export default async function DesktopTable({ lang, transactions }: TableProps) {
<tbody> <tbody>
{transactions.map((transaction) => ( {transactions.map((transaction) => (
<Row <Row
lang={lang}
key={transaction.confirmationNumber} key={transaction.confirmationNumber}
transaction={transaction} transaction={transaction}
/> />

View File

@@ -3,12 +3,13 @@ import { dt } from "@/lib/dt"
import AwardPoints from "@/components/MyPages/Blocks/Points/EarnAndBurn/Desktop/Row/AwardPoints" import AwardPoints from "@/components/MyPages/Blocks/Points/EarnAndBurn/Desktop/Row/AwardPoints"
import Body from "@/components/TempDesignSystem/Text/Body" import Body from "@/components/TempDesignSystem/Text/Body"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import styles from "./mobile.module.css" import styles from "./mobile.module.css"
import type { TableProps } from "@/types/components/myPages/myPage/earnAndBurn" import type { TableProps } from "@/types/components/myPages/myPage/earnAndBurn"
export default async function MobileTable({ lang, transactions }: TableProps) { export default async function MobileTable({ transactions }: TableProps) {
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
return ( return (
<div className={styles.container}> <div className={styles.container}>
@@ -32,7 +33,7 @@ export default async function MobileTable({ lang, transactions }: TableProps) {
<td className={`${styles.td} ${styles.transactionDetails}`}> <td className={`${styles.td} ${styles.transactionDetails}`}>
<span className={styles.transactionDate}> <span className={styles.transactionDate}>
{dt(transaction.checkinDate) {dt(transaction.checkinDate)
.locale(lang) .locale(getLang())
.format("DD MMM YYYY")} .format("DD MMM YYYY")}
</span> </span>
{transaction.hotelName && transaction.city ? ( {transaction.hotelName && transaction.city ? (

View File

@@ -10,7 +10,6 @@ import MobileTable from "./Mobile"
import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage" import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage"
export default async function EarnAndBurn({ export default async function EarnAndBurn({
lang,
link, link,
subtitle, subtitle,
title, title,
@@ -23,8 +22,8 @@ export default async function EarnAndBurn({
return ( return (
<SectionContainer> <SectionContainer>
<SectionHeader title={title} link={link} subtitle={subtitle} /> <SectionHeader title={title} link={link} subtitle={subtitle} />
<MobileTable lang={lang} transactions={transactions.data} /> <MobileTable transactions={transactions.data} />
<DesktopTable lang={lang} transactions={transactions.data} /> <DesktopTable transactions={transactions.data} />
<SectionLink link={link} variant="mobile" /> <SectionLink link={link} variant="mobile" />
</SectionContainer> </SectionContainer>
) )

View File

@@ -12,14 +12,12 @@ import Stats from "../../Overview/Stats"
import styles from "./overview.module.css" import styles from "./overview.module.css"
import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage" import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage"
import type { LangParams } from "@/types/params"
export default async function PointsOverview({ export default async function PointsOverview({
link, link,
subtitle, subtitle,
title, title,
lang, }: AccountPageComponentProps) {
}: AccountPageComponentProps & LangParams) {
const user = await serverClient().user.get() const user = await serverClient().user.get()
if (!user || "error" in user) { if (!user || "error" in user) {
return null return null
@@ -31,7 +29,7 @@ export default async function PointsOverview({
<Hero color="burgundy"> <Hero color="burgundy">
<Friend user={user} color="red" /> <Friend user={user} color="red" />
<Divider className={styles.divider} color="peach" /> <Divider className={styles.divider} color="peach" />
<Stats user={user} lang={lang} /> <Stats user={user} />
</Hero> </Hero>
<SectionLink link={link} variant="mobile" /> <SectionLink link={link} variant="mobile" />
</SectionContainer> </SectionContainer>

View File

@@ -16,7 +16,6 @@ import type {
export default function ClientPreviousStays({ export default function ClientPreviousStays({
initialPreviousStays, initialPreviousStays,
lang,
}: PreviousStaysClientProps) { }: PreviousStaysClientProps) {
const { data, isFetching, fetchNextPage, hasNextPage, isLoading } = const { data, isFetching, fetchNextPage, hasNextPage, isLoading } =
trpc.user.stays.previous.useInfiniteQuery( trpc.user.stays.previous.useInfiniteQuery(
@@ -49,11 +48,7 @@ export default function ClientPreviousStays({
<ListContainer> <ListContainer>
<Grids.Stackable> <Grids.Stackable>
{stays.map((stay) => ( {stays.map((stay) => (
<StayCard <StayCard key={stay.attributes.confirmationNumber} stay={stay} />
key={stay.attributes.confirmationNumber}
lang={lang}
stay={stay}
/>
))} ))}
</Grids.Stackable> </Grids.Stackable>
{hasNextPage ? ( {hasNextPage ? (

View File

@@ -10,7 +10,6 @@ import EmptyPreviousStaysBlock from "./EmptyPreviousStays"
import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage" import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage"
export default async function PreviousStays({ export default async function PreviousStays({
lang,
title, title,
subtitle, subtitle,
link, link,
@@ -23,10 +22,7 @@ export default async function PreviousStays({
<SectionContainer> <SectionContainer>
<SectionHeader title={title} subtitle={subtitle} link={link} /> <SectionHeader title={title} subtitle={subtitle} link={link} />
{initialPreviousStays.data.length ? ( {initialPreviousStays.data.length ? (
<ClientPreviousStays <ClientPreviousStays initialPreviousStays={initialPreviousStays} />
initialPreviousStays={initialPreviousStays}
lang={lang}
/>
) : ( ) : (
<EmptyPreviousStaysBlock /> <EmptyPreviousStaysBlock />
)} )}

View File

@@ -5,12 +5,11 @@ import { ArrowRightIcon } from "@/components/Icons"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import Title from "@/components/TempDesignSystem/Text/Title" import Title from "@/components/TempDesignSystem/Text/Title"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import styles from "./emptyUpcomingStays.module.css" import styles from "./emptyUpcomingStays.module.css"
import { LangParams } from "@/types/params" export default async function EmptyUpcomingStaysBlock() {
export default async function EmptyUpcomingStaysBlock({ lang }: LangParams) {
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
return ( return (
<section className={styles.container}> <section className={styles.container}>
@@ -23,7 +22,7 @@ export default async function EmptyUpcomingStaysBlock({ lang }: LangParams) {
</Title> </Title>
</div> </div>
<Link <Link
href={homeHrefs[env.NODE_ENV][lang]} href={homeHrefs[env.NODE_ENV][getLang()]}
className={styles.link} className={styles.link}
color="peach80" color="peach80"
> >

View File

@@ -11,7 +11,6 @@ import EmptyUpcomingStaysBlock from "./EmptyUpcomingStays"
import { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage" import { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage"
export default async function SoonestStays({ export default async function SoonestStays({
lang,
title, title,
subtitle, subtitle,
link, link,
@@ -27,15 +26,11 @@ export default async function SoonestStays({
{response.data.length ? ( {response.data.length ? (
<Grids.Stackable> <Grids.Stackable>
{response.data.map((stay) => ( {response.data.map((stay) => (
<StayCard <StayCard key={stay.attributes.confirmationNumber} stay={stay} />
key={stay.attributes.confirmationNumber}
lang={lang}
stay={stay}
/>
))} ))}
</Grids.Stackable> </Grids.Stackable>
) : ( ) : (
<EmptyUpcomingStaysBlock lang={lang} /> <EmptyUpcomingStaysBlock />
)} )}
<SectionLink link={link} variant="mobile" /> <SectionLink link={link} variant="mobile" />
</SectionContainer> </SectionContainer>

View File

@@ -5,12 +5,14 @@ import Image from "@/components/Image"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import Caption from "@/components/TempDesignSystem/Text/Caption" import Caption from "@/components/TempDesignSystem/Text/Caption"
import Title from "@/components/TempDesignSystem/Text/Title" import Title from "@/components/TempDesignSystem/Text/Title"
import useLang from "@/hooks/useLang"
import styles from "./stay.module.css" import styles from "./stay.module.css"
import type { StayCardProps } from "@/types/components/myPages/stays/stayCard" import type { StayCardProps } from "@/types/components/myPages/stays/stayCard"
export default function StayCard({ stay, lang }: StayCardProps) { export default function StayCard({ stay }: StayCardProps) {
const lang = useLang()
const { checkinDate, checkoutDate, hotelInformation, bookingUrl } = const { checkinDate, checkoutDate, hotelInformation, bookingUrl } =
stay.attributes stay.attributes

View File

@@ -16,7 +16,6 @@ import type {
export default function ClientUpcomingStays({ export default function ClientUpcomingStays({
initialUpcomingStays, initialUpcomingStays,
lang,
}: UpcomingStaysClientProps) { }: UpcomingStaysClientProps) {
const { data, isFetching, fetchNextPage, hasNextPage, isLoading } = const { data, isFetching, fetchNextPage, hasNextPage, isLoading } =
trpc.user.stays.upcoming.useInfiniteQuery( trpc.user.stays.upcoming.useInfiniteQuery(
@@ -49,11 +48,7 @@ export default function ClientUpcomingStays({
<ListContainer> <ListContainer>
<Grids.Stackable> <Grids.Stackable>
{stays.map((stay) => ( {stays.map((stay) => (
<StayCard <StayCard key={stay.attributes.confirmationNumber} stay={stay} />
key={stay.attributes.confirmationNumber}
lang={lang}
stay={stay}
/>
))} ))}
</Grids.Stackable> </Grids.Stackable>
{hasNextPage ? ( {hasNextPage ? (

View File

@@ -5,12 +5,11 @@ import { ArrowRightIcon } from "@/components/Icons"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import Title from "@/components/TempDesignSystem/Text/Title" import Title from "@/components/TempDesignSystem/Text/Title"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import styles from "./emptyUpcomingStays.module.css" import styles from "./emptyUpcomingStays.module.css"
import { LangParams } from "@/types/params" export default async function EmptyUpcomingStaysBlock() {
export default async function EmptyUpcomingStaysBlock({ lang }: LangParams) {
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
return ( return (
<section className={styles.container}> <section className={styles.container}>
@@ -23,7 +22,7 @@ export default async function EmptyUpcomingStaysBlock({ lang }: LangParams) {
</Title> </Title>
</div> </div>
<Link <Link
href={homeHrefs[env.NODE_ENV][lang]} href={homeHrefs[env.NODE_ENV][getLang()]}
className={styles.link} className={styles.link}
color="peach80" color="peach80"
> >

View File

@@ -10,7 +10,6 @@ import EmptyUpcomingStaysBlock from "./EmptyUpcomingStays"
import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage" import type { AccountPageComponentProps } from "@/types/components/myPages/myPage/accountPage"
export default async function UpcomingStays({ export default async function UpcomingStays({
lang,
title, title,
subtitle, subtitle,
link, link,
@@ -23,12 +22,9 @@ export default async function UpcomingStays({
<SectionContainer> <SectionContainer>
<SectionHeader title={title} subtitle={subtitle} link={link} /> <SectionHeader title={title} subtitle={subtitle} link={link} />
{initialUpcomingStays.data.length ? ( {initialUpcomingStays.data.length ? (
<ClientUpcomingStays <ClientUpcomingStays initialUpcomingStays={initialUpcomingStays} />
initialUpcomingStays={initialUpcomingStays}
lang={lang}
/>
) : ( ) : (
<EmptyUpcomingStaysBlock lang={lang} /> <EmptyUpcomingStaysBlock />
)} )}
<SectionLink link={link} variant="mobile" /> <SectionLink link={link} variant="mobile" />
</SectionContainer> </SectionContainer>

View File

@@ -7,12 +7,11 @@ import Divider from "@/components/TempDesignSystem/Divider"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle" import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import styles from "./sidebar.module.css" import styles from "./sidebar.module.css"
import type { LangParams } from "@/types/params" export default async function SidebarMyPages() {
export default async function SidebarMyPages({ lang }: LangParams) {
const navigation = await serverClient().contentstack.myPages.navigation.get() const navigation = await serverClient().contentstack.myPages.navigation.get()
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
if (!navigation) { if (!navigation) {
@@ -43,7 +42,7 @@ export default async function SidebarMyPages({ lang }: LangParams) {
<li> <li>
<Link <Link
color="burgundy" color="burgundy"
href={logout[lang]} href={logout[getLang()]}
prefetch={false} prefetch={false}
size="small" size="small"
variant="sidebar" variant="sidebar"

View File

@@ -4,15 +4,14 @@ import { overview } from "@/constants/routes/webviews"
import Link from "@/components/TempDesignSystem/Link" import Link from "@/components/TempDesignSystem/Link"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { getLang } from "@/i18n/serverContext"
import styles from "./linkToOverview.module.css" import styles from "./linkToOverview.module.css"
import type { LangParams } from "@/types/params" export default async function LinkToOverview() {
export default async function LinkToOverview({ lang }: LangParams) {
const { formatMessage } = await getIntl() const { formatMessage } = await getIntl()
return ( return (
<Link className={styles.overviewLink} href={overview[lang]}> <Link className={styles.overviewLink} href={overview[getLang()]}>
<ArrowLeft height={20} width={20} />{" "} <ArrowLeft height={20} width={20} />{" "}
{formatMessage({ id: "Go back to overview" })} {formatMessage({ id: "Go back to overview" })}
</Link> </Link>

12
hooks/useLang.ts Normal file
View File

@@ -0,0 +1,12 @@
"use client"
import { useParams } from "next/navigation"
import { LangParams } from "@/types/params"
/**
* A hook to get the current lang from the URL
*/
export default function useLang() {
const { lang } = useParams<LangParams>()
return lang
}

17
i18n/i18n.md Normal file
View File

@@ -0,0 +1,17 @@
# Internationalization
## The `lang` route parameter
All page paths starts with a language parameter, e.g. `/sv/utforska-scandic/wi-fi`.
### Get the language in a client component
We have a hook called `useLang` that directly returns the `lang` parameter from the path.
### Get the language in a server component
In order to not prop drill that all the way from a page we use React's `cache` in a way that resembles React`s context, but on the server side.
For this to work we must set the language with `setLang` on all root layouts and pages, including pages in parallel routes. Then we can use `getLang` in the components where we need the language.
This was inspired by [server-only-context](https://github.com/manvalls/server-only-context)

22
i18n/serverContext.ts Normal file
View File

@@ -0,0 +1,22 @@
import { cache } from "react"
import { Lang } from "@/constants/languages"
const getRef = cache(() => ({ current: Lang.en }))
/**
* Set the language for the current request
*
* It works kind of like React's context,
* but on the server side, per request.
*
* @param newLang
*/
export const setLang = (newLang: Lang) => {
getRef().current = newLang
}
/**
* Get the global language set for the current request
*/
export const getLang = () => getRef().current

View File

@@ -14,9 +14,9 @@ import { env } from "@/env/client"
import { SessionExpiredError } from "@/server/errors/trpc" import { SessionExpiredError } from "@/server/errors/trpc"
import { transformer } from "@/server/transformer" import { transformer } from "@/server/transformer"
import { trpc } from "./client" import useLang from "@/hooks/useLang"
import { LangParams } from "@/types/params" import { trpc } from "./client"
function initializeTrpcClient() { function initializeTrpcClient() {
// Locally we set nextjs to run on port to 3000 so that we always guarantee // Locally we set nextjs to run on port to 3000 so that we always guarantee
@@ -41,10 +41,8 @@ function initializeTrpcClient() {
}) })
} }
export default function TrpcProvider({ export default function TrpcProvider({ children }: React.PropsWithChildren) {
children, const lang = useLang()
lang,
}: React.PropsWithChildren<LangParams>) {
const [queryClient] = useState( const [queryClient] = useState(
() => () =>
new QueryClient({ new QueryClient({

View File

@@ -1,5 +1,3 @@
import { Lang } from "@/constants/languages"
import type { Image } from "@/types/image" import type { Image } from "@/types/image"
import type { import type {
CurrentHeaderLink, CurrentHeaderLink,
@@ -17,5 +15,4 @@ export type MainMenuProps = {
myPagesMobileDropdown: React.ReactNode | null myPagesMobileDropdown: React.ReactNode | null
bookingHref: string bookingHref: string
user: Pick<User, "firstName" | "lastName"> | null user: Pick<User, "firstName" | "lastName"> | null
lang: Lang
} }

View File

@@ -1,5 +1,3 @@
import { Lang } from "@/constants/languages"
import type { TopMenuHeaderLink } from "@/types/requests/currentHeader" import type { TopMenuHeaderLink } from "@/types/requests/currentHeader"
export type TopMenuProps = { export type TopMenuProps = {
@@ -7,5 +5,4 @@ export type TopMenuProps = {
homeHref: string homeHref: string
links: TopMenuHeaderLink[] links: TopMenuHeaderLink[]
languageSwitcher: React.ReactNode | null languageSwitcher: React.ReactNode | null
lang: Lang
} }

View File

@@ -8,6 +8,5 @@ export type LanguageSwitcherLink = {
} }
export type LanguageSwitcherProps = { export type LanguageSwitcherProps = {
currentLanguage: Lang
urls: LanguageSwitcherData urls: LanguageSwitcherData
} }

View File

@@ -6,11 +6,11 @@ import {
import { LangParams } from "@/types/params" import { LangParams } from "@/types/params"
export type SidebarProps = LangParams & { export type SidebarProps = {
blocks: Sidebar[] blocks: Sidebar[]
} }
export type JoinLoyaltyContactProps = LangParams & { export type JoinLoyaltyContactProps = {
block: JoinLoyaltyContact["join_loyalty_contact"] block: JoinLoyaltyContact["join_loyalty_contact"]
} }

View File

@@ -9,18 +9,15 @@ export type AccountPageContentProps = {
title: string | null title: string | null
subtitle: string | null subtitle: string | null
link?: { href: string; text: string } link?: { href: string; text: string }
lang: Lang
} }
} }
export type AccountPageComponentProps = { export type AccountPageComponentProps = {
lang: Lang
title: string | null title: string | null
subtitle: string | null subtitle: string | null
link?: { href: string; text: string } link?: { href: string; text: string }
} }
export type ContentProps = { export type ContentProps = {
lang: Lang
content: AccountPageContentItem[] content: AccountPageContentItem[]
} }

View File

@@ -24,12 +24,10 @@ export type EarnAndBurnProps = {
} }
export interface TableProps { export interface TableProps {
lang: Lang
transactions: Transactions transactions: Transactions
} }
export interface RowProps { export interface RowProps {
lang: Lang
transaction: Transaction transaction: Transaction
} }

View File

@@ -6,10 +6,11 @@ export type PreviousStaysResponse = Awaited<
> >
export type PreviousStaysNonNullResponseObject = export type PreviousStaysNonNullResponseObject =
NonNullable<PreviousStaysResponse> NonNullable<PreviousStaysResponse>
export type PreviousStays = NonNullable<PreviousStaysNonNullResponseObject>["data"] export type PreviousStays =
export type PreviousStay = NonNullable<PreviousStaysNonNullResponseObject>["data"][number] NonNullable<PreviousStaysNonNullResponseObject>["data"]
export type PreviousStay =
NonNullable<PreviousStaysNonNullResponseObject>["data"][number]
export interface PreviousStaysClientProps { export interface PreviousStaysClientProps {
lang: Lang
initialPreviousStays: PreviousStaysNonNullResponseObject initialPreviousStays: PreviousStaysNonNullResponseObject
} }

View File

@@ -1,7 +1,5 @@
import type { Lang } from "@/constants/languages"
import type { Stay } from "@/server/routers/user/output" import type { Stay } from "@/server/routers/user/output"
export type StayCardProps = { export type StayCardProps = {
lang: Lang
stay: Stay stay: Stay
} }

View File

@@ -6,10 +6,11 @@ export type UpcomingStaysResponse = Awaited<
> >
export type UpcomingStaysNonNullResponseObject = export type UpcomingStaysNonNullResponseObject =
NonNullable<UpcomingStaysResponse> NonNullable<UpcomingStaysResponse>
export type UpcomingStays = NonNullable<UpcomingStaysNonNullResponseObject>["data"] export type UpcomingStays =
export type UpcomingStay = NonNullable<UpcomingStaysNonNullResponseObject>["data"][number] NonNullable<UpcomingStaysNonNullResponseObject>["data"]
export type UpcomingStay =
NonNullable<UpcomingStaysNonNullResponseObject>["data"][number]
export interface UpcomingStaysClientProps { export interface UpcomingStaysClientProps {
lang: Lang
initialUpcomingStays: UpcomingStaysNonNullResponseObject initialUpcomingStays: UpcomingStaysNonNullResponseObject
} }