fix(ux): preserve localized save errors and current language retries

This commit is contained in:
cesnimda
2026-10-11 03:05:33 +02:00
parent a94ea0a4a4
commit c135d03602
15 changed files with 485 additions and 11 deletions
+16 -2
View File
@@ -1,6 +1,7 @@
import React, { createContext, useCallback, useContext, useEffect, useState } from "react";
import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
import { translations, TranslationKey, UiLanguage } from "./translations";
import { api } from "../api";
import { useOptionalToast } from "../toast";
type TranslationParams = Record<string, string | number>;
@@ -19,12 +20,16 @@ function interpolate(template: string, params?: TranslationParams) {
}
export function I18nProvider({ children }: { children: React.ReactNode }) {
const feedback = useOptionalToast();
const [language, setLanguageState] = useState<UiLanguage>(() => {
const raw = window.localStorage.getItem("uiLanguage");
return raw === "nb" || raw === "nb-NO" || raw === "no" ? "nb" : "en";
});
const currentLanguage = useRef(language);
const languageSaveSequence = useRef(0);
const applyLanguage = useCallback((l: UiLanguage) => {
currentLanguage.current = l;
setLanguageState(l);
window.localStorage.setItem("uiLanguage", l);
document.documentElement.lang = l === "nb" ? "nb-NO" : "en";
@@ -32,7 +37,16 @@ export function I18nProvider({ children }: { children: React.ReactNode }) {
const setLanguage = (l: UiLanguage) => {
applyLanguage(l);
void api.put("/auth/preferences/language", { language: l === "nb" ? "nb-NO" : "en" }).catch(() => undefined);
const requestSequence = ++languageSaveSequence.current;
void api.put("/auth/preferences/language", { language: l === "nb" ? "nb-NO" : "en" }).catch(() => {
if (requestSequence !== languageSaveSequence.current || currentLanguage.current !== l) return;
feedback?.toast(translations[l].settingsLanguageSaveFailed, "error", {
label: translations[l].retry,
onClick: () => {
if (requestSequence === languageSaveSequence.current && currentLanguage.current === l) setLanguage(l);
},
});
});
};
const hydrateLanguage = useCallback((value?: string | null) => {
+2
View File
@@ -1149,6 +1149,7 @@ export const translations = {
settingsLanguageTitle: "Language and localization",
settingsLanguageBody: "Set your preferred app language. This is also the language used when deciding whether imported job descriptions should show translated text.",
settingsPreferredLanguage: "Preferred language",
settingsLanguageSaveFailed: "The language changed on this device, but your account preference could not be saved.",
settingsEnglish: "English",
settingsNorwegian: "Norwegian Bokmål",
settingsJobs: "Jobs",
@@ -3732,6 +3733,7 @@ export const translations = {
settingsLanguageTitle: "Språk og lokalisering",
settingsLanguageBody: "Velg foretrukket språk i appen. Dette brukes også når appen avgjør om importerte stillingsbeskrivelser skal vise oversatt tekst.",
settingsPreferredLanguage: "Foretrukket språk",
settingsLanguageSaveFailed: "Språket er endret på denne enheten, men kunne ikke lagres på kontoen din.",
settingsEnglish: "Engelsk",
settingsNorwegian: "Norsk Bokmål",
settingsJobs: "Jobber",