feat(i18n): persist Bokmal preference globally

This commit is contained in:
cesnimda
2026-08-28 12:44:02 +02:00
parent b6dcc7c760
commit 5805c1a621
21 changed files with 359 additions and 66 deletions
+27 -4
View File
@@ -1,11 +1,13 @@
import React, { createContext, useContext, useState } from "react";
import React, { createContext, useCallback, useContext, useEffect, useState } from "react";
import { translations, TranslationKey, UiLanguage } from "./translations";
import { api } from "../api";
type TranslationParams = Record<string, string | number>;
type Ctx = {
language: UiLanguage;
setLanguage: (l: UiLanguage) => void;
hydrateLanguage: (language?: string | null) => void;
t: (key: TranslationKey, params?: TranslationParams) => string;
};
@@ -19,17 +21,38 @@ function interpolate(template: string, params?: TranslationParams) {
export function I18nProvider({ children }: { children: React.ReactNode }) {
const [language, setLanguageState] = useState<UiLanguage>(() => {
const raw = window.localStorage.getItem("uiLanguage");
return raw === "no" ? "no" : "en";
return raw === "nb" || raw === "nb-NO" || raw === "no" ? "nb" : "en";
});
const setLanguage = (l: UiLanguage) => {
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 <I18nContext.Provider value={{ language, setLanguage, t }}>{children}</I18nContext.Provider>;
return <I18nContext.Provider value={{ language, setLanguage, hydrateLanguage, t }}>{children}</I18nContext.Provider>;
}
export function useI18n() {