* move setLang() to a root layout

* fix: findLang only returns acceptable languages
* fix: fallback to use header x-lang if we haven't setLang yet
* fix: languageSchema, allow uppercase

Approved-by: Linus Flood
This commit is contained in:
Joakim Jäderberg
2025-02-19 09:06:37 +00:00
parent d27163a915
commit 873183ec2f
45 changed files with 159 additions and 117 deletions

View File

@@ -2,14 +2,11 @@ import { Suspense } from "react"
import Breadcrumbs from "@/components/Breadcrumbs"
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
import { setLang } from "@/i18n/serverContext"
import type { LangParams, PageArgs } from "@/types/params"
import { PageContentTypeEnum } from "@/types/requests/contentType"
export default function AllBreadcrumbs({ params }: PageArgs<LangParams>) {
setLang(params.lang)
export default function AllBreadcrumbs({}: PageArgs<LangParams>) {
return (
<Suspense fallback={<BreadcrumbsSkeleton />}>
<Breadcrumbs variant={PageContentTypeEnum.accountPage} />

View File

@@ -6,7 +6,6 @@ import Blocks from "@/components/Blocks"
import SectionHeader from "@/components/Section/Header"
import TrackingSDK from "@/components/TrackingSDK"
import { getIntl } from "@/i18n"
import { setLang } from "@/i18n/serverContext"
import styles from "./page.module.css"
@@ -14,11 +13,9 @@ import type { LangParams, PageArgs } from "@/types/params"
export { generateMetadata } from "@/utils/generateMetadata"
export default async function MyPages({
params,
}: PageArgs<LangParams & { path: string[] }>) {
setLang(params.lang)
export default async function MyPages({}: PageArgs<
LangParams & { path: string[] }
>) {
const accountPageRes = await serverClient().contentstack.accountPage.get()
const intl = await getIntl()

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,14 +16,12 @@ import Link from "@/components/TempDesignSystem/Link"
import Body from "@/components/TempDesignSystem/Text/Body"
import Title from "@/components/TempDesignSystem/Text/Title"
import { getIntl } from "@/i18n"
import { setLang } from "@/i18n/serverContext"
import styles from "./page.module.css"
import type { LangParams, PageArgs } from "@/types/params"
export default async function Profile({ params }: PageArgs<LangParams>) {
setLang(params.lang)
const intl = await getIntl()
const user = await getProfile()
if (!user || "error" in user) {

View File

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

View File

@@ -2,7 +2,6 @@ import { Suspense } from "react"
import Breadcrumbs from "@/components/Breadcrumbs"
import BreadcrumbsSkeleton from "@/components/TempDesignSystem/Breadcrumbs/BreadcrumbsSkeleton"
import { setLang } from "@/i18n/serverContext"
import type { ContentTypeParams, LangParams, PageArgs } from "@/types/params"
import { PageContentTypeEnum } from "@/types/requests/contentType"
@@ -16,8 +15,6 @@ const IGNORED_CONTENT_TYPES = [
export default function PageBreadcrumbs({
params,
}: PageArgs<LangParams & ContentTypeParams>) {
setLang(params.lang)
if (IGNORED_CONTENT_TYPES.includes(params.contentType)) {
return null
}

View File

@@ -16,7 +16,7 @@ import LoyaltyPage from "@/components/ContentType/LoyaltyPage"
import StartPage from "@/components/ContentType/StartPage"
import CollectionPage from "@/components/ContentType/StaticPages/CollectionPage"
import ContentPage from "@/components/ContentType/StaticPages/ContentPage"
import { getLang, setLang } from "@/i18n/serverContext"
import { getLang } from "@/i18n/serverContext"
import { isValidSession } from "@/utils/session"
import type {
@@ -33,8 +33,6 @@ export default async function ContentTypePage({
params,
searchParams,
}: PageArgs<LangParams & ContentTypeParams & UIDParams, { subpage?: string }>) {
setLang(params.lang)
const pathname = headers().get("x-pathname") || ""
switch (params.contentType) {

View File

@@ -1,15 +1,12 @@
import { getBookingConfirmation } from "@/lib/trpc/memoizedRequests"
import BookingConfirmation from "@/components/HotelReservation/BookingConfirmation"
import { setLang } from "@/i18n/serverContext"
import type { LangParams, PageArgs } from "@/types/params"
export default async function BookingConfirmationPage({
params,
searchParams,
}: PageArgs<LangParams, { confirmationNumber: string }>) {
setLang(params.lang)
void getBookingConfirmation(searchParams.confirmationNumber)
return (
<BookingConfirmation confirmationNumber={searchParams.confirmationNumber} />

View File

@@ -3,7 +3,6 @@ import { Suspense } from "react"
import { SelectHotelMapContainer } from "@/components/HotelReservation/SelectHotel/SelectHotelMap/SelectHotelMapContainer"
import { SelectHotelMapContainerSkeleton } from "@/components/HotelReservation/SelectHotel/SelectHotelMap/SelectHotelMapContainerSkeleton"
import { MapContainer } from "@/components/MapContainer"
import { setLang } from "@/i18n/serverContext"
import styles from "./page.module.css"
@@ -11,11 +10,8 @@ import type { AlternativeHotelsSearchParams } from "@/types/components/hotelRese
import type { LangParams, PageArgs } from "@/types/params"
export default async function SelectHotelMapPage({
params,
searchParams,
}: PageArgs<LangParams, AlternativeHotelsSearchParams>) {
setLang(params.lang)
return (
<div className={styles.main}>
<MapContainer>

View File

@@ -2,7 +2,6 @@ import { Suspense } from "react"
import SelectHotel from "@/components/HotelReservation/SelectHotel"
import { SelectHotelSkeleton } from "@/components/HotelReservation/SelectHotel/SelectHotelSkeleton"
import { setLang } from "@/i18n/serverContext"
import type { AlternativeHotelsSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
import type { LangParams, PageArgs } from "@/types/params"
@@ -11,8 +10,6 @@ export default async function AlternativeHotelsPage({
params,
searchParams,
}: PageArgs<LangParams, AlternativeHotelsSearchParams>) {
setLang(params.lang)
const roomKey = Object.keys(searchParams)
.filter((key) => key.startsWith("room["))
.map((key) => searchParams[key])

View File

@@ -21,7 +21,6 @@ import MobileSummary from "@/components/HotelReservation/EnterDetails/Summary/Mo
import { generateChildrenString } from "@/components/HotelReservation/utils"
import Title from "@/components/TempDesignSystem/Text/Title"
import { getIntl } from "@/i18n"
import { setLang } from "@/i18n/serverContext"
import EnterDetailsProvider from "@/providers/EnterDetailsProvider"
import { convertSearchParamsToObj } from "@/utils/url"
@@ -49,8 +48,6 @@ export default async function DetailsPage({
params: { lang },
searchParams,
}: PageArgs<LangParams, SelectRateSearchParams>) {
setLang(lang)
const intl = await getIntl()
const selectRoomParams = new URLSearchParams(searchParams)
const booking = convertSearchParamsToObj<SelectRateSearchParams>(searchParams)

View File

@@ -3,7 +3,6 @@ import { Suspense } from "react"
import { env } from "@/env/server"
import TrackingSDK from "@/components/TrackingSDK"
import { setLang } from "@/i18n/serverContext"
import styles from "./page.module.css"
@@ -14,8 +13,6 @@ import {
import type { LangParams, PageArgs } from "@/types/params"
export default function HotelReservationPage({ params }: PageArgs<LangParams>) {
setLang(params.lang)
if (!env.ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH) {
return null
}

View File

@@ -4,7 +4,6 @@ import { Suspense } from "react"
import { SelectHotelMapContainer } from "@/components/HotelReservation/SelectHotel/SelectHotelMap/SelectHotelMapContainer"
import { SelectHotelMapContainerSkeleton } from "@/components/HotelReservation/SelectHotel/SelectHotelMap/SelectHotelMapContainerSkeleton"
import { MapContainer } from "@/components/MapContainer"
import { setLang } from "@/i18n/serverContext"
import styles from "./page.module.css"
@@ -12,11 +11,8 @@ import type { SelectHotelSearchParams } from "@/types/components/hotelReservatio
import type { LangParams, PageArgs } from "@/types/params"
export default async function SelectHotelMapPage({
params,
searchParams,
}: PageArgs<LangParams, SelectHotelSearchParams>) {
setLang(params.lang)
const suspenseKey = stringify(searchParams)
return (

View File

@@ -3,7 +3,6 @@ import { Suspense } from "react"
import SelectHotel from "@/components/HotelReservation/SelectHotel"
import { SelectHotelSkeleton } from "@/components/HotelReservation/SelectHotel/SelectHotelSkeleton"
import { setLang } from "@/i18n/serverContext"
import type { SelectHotelSearchParams } from "@/types/components/hotelReservation/selectHotel/selectHotelSearchParams"
import type { LangParams, PageArgs } from "@/types/params"
@@ -12,8 +11,6 @@ export default async function SelectHotelPage({
params,
searchParams,
}: PageArgs<LangParams, SelectHotelSearchParams>) {
setLang(params.lang)
const suspenseKey = stringify(searchParams)
return (

View File

@@ -4,7 +4,6 @@ import { Suspense } from "react"
import SelectRate from "@/components/HotelReservation/SelectRate"
import { HotelInfoCardSkeleton } from "@/components/HotelReservation/SelectRate/HotelInfoCard"
import { RoomsContainerSkeleton } from "@/components/HotelReservation/SelectRate/RoomsContainer/RoomsContainerSkeleton"
import { setLang } from "@/i18n/serverContext"
import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
import type { LangParams, PageArgs } from "@/types/params"
@@ -13,8 +12,6 @@ export default async function SelectRatePage({
params,
searchParams,
}: PageArgs<LangParams & { section: string }, SelectRateSearchParams>) {
setLang(params.lang)
const suspenseKey = stringify(searchParams)
return (

View File

@@ -2,15 +2,12 @@ import { Suspense } from "react"
import { MyStay } from "@/components/HotelReservation/MyStay"
import { MyStaySkeleton } from "@/components/HotelReservation/MyStay/myStaySkeleton"
import { setLang } from "@/i18n/serverContext"
import type { LangParams, PageArgs } from "@/types/params"
export default async function MyStayPage({
params,
}: PageArgs<LangParams & { refId: string }>) {
setLang(params.lang)
return (
<Suspense fallback={<MyStaySkeleton />}>
<MyStay reservationId={params.refId} />

View File

@@ -0,0 +1,22 @@
"use client"
import { env } from "@/env/client"
export default function Error({
error,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<section>
<pre>Unable to render footer</pre>
{env.NEXT_PUBLIC_NODE_ENV === "development" && (
<div>
<div>{error.message}</div>
<pre>{error.stack}</pre>
</div>
)}
</section>
)
}

View File

@@ -0,0 +1,16 @@
import { env } from "@/env/server"
import CurrentFooter from "@/components/Current/Footer"
import Footer, { preload } from "@/components/Footer"
import type { LangParams, PageArgs } from "@/types/params"
export default function FooterSlot({}: PageArgs<LangParams>) {
if (env.HIDE_FOR_NEXT_RELEASE) {
return <CurrentFooter />
}
preload()
return <Footer />
}

View File

@@ -0,0 +1,22 @@
"use client"
import { env } from "@/env/client"
export default function Error({
error,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<section>
<pre>Unable to render header</pre>
{env.NEXT_PUBLIC_NODE_ENV === "development" && (
<div>
<div>{error.message}</div>
<pre>{error.stack}</pre>
</div>
)}
</section>
)
}

View File

@@ -0,0 +1,21 @@
import { Suspense } from "react"
import { env } from "@/env/server"
import CurrentHeader from "@/components/Current/Header"
import HeaderFallback from "@/components/Current/Header/HeaderFallback"
import Header from "@/components/Header"
import type { LangParams, PageArgs } from "@/types/params"
export default function HeaderPage({}: PageArgs<LangParams>) {
if (env.HIDE_FOR_NEXT_RELEASE) {
return (
<Suspense fallback={<HeaderFallback />}>
<CurrentHeader />
</Suspense>
)
}
return <Header />
}

View File

@@ -20,25 +20,22 @@ import GTMScript from "@/components/TrackingSDK/GTMScript"
import RouterTracking from "@/components/TrackingSDK/RouterTracking"
import { getIntl } from "@/i18n"
import ServerIntlProvider from "@/i18n/Provider"
import { setLang } from "@/i18n/serverContext"
import type { LangParams, LayoutArgs } from "@/types/params"
export default async function RootLayout({
bookingwidget,
children,
params,
}: React.PropsWithChildren<
LayoutArgs<LangParams> & {
bookingwidget: React.ReactNode
}
>) {
setLang(params.lang)
preloadUserTracking()
const { defaultLocale, locale, messages } = await getIntl()
return (
<html lang={params.lang}>
<>
<head>
<AdobeSDKScript />
<GTMScript />
@@ -73,6 +70,6 @@ export default async function RootLayout({
</ServerIntlProvider>
</SessionProvider>
</body>
</html>
</>
)
}

View File

@@ -1,10 +1,7 @@
import NotFound from "@/components/Current/NotFound"
import { setLang } from "@/i18n/serverContext"
import type { LangParams, PageArgs } from "@/types/params"
export default function NotFoundPage({ params }: PageArgs<LangParams>) {
setLang(params.lang)
export default function NotFoundPage({}: PageArgs<LangParams>) {
return <NotFound />
}

View File

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

View File

@@ -1,10 +1,7 @@
import Header from "@/components/Current/Header"
import { setLang } from "@/i18n/serverContext"
import type { LangParams, PageArgs } from "@/types/params"
export default async function HeaderPage({ params }: PageArgs<LangParams>) {
setLang(params.lang)
export default async function HeaderPage({}: PageArgs<LangParams>) {
return <Header />
}

View File

@@ -6,7 +6,6 @@ import { request } from "@/lib/graphql/request"
import ContentPage from "@/components/Current/ContentPage"
import Tracking from "@/components/Current/Tracking"
import { setLang } from "@/i18n/serverContext"
import type { LangParams, PageArgs, UriParams } from "@/types/params"
import type { GetCurrentBlockPageData } from "@/types/requests/currentBlockPage"
@@ -16,8 +15,6 @@ export default async function CurrentContentPage({
params,
searchParams,
}: PageArgs<LangParams, UriParams>) {
setLang(params.lang)
try {
if (!searchParams.uri) {
throw new Error("Bad URI")

View File

@@ -12,7 +12,6 @@ import SkipToMainContent from "@/components/SkipToMainContent"
import RouterTracking from "@/components/TrackingSDK/RouterTracking"
import { getIntl } from "@/i18n"
import ServerIntlProvider from "@/i18n/Provider"
import { setLang } from "@/i18n/serverContext"
import type { Metadata } from "next"
@@ -31,7 +30,6 @@ export default async function RootLayout({
}: React.PropsWithChildren<
LayoutArgs<LangParams> & { header: React.ReactNode }
>) {
setLang(params.lang)
const { defaultLocale, locale, messages } = await getIntl()
return (

View File

@@ -17,7 +17,6 @@ import GTMScript from "@/components/TrackingSDK/GTMScript"
import RouterTracking from "@/components/TrackingSDK/RouterTracking"
import { getIntl } from "@/i18n"
import ServerIntlProvider from "@/i18n/Provider"
import { setLang } from "@/i18n/serverContext"
import type { LangParams, LayoutArgs } from "@/types/params"
@@ -29,7 +28,6 @@ export default async function RootLayout({
return null
}
setLang(params.lang)
preloadUserTracking()
const { defaultLocale, locale, messages } = await getIntl()

16
app/[lang]/layout.tsx Normal file
View File

@@ -0,0 +1,16 @@
import { getLang, setLang } from "@/i18n/serverContext"
import type { Lang } from "@/constants/languages"
export default function Layout({
children,
params,
}: {
children: React.ReactNode
params: { lang: Lang }
}) {
setLang(params.lang)
const lang = getLang()
return <html lang={lang}>{children}</html>
}

View File

@@ -6,7 +6,6 @@ import { getProfile } from "@/lib/trpc/memoizedRequests"
import AccountPage from "@/components/Webviews/AccountPage"
import LoyaltyPage from "@/components/Webviews/LoyaltyPage"
import { getIntl } from "@/i18n"
import { setLang } from "@/i18n/serverContext"
import type {
ContentTypeWebviewParams,
@@ -18,7 +17,6 @@ import type {
export default async function ContentTypePage({
params,
}: PageArgs<LangParams & ContentTypeWebviewParams & UIDParams, {}>) {
setLang(params.lang)
const intl = await getIntl()
const user = await getProfile()

View File

@@ -10,7 +10,6 @@ import GTMScript from "@/components/TrackingSDK/GTMScript"
import RouterTracking from "@/components/TrackingSDK/RouterTracking"
import { getIntl } from "@/i18n"
import ServerIntlProvider from "@/i18n/Provider"
import { setLang } from "@/i18n/serverContext"
import styles from "./layout.module.css"
@@ -26,7 +25,6 @@ export default async function RootLayout({
children,
params,
}: React.PropsWithChildren<LayoutArgs<LangParams>>) {
setLang(params.lang)
const { defaultLocale, locale, messages } = await getIntl()
return (

View File

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

View File

@@ -5,7 +5,9 @@ import { texts } from "./Texts"
import styles from "./notFound.module.css"
export default function NotFound() {
const infoTexts = texts[getLang()]
const lang = getLang()
const infoTexts = texts[lang]
return (
<div className={styles.container}>
<div className={styles.content}>

View File

@@ -19,7 +19,7 @@
transform-origin: bottom;
}
.roomPanel>* {
.roomPanel > * {
overflow: hidden;
}
@@ -52,4 +52,4 @@ div.roomContainer p.subtitle {
.roomContainer {
padding: var(--Spacing-x2);
}
}
}

View File

@@ -1,12 +1,18 @@
"use client"
import { useParams } from "next/navigation"
import { LangParams } from "@/types/params"
import { Lang } from "@/constants/languages"
import { languageSchema } from "@/utils/languages"
import type { LangParams } from "@/types/params"
/**
* A hook to get the current lang from the URL
*/
export default function useLang() {
const { lang } = useParams<LangParams>()
return lang
const parsedLang = languageSchema.safeParse(lang)
return parsedLang.success ? parsedLang.data : Lang.en
}

View File

@@ -12,7 +12,7 @@ We have a hook called `useLang` that directly returns the `lang` parameter from
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.
For this to work we must set the language with `setLang` on the topmost layout
This was inspired by [server-only-context](https://github.com/manvalls/server-only-context)

View File

@@ -1,8 +1,13 @@
import "server-only"
import { headers } from "next/headers"
import { cache } from "react"
import { Lang } from "@/constants/languages"
const getRef = cache(() => ({ current: Lang.en }))
import { languageSchema } from "@/utils/languages"
const getRef = cache(() => ({ current: undefined as Lang | undefined }))
/**
* Set the language for the current request
@@ -13,12 +18,17 @@ const getRef = cache(() => ({ current: Lang.en }))
* @param newLang
*/
export function setLang(newLang: Lang) {
getRef().current = newLang
const parseResult = languageSchema.safeParse(newLang)
getRef().current = parseResult.success ? parseResult.data : Lang.en
}
/**
* Get the global language set for the current request
*/
export function getLang() {
return getRef().current
export function getLang(): Lang {
const contextLang = getRef().current
const headerLang = headers().get("x-lang") as Lang
const l = contextLang || headerLang || Lang.en
return l
}

View File

@@ -1,4 +1,4 @@
import { type NextMiddleware,NextResponse } from "next/server"
import { type NextMiddleware, NextResponse } from "next/server"
import {
myPages,

View File

@@ -1,7 +1,7 @@
import { z } from "zod"
import { MembershipLevelEnum } from "@/constants/membershipLevels"
import { Lang } from "@/constants/languages"
import { MembershipLevelEnum } from "@/constants/membershipLevels"
export const loyaltyLevelInput = z.object({
level: z.nativeEnum(MembershipLevelEnum),

View File

@@ -33,7 +33,7 @@ const outputSchema = z.object({
export const verifyOtp = protectedProcedure
.input(inputSchema)
.output(outputSchema)
.mutation(async function ({ ctx, input }) {
.mutation(async function ({ input }) {
const sasAuthToken = getSasToken()
if (!sasAuthToken) {

View File

@@ -57,6 +57,7 @@ export const contentstackBaseProcedure = baseProcedure.use(
if (!opts.ctx.lang) {
// When fetching data client side with TRPC we don't pass through middlewares and therefore do not get the lang through headers
// We can then pass lang as an input in the request and set it to the context in the procedure
const input = await opts.getRawInput()
const parsedInput = langInput.safeParse(input)
if (!parsedInput.success) {

View File

@@ -1,4 +1,3 @@
import type { LanguageSwitcherData } from "@/types/requests/languageSwitcher"
import type { Header } from "@/types/trpc/routers/contentstack/header"
export interface MobileMenuProps {

View File

@@ -1,7 +1,21 @@
import { z } from "zod"
import { Lang } from "@/constants/languages"
export function findLang(pathname: string) {
return Object.values(Lang).find(
const langFromPath = Object.values(Lang).find(
(l) => pathname.startsWith(`/${l}/`) || pathname === `/${l}`
)
const parsedLang = languageSchema.safeParse(langFromPath)
if (!parsedLang.success) {
return undefined
}
return parsedLang.data
}
export const languageSchema = z.preprocess(
(arg) => (typeof arg === "string" ? arg.toLowerCase() : arg),
z.nativeEnum(Lang)
)