import React, { createContext, useCallback, useContext, useEffect, useState } from "react"; import { translations, TranslationKey, UiLanguage } from "./translations"; import { api } from "../api"; type TranslationParams = Record; type Ctx = { language: UiLanguage; setLanguage: (l: UiLanguage) => void; hydrateLanguage: (language?: string | null) => void; t: (key: TranslationKey, params?: TranslationParams) => string; }; const I18nContext = createContext(null); function interpolate(template: string, params?: TranslationParams) { if (!params) return template; return template.replace(/\{(\w+)\}/g, (_, key) => String(params[key] ?? `{${key}}`)); } export function I18nProvider({ children }: { children: React.ReactNode }) { const [language, setLanguageState] = useState(() => { const raw = window.localStorage.getItem("uiLanguage"); return raw === "nb" || raw === "nb-NO" || raw === "no" ? "nb" : "en"; }); const applyLanguage = useCallback((l: UiLanguage) => { setLanguageState(l); window.localStorage.setItem("uiLanguage", l); document.documentElement.lang = l === "nb" ? "nb-NO" : "en"; }, []); const setLanguage = (l: UiLanguage) => { applyLanguage(l); void api.put("/auth/preferences/language", { language: l === "nb" ? "nb-NO" : "en" }).catch(() => undefined); }; const hydrateLanguage = useCallback((value?: string | null) => { if (!value) return; applyLanguage(value === "nb" || value === "nb-NO" || value === "no" ? "nb" : "en"); }, [applyLanguage]); useEffect(() => { document.documentElement.lang = language === "nb" ? "nb-NO" : "en"; const onStorage = (event: StorageEvent) => { if (event.key !== "uiLanguage") return; applyLanguage(event.newValue === "nb" || event.newValue === "nb-NO" || event.newValue === "no" ? "nb" : "en"); }; window.addEventListener("storage", onStorage); return () => window.removeEventListener("storage", onStorage); }, [applyLanguage, language]); const t = (key: TranslationKey, params?: TranslationParams) => interpolate(translations[language][key] ?? translations.en[key], params); return {children}; } export function useI18n() { const ctx = useContext(I18nContext); if (!ctx) throw new Error("useI18n must be used within I18nProvider"); return ctx; } export function useOptionalI18n() { return useContext(I18nContext); }