diff --git a/src/i18n.tsx b/src/i18n.tsx index ca3c216..c3efa17 100644 --- a/src/i18n.tsx +++ b/src/i18n.tsx @@ -5,7 +5,11 @@ import React, { useMemo, useState, } from "react"; -import { languageForHomePathname } from "./languageRoutes"; +import { + languageForHomePathname, + languageFromPathname, + langPathPrefix, +} from "./languageRoutes"; import type { Dict } from "./locales/types"; import { zhDict } from "./locales/zh-CN"; import { enDict } from "./locales/en"; @@ -40,9 +44,14 @@ const LANG_KEY = "ark_lang"; export function I18nProvider({ children }: { children: React.ReactNode }) { const [lang, setLangState] = useState(() => { - const routeLang = languageForHomePathname(window.location.pathname); - if (routeLang) return routeLang; - if (window.location.pathname === "/") return "en"; + const path = window.location.pathname; + // Any URL whose first path segment is a known language prefix wins + // (covers /malay, /malay/browse, /korean/category/foo, etc.). + const homeLang = languageForHomePathname(path); + if (homeLang) return homeLang; + const deepLang = languageFromPathname(path); + if (langPathPrefix(deepLang)) return deepLang; + if (path === "/") return "en"; const s = localStorage.getItem(LANG_KEY); if (s === "zh" || s === "zh-TW") return "zh-CN"; diff --git a/src/layouts/PublicLayout.tsx b/src/layouts/PublicLayout.tsx index b454568..ff983f8 100644 --- a/src/layouts/PublicLayout.tsx +++ b/src/layouts/PublicLayout.tsx @@ -15,6 +15,7 @@ import { homePathForLang, isHomePathname, languageFromPathname, + localizePath, stripLangPrefix, } from "../languageRoutes"; import { useLocalizedPath } from "../useLocalizedPath"; @@ -318,7 +319,15 @@ export function PublicLayout() { const homePath = homePathForLang(lang); const changeLang = (nextLang: Lang) => { setLang(nextLang); - if (isHome) nav(homePathForLang(nextLang), { replace: true }); + if (isHome) { + nav(homePathForLang(nextLang), { replace: true }); + } else { + // Preserve sub-path and query/hash; only swap the language prefix. + const canonical = stripLangPrefix(pathname); + nav(localizePath(canonical, nextLang) + search + hash, { + replace: true, + }); + } }; const footerInContentFlow = stripLangPrefix(pathname) === "/browse"; // Current page name shown in the header brand slot (falls back to the brand).