diff --git a/apps/web/next-i18next.config.js b/apps/web/next-i18next.config.js index f616366..0e8efcc 100644 --- a/apps/web/next-i18next.config.js +++ b/apps/web/next-i18next.config.js @@ -4,4 +4,8 @@ export default { locales: ["en", "es", "pt"], localeDetection: true, }, + detection: { + order: ["cookie", "localStorage", "navigator", "querystring"], + caches: ["cookie", "localStorage"], + }, }; diff --git a/apps/web/package.json b/apps/web/package.json index 74ccc86..9b1666e 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -35,6 +35,7 @@ "geist": "^1.3.0", "get-starknet-core": "^3.3.3", "i18next": "^23.15.1", + "i18next-browser-languagedetector": "^8.0.2", "next": "^14.2.4", "next-auth": "^4.24.7", "pinata-web3": "^0.5.3", diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 5bb763c..c290138 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -1,5 +1,5 @@ "use client"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { Toaster } from "react-hot-toast"; import "~/styles/globals.css"; import "~/i18n"; @@ -16,16 +16,15 @@ export default function RootLayout({ children: React.ReactNode; }) { useEffect(() => { - const savedLanguage = localStorage.getItem("app_language"); - if (savedLanguage) { - void i18n.changeLanguage(savedLanguage); - } + const savedLanguage = + localStorage.getItem("app_language") ?? i18n.language ?? "en"; + void i18n.changeLanguage(savedLanguage); }, []); return ( diff --git a/apps/web/src/app/user/settings/page.tsx b/apps/web/src/app/user/settings/page.tsx index 7bef189..839727e 100644 --- a/apps/web/src/app/user/settings/page.tsx +++ b/apps/web/src/app/user/settings/page.tsx @@ -22,16 +22,14 @@ const languageSchema = z.object({ type FormValues = z.infer; const LANGUAGE_KEY = "app_language"; +const SUPPORTED_LANGUAGES = ["en", "es", "pt"]; export default function Settings({ initialLanguage = "en" }: SettingsProps) { const { i18n, t } = useTranslation(); - // Set a default language that matches on both server and client const [language, setLanguage] = useState(initialLanguage); const [isLanguageModalOpen, setLanguageModalOpen] = useState(false); - const languages = ["en", "es", "pt"]; - const { control, handleSubmit, setValue } = useForm({ defaultValues: { language: language, @@ -40,14 +38,12 @@ export default function Settings({ initialLanguage = "en" }: SettingsProps) { }); useEffect(() => { - // Update language from localStorage after component mounts - const savedLanguage = localStorage.getItem(LANGUAGE_KEY); - if (savedLanguage && savedLanguage !== language) { - setLanguage(savedLanguage); - setValue("language", savedLanguage); - void i18n.changeLanguage(savedLanguage); - } - }, [i18n, language, setValue]); + const detectedLanguage = + SUPPORTED_LANGUAGES.find((lang) => i18n.language.includes(lang)) ?? "en"; + setLanguage(detectedLanguage); + setValue("language", detectedLanguage); + localStorage.setItem(LANGUAGE_KEY, detectedLanguage); + }, [i18n.language, setValue]); const openLanguageModal = () => setLanguageModalOpen(true); const closeLanguageModal = () => setLanguageModalOpen(false); @@ -58,7 +54,7 @@ export default function Settings({ initialLanguage = "en" }: SettingsProps) { const onSubmit = async (data: FormValues) => { setLanguage(data.language); - await i18n.changeLanguage(data.language); + void i18n.changeLanguage(data.language); localStorage.setItem(LANGUAGE_KEY, data.language); closeLanguageModal(); }; @@ -92,7 +88,7 @@ export default function Settings({ initialLanguage = "en" }: SettingsProps) {
- {languages.map((lang, index) => ( + {SUPPORTED_LANGUAGES.map((lang, index) => (
- {index < languages.length - 1 && ( + {index < SUPPORTED_LANGUAGES.length - 1 && (
)}
diff --git a/apps/web/src/i18n.ts b/apps/web/src/i18n.ts index 2f6fbdd..0daaed0 100644 --- a/apps/web/src/i18n.ts +++ b/apps/web/src/i18n.ts @@ -1,22 +1,36 @@ import i18n from "i18next"; +import detector from "i18next-browser-languagedetector"; import { initReactI18next } from "react-i18next"; import enTranslation from "../public/locales/en/common.json"; import esTranslation from "../public/locales/es/common.json"; import ptTranslation from "../public/locales/pt/common.json"; -void i18n.use(initReactI18next).init({ - resources: { - en: { common: enTranslation }, - es: { common: esTranslation }, - pt: { common: ptTranslation }, - }, - lng: "en", - fallbackLng: "en", - defaultNS: "common", - debug: true, - interpolation: { - escapeValue: false, - }, -}); +void i18n + .use(detector) + .use(initReactI18next) + .init( + { + resources: { + en: { common: enTranslation }, + es: { common: esTranslation }, + pt: { common: ptTranslation }, + }, + saveMissing: true, + fallbackLng: "en", + defaultNS: "common", + debug: true, + interpolation: { + escapeValue: false, + }, + detection: { + order: ["cookie", "localStorage", "navigator"], + caches: ["cookie", "localStorage"], + }, + }, + (err, t) => { + if (err) return console.error(err); + console.log("Detected language:", i18n.language); + }, + ); export default i18n;