From 384a187e06039a1f02c5a51cd4b980ca0f6260f6 Mon Sep 17 00:00:00 2001
From: laaichiu <134155205+laaichiu@users.noreply.github.com>
Date: Sun, 9 Aug 2026 14:56:07 +0800
Subject: [PATCH 1/3] fix: support lowercase regional locale routes
---
src/app/[country]/[locale]/layout.test.tsx | 21 ++++++++++++
src/components/layout/RegionPreferences.tsx | 28 ++++++++++++----
.../__tests__/RegionPreferences.test.tsx | 32 +++++++++++++++++++
src/hooks/__tests__/useCountrySwitch.test.ts | 30 +++++++++++++++++
src/hooks/useCountrySwitch.ts | 9 ++++--
src/i18n/__tests__/locales.test.ts | 3 ++
src/i18n/__tests__/markets.test.ts | 14 ++++++++
src/i18n/__tests__/routing.test.ts | 10 ++++++
src/i18n/locales.ts | 1 +
src/i18n/normalize.ts | 5 +++
src/i18n/routing.ts | 6 +++-
src/lib/spree/middleware.test.ts | 16 ++++++++++
12 files changed, 164 insertions(+), 11 deletions(-)
diff --git a/src/app/[country]/[locale]/layout.test.tsx b/src/app/[country]/[locale]/layout.test.tsx
index 615a1c56..1a5eb0d9 100644
--- a/src/app/[country]/[locale]/layout.test.tsx
+++ b/src/app/[country]/[locale]/layout.test.tsx
@@ -143,6 +143,27 @@ describe("CountryLocaleLayout Market fallback", () => {
});
});
+ it("renders a Market configured with en-GB at the lowercase route", async () => {
+ mocks.getMarkets.mockResolvedValue({
+ data: [
+ market({
+ default_locale: "en-GB",
+ supported_locales: [],
+ country_isos: ["GB"],
+ countries: [country("GB")],
+ }),
+ ],
+ });
+
+ await expect(
+ CountryLocaleLayoutContent({
+ children: ,
+ params: Promise.resolve({ country: "gb", locale: "en-gb" }),
+ }),
+ ).resolves.toBeDefined();
+ expect(mocks.redirect).not.toHaveBeenCalled();
+ });
+
it("redirects an unknown country to the default Market and keeps the page", async () => {
mocks.getMarkets.mockResolvedValue({ data: [market()] });
mocks.headers.mockResolvedValue(
diff --git a/src/components/layout/RegionPreferences.tsx b/src/components/layout/RegionPreferences.tsx
index 4f65444d..d24d6155 100644
--- a/src/components/layout/RegionPreferences.tsx
+++ b/src/components/layout/RegionPreferences.tsx
@@ -25,6 +25,7 @@ import {
} from "@/components/ui/native-select";
import { type CountryWithMarket, useStore } from "@/contexts/StoreContext";
import { useCountrySwitch } from "@/hooks/useCountrySwitch";
+import { toRouteLocale } from "@/i18n/normalize";
import { cn } from "@/lib/utils";
interface RegionPreferencesProps {
@@ -65,9 +66,20 @@ function getCountry(
}
function getSupportedLocales(entry: CountryWithMarket): string[] {
- return entry.supported_locales.length > 0
- ? entry.supported_locales
- : [entry.default_locale];
+ const locales =
+ entry.supported_locales.length > 0
+ ? entry.supported_locales
+ : [entry.default_locale];
+
+ return Array.from(
+ new Set(
+ locales.map(
+ (locale) =>
+ toRouteLocale(locale) ??
+ locale.trim().replaceAll("_", "-").toLowerCase(),
+ ),
+ ),
+ );
}
export function RegionPreferences({ variant }: RegionPreferencesProps) {
@@ -88,7 +100,7 @@ export function RegionPreferences({ variant }: RegionPreferencesProps) {
getCountry(countries, draftCountry) ?? getCountry(countries, country);
const localeOptions = selectedCountry
? getSupportedLocales(selectedCountry)
- : [locale];
+ : [toRouteLocale(locale) ?? locale];
const languageDisplayNames = useMemo(() => {
try {
return new Intl.DisplayNames([locale], { type: "language" });
@@ -113,9 +125,11 @@ export function RegionPreferences({ variant }: RegionPreferencesProps) {
const supportedLocales = getSupportedLocales(entry);
setDraftCountry(nextCountry);
setDraftLocale((currentLocale) =>
- supportedLocales.includes(currentLocale)
- ? currentLocale
- : entry.default_locale || supportedLocales[0],
+ supportedLocales.includes(
+ toRouteLocale(currentLocale) ?? currentLocale.toLowerCase(),
+ )
+ ? (toRouteLocale(currentLocale) ?? currentLocale.toLowerCase())
+ : supportedLocales[0],
);
setSwitchError(false);
}
diff --git a/src/components/layout/__tests__/RegionPreferences.test.tsx b/src/components/layout/__tests__/RegionPreferences.test.tsx
index c1c56d05..d71d6d0d 100644
--- a/src/components/layout/__tests__/RegionPreferences.test.tsx
+++ b/src/components/layout/__tests__/RegionPreferences.test.tsx
@@ -48,6 +48,14 @@ const countries = [
supported_locales: ["en", "fr"],
marketId: "market-ca",
},
+ {
+ iso: "GB",
+ name: "United Kingdom",
+ currency: "GBP",
+ default_locale: "en-GB",
+ supported_locales: [],
+ marketId: "market-gb",
+ },
] as CountryWithMarket[];
describe("RegionPreferences", () => {
@@ -97,6 +105,30 @@ describe("RegionPreferences", () => {
expect(handleCountrySelect).toHaveBeenCalledWith(countries[1], "en");
});
+ it("uses a lowercase regional locale in the language selector", async () => {
+ const user = userEvent.setup();
+ const handleCountrySelect = vi.fn().mockResolvedValue(true);
+ mockUseCountrySwitch.mockReturnValue({
+ handleCountrySelect,
+ isCartLoading: false,
+ isCountryNavigating: false,
+ });
+
+ render();
+ await user.click(
+ screen.getByRole("button", { name: "Region and language" }),
+ );
+ await user.selectOptions(screen.getByLabelText("Region"), "gb");
+
+ expect(screen.getByLabelText("Language")).toHaveValue("en-gb");
+
+ await user.click(
+ screen.getByRole("button", { name: "Update preferences" }),
+ );
+
+ expect(handleCountrySelect).toHaveBeenCalledWith(countries[2], "en-gb");
+ });
+
it("disables submission while the cart is loading", async () => {
const user = userEvent.setup();
mockUseCountrySwitch.mockReturnValue({
diff --git a/src/hooks/__tests__/useCountrySwitch.test.ts b/src/hooks/__tests__/useCountrySwitch.test.ts
index 4e575953..98e79989 100644
--- a/src/hooks/__tests__/useCountrySwitch.test.ts
+++ b/src/hooks/__tests__/useCountrySwitch.test.ts
@@ -121,6 +121,36 @@ describe("useCountrySwitch", () => {
expect(mockAssign).toHaveBeenCalledWith("/de/de/products");
});
+ it("normalizes a regional locale to a lowercase route", async () => {
+ const regionalCountry = {
+ iso: "GB",
+ currency: "GBP",
+ default_locale: "en-GB",
+ supported_locales: [],
+ } as unknown as CountryWithMarket;
+ const { result } = renderHook(() =>
+ useCountrySwitch({
+ currentCountry: "us",
+ currentLocale: "en",
+ }),
+ );
+ const mockAssign = vi.fn();
+ vi.stubGlobal("window", {
+ location: { assign: mockAssign, hash: "", search: "" },
+ });
+
+ await act(async () => {
+ await result.current.handleCountrySelect(regionalCountry);
+ });
+
+ expect(mockUpdateCartMarket).toHaveBeenCalledWith("cart-1", {
+ currency: "GBP",
+ locale: "en-gb",
+ });
+ expect(mockSetStoreCookies).toHaveBeenCalledWith("gb", "en-gb");
+ expect(mockAssign).toHaveBeenCalledWith("/gb/en-gb/products");
+ });
+
it("rebases a login return target when switching market", async () => {
const { rebaseAccountRedirectSearch } = await import(
"@/lib/utils/account-redirect"
diff --git a/src/hooks/useCountrySwitch.ts b/src/hooks/useCountrySwitch.ts
index 63baea4b..590943e1 100644
--- a/src/hooks/useCountrySwitch.ts
+++ b/src/hooks/useCountrySwitch.ts
@@ -4,6 +4,7 @@ import { usePathname } from "next/navigation";
import { useState } from "react";
import { useCart } from "@/contexts/CartContext";
import type { CountryWithMarket } from "@/contexts/StoreContext";
+import { toRouteLocale } from "@/i18n/normalize";
import { updateCartMarket } from "@/lib/data/checkout";
import { rebaseAccountRedirectSearch } from "@/lib/utils/account-redirect";
import { setStoreCookies } from "@/lib/utils/cookies";
@@ -39,13 +40,15 @@ export function useCountrySwitch({
): Promise => {
const nextCountry = entry.iso.toLowerCase();
const activeCountry = currentCountry.toLowerCase();
- const newLocale = locale || entry.default_locale || "en";
+ const newLocale =
+ toRouteLocale(locale || entry.default_locale || "en") ?? "en";
+ const activeLocale = toRouteLocale(currentLocale) ?? currentLocale;
if (isCountryNavigating) {
return false;
}
- if (nextCountry === activeCountry && newLocale === currentLocale) {
+ if (nextCountry === activeCountry && newLocale === activeLocale) {
return true;
}
@@ -58,7 +61,7 @@ export function useCountrySwitch({
const newCurrency = entry.currency;
const pathRest = getPathWithoutPrefix(pathname);
const newPath = `/${nextCountry}/${newLocale}${pathRest}`;
- const currentBasePath = `/${activeCountry}/${currentLocale}`;
+ const currentBasePath = `/${activeCountry}/${activeLocale}`;
const nextBasePath = `/${nextCountry}/${newLocale}`;
try {
diff --git a/src/i18n/__tests__/locales.test.ts b/src/i18n/__tests__/locales.test.ts
index 3b482116..b932d229 100644
--- a/src/i18n/__tests__/locales.test.ts
+++ b/src/i18n/__tests__/locales.test.ts
@@ -9,11 +9,13 @@ import {
matchLocale,
negotiateAcceptLanguage,
negotiateLocale,
+ toRouteLocale,
} from "@/i18n/normalize";
describe("locale configuration", () => {
it("resolves configured locales case-insensitively", () => {
expect(resolveSupportedLocale("EN")).toBe("en");
+ expect(resolveSupportedLocale("en-GB")).toBe("en-gb");
expect(resolveSupportedLocale("it")).toBeUndefined();
expect(SUPPORTED_LOCALES).toContain(DEFAULT_LOCALE);
});
@@ -22,6 +24,7 @@ describe("locale configuration", () => {
expect(canonicalizeLocale("zh_cn")).toBe("zh-CN");
expect(canonicalizeLocale("sr_latn_rs")).toBe("sr-Latn-RS");
expect(canonicalizeLocale("not_a_locale_!")).toBeUndefined();
+ expect(toRouteLocale("en-GB")).toBe("en-gb");
});
it("preserves configured spelling and negotiates a base language", () => {
diff --git a/src/i18n/__tests__/markets.test.ts b/src/i18n/__tests__/markets.test.ts
index 093b3caa..84eee77c 100644
--- a/src/i18n/__tests__/markets.test.ts
+++ b/src/i18n/__tests__/markets.test.ts
@@ -67,4 +67,18 @@ describe("Market locale routes", () => {
locale: "en",
});
});
+
+ it("renders a regional Market locale with a lowercase route segment", () => {
+ const current = market({
+ default_locale: "en-GB",
+ supported_locales: [],
+ countries: [country("GB")],
+ });
+
+ expect(getMarketLocales(current)).toEqual(["en-gb"]);
+ expect(isLocaleEnabledForMarket(current, "en-GB")).toBe(true);
+ expect(getMarketLocaleTargets([current])).toEqual([
+ { marketId: "market-1", country: "gb", locale: "en-gb" },
+ ]);
+ });
});
diff --git a/src/i18n/__tests__/routing.test.ts b/src/i18n/__tests__/routing.test.ts
index e3993fbf..e024e8e2 100644
--- a/src/i18n/__tests__/routing.test.ts
+++ b/src/i18n/__tests__/routing.test.ts
@@ -18,4 +18,14 @@ describe("localized route fallback", () => {
"/us/en",
);
});
+
+ it("normalizes regional locale redirects to lowercase", () => {
+ expect(
+ buildLocalizedRedirectPath({
+ country: "GB",
+ locale: "en-GB",
+ pathname: "/gb/en/products",
+ }),
+ ).toBe("/gb/en-gb/products");
+ });
});
diff --git a/src/i18n/locales.ts b/src/i18n/locales.ts
index f9588e35..ac0bd26f 100644
--- a/src/i18n/locales.ts
+++ b/src/i18n/locales.ts
@@ -9,6 +9,7 @@ import { canonicalizeLocale, matchLocale } from "@/i18n/normalize";
const MESSAGE_LOADERS = {
de: () => import("../../messages/de.json"),
en: () => import("../../messages/en.json"),
+ "en-gb": () => import("../../messages/en.json"),
es: () => import("../../messages/es.json"),
fr: () => import("../../messages/fr.json"),
pl: () => import("../../messages/pl.json"),
diff --git a/src/i18n/normalize.ts b/src/i18n/normalize.ts
index 62581953..ab1c774e 100644
--- a/src/i18n/normalize.ts
+++ b/src/i18n/normalize.ts
@@ -11,6 +11,11 @@ export function canonicalizeLocale(
}
}
+/** Convert a locale to the lowercase form used in storefront URL segments. */
+export function toRouteLocale(value: string | undefined): string | undefined {
+ return canonicalizeLocale(value)?.toLowerCase();
+}
+
/** Match a locale case-insensitively while preserving the configured spelling. */
export function matchLocale(
value: string | undefined,
diff --git a/src/i18n/routing.ts b/src/i18n/routing.ts
index 3199d2e3..8f707129 100644
--- a/src/i18n/routing.ts
+++ b/src/i18n/routing.ts
@@ -1,3 +1,5 @@
+import { toRouteLocale } from "@/i18n/normalize";
+
export const REQUEST_PATHNAME_HEADER = "x-spree-request-pathname";
export const REQUEST_SEARCH_HEADER = "x-spree-request-search";
@@ -17,7 +19,9 @@ export function buildLocalizedRedirectPath({
pathname,
search,
}: LocalizedRedirectParams): string {
- const prefix = `/${country.toLowerCase()}/${locale}`;
+ const normalizedLocale =
+ toRouteLocale(locale) ?? locale.trim().replaceAll("_", "-").toLowerCase();
+ const prefix = `/${country.toLowerCase()}/${normalizedLocale}`;
const matchedPrefix = pathname?.match(LOCALIZED_PREFIX)?.[0];
const suffix = matchedPrefix ? pathname?.slice(matchedPrefix.length) : "";
const normalizedSearch = search?.startsWith("?") ? search : "";
diff --git a/src/lib/spree/middleware.test.ts b/src/lib/spree/middleware.test.ts
index 8183f6d5..d7a27199 100644
--- a/src/lib/spree/middleware.test.ts
+++ b/src/lib/spree/middleware.test.ts
@@ -19,6 +19,22 @@ describe("Spree locale middleware", () => {
);
});
+ it("keeps regional default locales in lowercase route segments", () => {
+ const regionalMiddleware = createSpreeMiddleware({
+ defaultCountry: "gb",
+ defaultLocale: "en-GB",
+ supportedLocales: ["en", "en-gb"],
+ });
+
+ const response = regionalMiddleware(
+ new NextRequest("https://store.example/"),
+ );
+
+ expect(response.headers.get("location")).toBe(
+ "https://store.example/gb/en-gb",
+ );
+ });
+
it("redirects an unsupported storefront locale without dropping the path", () => {
const response = middleware(
new NextRequest("https://store.example/ar/it/products/coffee"),
From 63122d19227eea06ef2c3d770f80d9379a4f008c Mon Sep 17 00:00:00 2001
From: laaichiu <134155205+laaichiu@users.noreply.github.com>
Date: Sun, 9 Aug 2026 15:12:41 +0800
Subject: [PATCH 2/3] fix: sync cart with canonical regional locale
---
src/hooks/__tests__/useCountrySwitch.test.ts | 41 ++++++++++++++++++++
src/hooks/useCountrySwitch.ts | 10 ++++-
2 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/src/hooks/__tests__/useCountrySwitch.test.ts b/src/hooks/__tests__/useCountrySwitch.test.ts
index 98e79989..e4a1ec3a 100644
--- a/src/hooks/__tests__/useCountrySwitch.test.ts
+++ b/src/hooks/__tests__/useCountrySwitch.test.ts
@@ -151,6 +151,47 @@ describe("useCountrySwitch", () => {
expect(mockAssign).toHaveBeenCalledWith("/gb/en-gb/products");
});
+ it("updates a cart that still has the legacy regional locale casing", async () => {
+ const refreshCart = vi.fn().mockResolvedValue(undefined);
+ const regionalCountry = {
+ iso: "GB",
+ currency: "GBP",
+ default_locale: "en-GB",
+ supported_locales: [],
+ } as unknown as CountryWithMarket;
+ mockUseCart.mockReturnValue({
+ cart: {
+ id: "cart-1",
+ currency: "GBP",
+ locale: "en-GB",
+ } as never,
+ loading: false,
+ refreshCart,
+ } as never);
+
+ const { result } = renderHook(() =>
+ useCountrySwitch({
+ currentCountry: "gb",
+ currentLocale: "en-gb",
+ }),
+ );
+ const mockAssign = vi.fn();
+ vi.stubGlobal("window", {
+ location: { assign: mockAssign, hash: "", search: "" },
+ });
+
+ await act(async () => {
+ await result.current.handleCountrySelect(regionalCountry, "en-gb");
+ });
+
+ expect(mockUpdateCartMarket).toHaveBeenCalledWith("cart-1", {
+ currency: "GBP",
+ locale: "en-gb",
+ });
+ expect(refreshCart).toHaveBeenCalledOnce();
+ expect(mockAssign).toHaveBeenCalledWith("/gb/en-gb/products");
+ });
+
it("rebases a login return target when switching market", async () => {
const { rebaseAccountRedirectSearch } = await import(
"@/lib/utils/account-redirect"
diff --git a/src/hooks/useCountrySwitch.ts b/src/hooks/useCountrySwitch.ts
index 590943e1..f1c3bdc7 100644
--- a/src/hooks/useCountrySwitch.ts
+++ b/src/hooks/useCountrySwitch.ts
@@ -43,12 +43,19 @@ export function useCountrySwitch({
const newLocale =
toRouteLocale(locale || entry.default_locale || "en") ?? "en";
const activeLocale = toRouteLocale(currentLocale) ?? currentLocale;
+ const newCurrency = entry.currency;
+ const cartMatchesTarget =
+ !cart || (cart.currency === newCurrency && cart.locale === newLocale);
if (isCountryNavigating) {
return false;
}
- if (nextCountry === activeCountry && newLocale === activeLocale) {
+ if (
+ nextCountry === activeCountry &&
+ newLocale === activeLocale &&
+ cartMatchesTarget
+ ) {
return true;
}
@@ -58,7 +65,6 @@ export function useCountrySwitch({
setIsCountryNavigating(true);
- const newCurrency = entry.currency;
const pathRest = getPathWithoutPrefix(pathname);
const newPath = `/${nextCountry}/${newLocale}${pathRest}`;
const currentBasePath = `/${activeCountry}/${activeLocale}`;
From 281ff2fe0637949c9cfa9b296bc187202c6d879a Mon Sep 17 00:00:00 2001
From: laaichiu <134155205+laaichiu@users.noreply.github.com>
Date: Sun, 9 Aug 2026 15:55:27 +0800
Subject: [PATCH 3/3] fix: keep unsupported regions selectable
---
messages/de.json | 1 +
messages/en.json | 1 +
messages/es.json | 1 +
messages/fr.json | 1 +
messages/pl.json | 1 +
src/components/layout/RegionPreferences.tsx | 49 ++++++++++++-------
.../__tests__/RegionPreferences.test.tsx | 42 +++++++++++++++-
7 files changed, 78 insertions(+), 18 deletions(-)
diff --git a/messages/de.json b/messages/de.json
index 6061798f..91d7f1db 100644
--- a/messages/de.json
+++ b/messages/de.json
@@ -48,6 +48,7 @@
"description": "Wählen Sie Ihre Region und Sprache. Die Währung wird durch die ausgewählte Region festgelegt.",
"region": "Region",
"language": "Sprache",
+ "noSupportedLanguage": "Sprache nicht verfügbar",
"updatePreferences": "Einstellungen aktualisieren",
"updatingPreferences": "Einstellungen werden aktualisiert...",
"updatePreferencesFailed": "Die regionalen Einstellungen konnten nicht aktualisiert werden. Bitte versuchen Sie es erneut."
diff --git a/messages/en.json b/messages/en.json
index 8c5a8807..58edfe96 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -48,6 +48,7 @@
"description": "Choose your region and language. Currency is set by the selected region.",
"region": "Region",
"language": "Language",
+ "noSupportedLanguage": "Language unavailable",
"updatePreferences": "Update preferences",
"updatingPreferences": "Updating preferences...",
"updatePreferencesFailed": "We couldn't update your region preferences. Please try again."
diff --git a/messages/es.json b/messages/es.json
index 9db1c1d4..6d0e74e6 100644
--- a/messages/es.json
+++ b/messages/es.json
@@ -48,6 +48,7 @@
"description": "Elige tu región e idioma. La moneda depende de la región seleccionada.",
"region": "Región",
"language": "Idioma",
+ "noSupportedLanguage": "Idioma no disponible",
"updatePreferences": "Actualizar preferencias",
"updatingPreferences": "Actualizando preferencias...",
"updatePreferencesFailed": "No pudimos actualizar tus preferencias regionales. Inténtalo de nuevo."
diff --git a/messages/fr.json b/messages/fr.json
index 81e2ecaf..2f7b091a 100644
--- a/messages/fr.json
+++ b/messages/fr.json
@@ -48,6 +48,7 @@
"description": "Choisissez votre région et votre langue. La devise dépend de la région sélectionnée.",
"region": "Région",
"language": "Langue",
+ "noSupportedLanguage": "Langue indisponible",
"updatePreferences": "Mettre à jour les préférences",
"updatingPreferences": "Mise à jour des préférences...",
"updatePreferencesFailed": "Impossible de mettre à jour vos préférences régionales. Veuillez réessayer."
diff --git a/messages/pl.json b/messages/pl.json
index 22ac0a33..5910c2df 100644
--- a/messages/pl.json
+++ b/messages/pl.json
@@ -48,6 +48,7 @@
"description": "Wybierz region i język. Waluta jest ustawiana na podstawie wybranego regionu.",
"region": "Region",
"language": "Język",
+ "noSupportedLanguage": "Język niedostępny",
"updatePreferences": "Zaktualizuj preferencje",
"updatingPreferences": "Aktualizowanie preferencji...",
"updatePreferencesFailed": "Nie udało się zaktualizować preferencji regionalnych. Spróbuj ponownie."
diff --git a/src/components/layout/RegionPreferences.tsx b/src/components/layout/RegionPreferences.tsx
index d24d6155..4a81fb34 100644
--- a/src/components/layout/RegionPreferences.tsx
+++ b/src/components/layout/RegionPreferences.tsx
@@ -25,6 +25,7 @@ import {
} from "@/components/ui/native-select";
import { type CountryWithMarket, useStore } from "@/contexts/StoreContext";
import { useCountrySwitch } from "@/hooks/useCountrySwitch";
+import { resolveSupportedLocale, type SupportedLocale } from "@/i18n/locales";
import { toRouteLocale } from "@/i18n/normalize";
import { cn } from "@/lib/utils";
@@ -66,18 +67,13 @@ function getCountry(
}
function getSupportedLocales(entry: CountryWithMarket): string[] {
- const locales =
- entry.supported_locales.length > 0
- ? entry.supported_locales
- : [entry.default_locale];
+ const locales = [entry.default_locale, ...entry.supported_locales];
return Array.from(
new Set(
- locales.map(
- (locale) =>
- toRouteLocale(locale) ??
- locale.trim().replaceAll("_", "-").toLowerCase(),
- ),
+ locales
+ .map((locale) => resolveSupportedLocale(locale))
+ .filter((locale): locale is SupportedLocale => locale !== undefined),
),
);
}
@@ -123,13 +119,14 @@ export function RegionPreferences({ variant }: RegionPreferencesProps) {
if (!entry) return;
const supportedLocales = getSupportedLocales(entry);
+
setDraftCountry(nextCountry);
setDraftLocale((currentLocale) =>
supportedLocales.includes(
toRouteLocale(currentLocale) ?? currentLocale.toLowerCase(),
)
? (toRouteLocale(currentLocale) ?? currentLocale.toLowerCase())
- : supportedLocales[0],
+ : (supportedLocales[0] ?? ""),
);
setSwitchError(false);
}
@@ -138,7 +135,13 @@ export function RegionPreferences({ variant }: RegionPreferencesProps) {
event: FormEvent,
): Promise {
event.preventDefault();
- if (!selectedCountry) return;
+ if (
+ !selectedCountry ||
+ localeOptions.length === 0 ||
+ !localeOptions.includes(draftLocale)
+ ) {
+ return;
+ }
setSwitchError(false);
const switched = await handleCountrySelect(selectedCountry, draftLocale);
@@ -153,6 +156,8 @@ export function RegionPreferences({ variant }: RegionPreferencesProps) {
}
const isHeaderVariant = variant === "header";
+ const hasRenderableLocale =
+ localeOptions.length > 0 && localeOptions.includes(draftLocale);
return (