From 44d4a42827554405617616842e10904a08d17d3e Mon Sep 17 00:00:00 2001 From: Gabriel Graves <64073162+NebraskaCoder@users.noreply.github.com> Date: Fri, 28 Nov 2025 15:32:23 -0600 Subject: [PATCH] Fix i18n caching issue by enabling static rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: - Disable localeDetection in middleware to prevent Accept-Language header reading - Add setRequestLocale() to layout and all pages for static rendering - Add generateStaticParams() to layout for pre-generating locale paths - Convert useTranslations to getTranslations in page components - Wrap TabsClient in Suspense boundary for useSearchParams compatibility This fixes the random locale switching issue on Vercel by allowing pages to be statically generated and cached per locale path. See: docs/i18n/caching-and-locale-detection.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/[locale]/about/[slug]/page.tsx | 5 ++++- app/[locale]/about/page.tsx | 13 ++++++++++--- app/[locale]/about/partners/page.tsx | 13 ++++++++++--- app/[locale]/about/sponsors/page.tsx | 13 ++++++++++--- app/[locale]/community/page.tsx | 15 +++++++++++---- app/[locale]/contribute/shop/page.tsx | 13 ++++++++++--- app/[locale]/download/components/Tabs.tsx | 13 +++++++++---- app/[locale]/download/page.tsx | 13 ++++++++++--- app/[locale]/layout.tsx | 9 ++++++++- app/[locale]/legal/[slug]/page.tsx | 5 ++++- app/[locale]/news/[slug]/page.tsx | 5 ++++- app/[locale]/news/page.tsx | 13 ++++++++++--- app/[locale]/page.tsx | 10 +++++++++- app/[locale]/resources/[slug]/page.tsx | 5 ++++- app/[locale]/support/support-providers/page.tsx | 13 ++++++++++--- middleware.ts | 4 +++- 16 files changed, 126 insertions(+), 36 deletions(-) diff --git a/app/[locale]/about/[slug]/page.tsx b/app/[locale]/about/[slug]/page.tsx index 171cd3a1..7fec051e 100644 --- a/app/[locale]/about/[slug]/page.tsx +++ b/app/[locale]/about/[slug]/page.tsx @@ -1,7 +1,9 @@ import { checkIfSlugIsValid, getContentData } from "@/lib/aboutPages"; import { notFound } from "next/navigation"; +import { setRequestLocale } from "next-intl/server"; export type Params = { + locale: string; slug: string; }; @@ -32,7 +34,8 @@ export async function generateMetadata({ params }: Props) { } export default async function Page({ params }: Props) { - const { slug } = await params; + const { locale, slug } = await params; + setRequestLocale(locale); if (!(await checkIfSlugIsValid(slug))) { notFound(); diff --git a/app/[locale]/about/page.tsx b/app/[locale]/about/page.tsx index 1e113547..11af884b 100644 --- a/app/[locale]/about/page.tsx +++ b/app/[locale]/about/page.tsx @@ -1,5 +1,5 @@ import LogoCloud from "@/components/logoCloud/LogoCloud"; -import { useTranslations } from "next-intl"; +import { getTranslations, setRequestLocale } from "next-intl/server"; import Image from "next/image"; import Link from "next/link"; import type { Metadata } from "next"; @@ -9,8 +9,15 @@ export const metadata: Metadata = { description: "It all started with a blog comment.", }; -const AboutPage = () => { - const t = useTranslations("about"); +type Props = { + params: Promise<{ locale: string }>; +}; + +const AboutPage = async ({ params }: Props) => { + const { locale } = await params; + setRequestLocale(locale); + + const t = await getTranslations("about"); return ( <> diff --git a/app/[locale]/about/partners/page.tsx b/app/[locale]/about/partners/page.tsx index 8f8a70a6..5b80b7a5 100644 --- a/app/[locale]/about/partners/page.tsx +++ b/app/[locale]/about/partners/page.tsx @@ -1,6 +1,6 @@ import React from "react"; import partnerSponsorData from "@/data/partnersSponsors"; -import { useTranslations } from "next-intl"; +import { getTranslations, setRequestLocale } from "next-intl/server"; import type { Metadata } from "next"; import Image from "next/image"; import { Badge } from "@/components/ui/badge"; @@ -11,7 +11,14 @@ export const metadata: Metadata = { "Our partners provide us with the resources we need. We wouldn't be here without their help!", }; -const PartnersPage = () => { +type Props = { + params: Promise<{ locale: string }>; +}; + +const PartnersPage = async ({ params }: Props) => { + const { locale } = await params; + setRequestLocale(locale); + const { partners } = partnerSponsorData[0]; const sortedTierOnePartners = partners.flatMap((partner) => partner.tierOne); @@ -19,7 +26,7 @@ const PartnersPage = () => { (partner) => partner.tierFour ); - const t = useTranslations("partners"); + const t = await getTranslations("partners"); return ( <> diff --git a/app/[locale]/about/sponsors/page.tsx b/app/[locale]/about/sponsors/page.tsx index 48606833..b0ff7fc8 100644 --- a/app/[locale]/about/sponsors/page.tsx +++ b/app/[locale]/about/sponsors/page.tsx @@ -1,6 +1,6 @@ import React from "react"; import partnerSponsorData from "@/data/partnersSponsors"; -import { useTranslations } from "next-intl"; +import { getTranslations, setRequestLocale } from "next-intl/server"; import type { Metadata } from "next"; import Image from "next/image"; import { Badge } from "@/components/ui/badge"; @@ -12,7 +12,14 @@ export const metadata: Metadata = { "Our sponsors provide us with financial backing. We wouldn't be here without their help!", }; -const SponsorsPage = () => { +type Props = { + params: Promise<{ locale: string }>; +}; + +const SponsorsPage = async ({ params }: Props) => { + const { locale } = await params; + setRequestLocale(locale); + const { sponsors } = partnerSponsorData[0]; const sortedTierOneSponsors = sponsors.flatMap((sponsor) => sponsor.tierOne); @@ -20,7 +27,7 @@ const SponsorsPage = () => { (sponsor) => sponsor.tierFour ); - const t = useTranslations("sponsors"); + const t = await getTranslations("sponsors"); return ( <> diff --git a/app/[locale]/community/page.tsx b/app/[locale]/community/page.tsx index 9b36253f..4303b104 100644 --- a/app/[locale]/community/page.tsx +++ b/app/[locale]/community/page.tsx @@ -1,4 +1,4 @@ -import { useTranslations } from "next-intl"; +import { getTranslations, setRequestLocale } from "next-intl/server"; import Link from "next/link"; import type { Metadata } from "next"; import Quiz from "./components/Quiz"; @@ -10,8 +10,15 @@ export const metadata: Metadata = { "Welcome! Find out how to get started in the Rocky Linux community.", }; -const AboutPage = () => { - const t = useTranslations("community"); +type Props = { + params: Promise<{ locale: string }>; +}; + +const CommunityPage = async ({ params }: Props) => { + const { locale } = await params; + setRequestLocale(locale); + + const t = await getTranslations("community"); const quizTranslations: QuizInterests = { chatting: t("interests.chatting.title"), @@ -222,4 +229,4 @@ const AboutPage = () => { ); }; -export default AboutPage; +export default CommunityPage; diff --git a/app/[locale]/contribute/shop/page.tsx b/app/[locale]/contribute/shop/page.tsx index ef88cab8..eebc1964 100644 --- a/app/[locale]/contribute/shop/page.tsx +++ b/app/[locale]/contribute/shop/page.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { useTranslations } from "next-intl"; +import { getTranslations, setRequestLocale } from "next-intl/server"; import type { Metadata } from "next"; import Image from "next/image"; @@ -30,8 +30,15 @@ const vendors = [ }, ]; -const ShopPage = () => { - const t = useTranslations("shop"); +type Props = { + params: Promise<{ locale: string }>; +}; + +const ShopPage = async ({ params }: Props) => { + const { locale } = await params; + setRequestLocale(locale); + + const t = await getTranslations("shop"); return ( <> diff --git a/app/[locale]/download/components/Tabs.tsx b/app/[locale]/download/components/Tabs.tsx index a5951151..3b3657aa 100644 --- a/app/[locale]/download/components/Tabs.tsx +++ b/app/[locale]/download/components/Tabs.tsx @@ -1,3 +1,4 @@ +import { Suspense } from "react"; import { useTranslations } from "next-intl"; import TabsClient from "./TabsClient"; @@ -90,10 +91,14 @@ const DownloadTabs = ({ downloadData }: DownloadTabsProps) => { ); return ( - + } + > + + ); }; diff --git a/app/[locale]/download/page.tsx b/app/[locale]/download/page.tsx index eaea6e28..2af60951 100644 --- a/app/[locale]/download/page.tsx +++ b/app/[locale]/download/page.tsx @@ -1,4 +1,4 @@ -import { useTranslations } from "next-intl"; +import { getTranslations, setRequestLocale } from "next-intl/server"; import downloadData from "@/data/downloads.json"; import DownloadTabs from "./components/Tabs"; @@ -13,8 +13,15 @@ export const metadata: Metadata = { description: "Get started and download Rocky Linux today!", }; -const DownloadPage = () => { - const t = useTranslations("download"); +type Props = { + params: Promise<{ locale: string }>; +}; + +const DownloadPage = async ({ params }: Props) => { + const { locale } = await params; + setRequestLocale(locale); + + const t = await getTranslations("download"); const typedDownloadData = downloadData as DownloadData; return ( <> diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index 0b2c7604..f0cf0b85 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -13,7 +13,7 @@ import { ThemeProvider } from "@/components/ThemeProvider"; import Header from "./Header"; import Footer from "./Footer"; import { NextIntlClientProvider } from "next-intl"; -import { getMessages } from "next-intl/server"; +import { getMessages, setRequestLocale } from "next-intl/server"; import type { Metadata } from "next"; @@ -37,12 +37,19 @@ const fontDisplay = FontDisplay({ variable: "--font-display", }); +export function generateStaticParams() { + return availableLanguages.map((locale) => ({ locale })); +} + export default async function RootLayout({ children, params, }: LayoutProps<"/[locale]">) { const { locale } = await params; + // Enable static rendering for this locale + setRequestLocale(locale); + if (!availableLanguages.includes(locale as Locale)) notFound(); const messages = await getMessages(); diff --git a/app/[locale]/legal/[slug]/page.tsx b/app/[locale]/legal/[slug]/page.tsx index 3cc6f054..0b09107a 100644 --- a/app/[locale]/legal/[slug]/page.tsx +++ b/app/[locale]/legal/[slug]/page.tsx @@ -1,7 +1,9 @@ import { checkIfSlugIsValid, getContentData } from "@/lib/legalPages"; import { notFound } from "next/navigation"; +import { setRequestLocale } from "next-intl/server"; export type Params = { + locale: string; slug: string; }; @@ -32,7 +34,8 @@ export async function generateMetadata({ params }: Props) { } export default async function Page({ params }: Props) { - const { slug } = await params; + const { locale, slug } = await params; + setRequestLocale(locale); if (!(await checkIfSlugIsValid(slug))) { notFound(); diff --git a/app/[locale]/news/[slug]/page.tsx b/app/[locale]/news/[slug]/page.tsx index 2fadeeda..ecfa7118 100644 --- a/app/[locale]/news/[slug]/page.tsx +++ b/app/[locale]/news/[slug]/page.tsx @@ -3,8 +3,10 @@ import ShareButtons from "@/components/shareButtons/ShareButtons"; import { checkIfSlugIsValid, getPostData } from "@/lib/news"; import { notFound } from "next/navigation"; +import { setRequestLocale } from "next-intl/server"; export type Params = { + locale: string; slug: string; }; @@ -36,7 +38,8 @@ export async function generateMetadata({ params }: Props) { } export default async function Post({ params }: Props) { - const { slug } = await params; + const { locale, slug } = await params; + setRequestLocale(locale); if (!(await checkIfSlugIsValid(slug))) { notFound(); diff --git a/app/[locale]/news/page.tsx b/app/[locale]/news/page.tsx index 17141bc9..922db469 100644 --- a/app/[locale]/news/page.tsx +++ b/app/[locale]/news/page.tsx @@ -1,6 +1,6 @@ -import type { NextPage, Route } from "next"; +import type { Route } from "next"; -import { getTranslations } from "next-intl/server"; +import { getTranslations, setRequestLocale } from "next-intl/server"; import Link from "next/link"; import { format } from "date-fns"; @@ -23,7 +23,14 @@ export async function generateMetadata() { }; } -const NewsPage: NextPage = async () => { +type Props = { + params: Promise<{ locale: string }>; +}; + +const NewsPage = async ({ params }: Props) => { + const { locale } = await params; + setRequestLocale(locale); + const posts = await getSortedPostsData(); const t = await getTranslations("news"); diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx index 275e7c0b..5692d15f 100644 --- a/app/[locale]/page.tsx +++ b/app/[locale]/page.tsx @@ -2,8 +2,16 @@ import Hero from "./Hero"; import FeatureOne from "./FeatureOne"; import NewsSnippet from "./NewsSnippet"; import LogoCloud from "@/components/logoCloud/LogoCloud"; +import { setRequestLocale } from "next-intl/server"; + +type Props = { + params: Promise<{ locale: string }>; +}; + +export default async function Home({ params }: Props) { + const { locale } = await params; + setRequestLocale(locale); -export default function Home() { return ( <> diff --git a/app/[locale]/resources/[slug]/page.tsx b/app/[locale]/resources/[slug]/page.tsx index 1f1d35a8..00a150f9 100644 --- a/app/[locale]/resources/[slug]/page.tsx +++ b/app/[locale]/resources/[slug]/page.tsx @@ -1,7 +1,9 @@ import { checkIfSlugIsValid, getContentData } from "@/lib/resourcesPages"; import { notFound } from "next/navigation"; +import { setRequestLocale } from "next-intl/server"; export type Params = { + locale: string; slug: string; }; @@ -32,7 +34,8 @@ export async function generateMetadata({ params }: Props) { } export default async function Page({ params }: Props) { - const { slug } = await params; + const { locale, slug } = await params; + setRequestLocale(locale); if (!(await checkIfSlugIsValid(slug))) { notFound(); diff --git a/app/[locale]/support/support-providers/page.tsx b/app/[locale]/support/support-providers/page.tsx index 3529a81e..c15acea3 100644 --- a/app/[locale]/support/support-providers/page.tsx +++ b/app/[locale]/support/support-providers/page.tsx @@ -1,6 +1,6 @@ import React from "react"; import partnerSponsorData from "@/data/partnersSponsors"; -import { useTranslations } from "next-intl"; +import { getTranslations, setRequestLocale } from "next-intl/server"; import type { Metadata } from "next"; import Image from "next/image"; import { Badge } from "@/components/ui/badge"; @@ -11,7 +11,14 @@ export const metadata: Metadata = { "The following sponsors provide commercial support for Rocky Linux.", }; -const SupportProvidersPage = () => { +type Props = { + params: Promise<{ locale: string }>; +}; + +const SupportProvidersPage = async ({ params }: Props) => { + const { locale } = await params; + setRequestLocale(locale); + const { sponsors } = partnerSponsorData[0]; const sortedTierOneSponsors = sponsors @@ -21,7 +28,7 @@ const SupportProvidersPage = () => { .flatMap((sponsor) => sponsor.tierFour) .filter((sponsor) => sponsor.supportProvider === true); - const t = useTranslations("supportProviders"); + const t = await getTranslations("supportProviders"); return ( <> diff --git a/middleware.ts b/middleware.ts index 324a7d96..5db2f3d8 100644 --- a/middleware.ts +++ b/middleware.ts @@ -6,7 +6,9 @@ export default createMiddleware({ locales: [...availableLanguages], localePrefix: "as-needed", defaultLocale: "en", - localeDetection: true, + // Disable Accept-Language header detection to enable static rendering + // See: docs/i18n/caching-and-locale-detection.md + localeDetection: false, }); export const config = {