72 lines
2.7 KiB
TypeScript
72 lines
2.7 KiB
TypeScript
|
|
"use client"
|
||
|
|
|
||
|
|
// Språkbryter (ADR-094) -- setter locale-cookien direkte (samme cookie
|
||
|
|
// i18n/request.ts leser) og PATCHer /profile sin preferred_locale når
|
||
|
|
// innlogget, slik at valget følger brukeren til neste enhet/økt også
|
||
|
|
// (ikke bare denne nettleseren). cookie alene er nok til at selve
|
||
|
|
// visningen endres umiddelbart -- PATCH-en er en "husk til neste gang"-
|
||
|
|
// bonus, feiler stille (utlogget bruker har ingenting å PATCHe mot).
|
||
|
|
import { useState } from "react"
|
||
|
|
import { useRouter } from "next/navigation"
|
||
|
|
import { useLocale, useTranslations } from "next-intl"
|
||
|
|
import { cn } from "@/lib/utils"
|
||
|
|
import { LOCALE_COOKIE, SUPPORTED_LOCALES, type Locale } from "@/lib/locale"
|
||
|
|
|
||
|
|
export function LanguageSwitcher() {
|
||
|
|
const locale = useLocale()
|
||
|
|
const t = useTranslations("common.languageSwitcher")
|
||
|
|
const router = useRouter()
|
||
|
|
const [pending, setPending] = useState(false)
|
||
|
|
|
||
|
|
async function setLocale(next: Locale) {
|
||
|
|
if (next === locale || pending) return
|
||
|
|
setPending(true)
|
||
|
|
document.cookie = `${LOCALE_COOKIE}=${next}; path=/; max-age=31536000; SameSite=Lax`
|
||
|
|
try {
|
||
|
|
await fetch("/profile", {
|
||
|
|
method: "PATCH",
|
||
|
|
headers: { "Content-Type": "application/json" },
|
||
|
|
credentials: "include",
|
||
|
|
body: JSON.stringify({ preferred_locale: next }),
|
||
|
|
})
|
||
|
|
} catch {
|
||
|
|
// Stille -- cookien er allerede satt, selve visningen fungerer uansett.
|
||
|
|
}
|
||
|
|
router.refresh()
|
||
|
|
setPending(false)
|
||
|
|
}
|
||
|
|
|
||
|
|
const labels: Record<Locale, string> = { nb: t("norwegian"), en: t("english") }
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="flex flex-col gap-2">
|
||
|
|
<span className="text-sm font-bold text-foreground">{t("label")}</span>
|
||
|
|
<div role="radiogroup" aria-label={t("label")} className="flex gap-2">
|
||
|
|
{SUPPORTED_LOCALES.map((code) => {
|
||
|
|
const selected = code === locale
|
||
|
|
return (
|
||
|
|
<button
|
||
|
|
key={code}
|
||
|
|
type="button"
|
||
|
|
role="radio"
|
||
|
|
aria-checked={selected}
|
||
|
|
disabled={pending}
|
||
|
|
onClick={() => setLocale(code)}
|
||
|
|
className={cn(
|
||
|
|
"min-h-11 flex-1 rounded-xl border px-4 text-sm font-bold transition-all duration-200 ease-in-out",
|
||
|
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
||
|
|
"active:scale-[0.98] disabled:pointer-events-none disabled:opacity-50",
|
||
|
|
selected
|
||
|
|
? "border-primary bg-primary text-primary-foreground"
|
||
|
|
: "border-border bg-card text-foreground hover:bg-accent/50",
|
||
|
|
)}
|
||
|
|
>
|
||
|
|
{labels[code]}
|
||
|
|
</button>
|
||
|
|
)
|
||
|
|
})}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|