Skip to content

Commit

Permalink
feat: language detection
Browse files Browse the repository at this point in the history
  • Loading branch information
PedroCo3lho committed Feb 6, 2025
1 parent 273ad5d commit 0166bd3
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 35 deletions.
4 changes: 4 additions & 0 deletions apps/web/next-i18next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ export default {
locales: ["en", "es", "pt"],
localeDetection: true,
},
detection: {
order: ["cookie", "localStorage", "navigator", "querystring"],
caches: ["cookie", "localStorage"],
},
};
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
13 changes: 6 additions & 7 deletions apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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 (
<html
suppressHydrationWarning
lang="en"
suppressHydrationWarning={true}
lang={i18n.language}
data-theme="cofiblocks"
className={`${GeistSans.variable}`}
>
Expand Down
24 changes: 10 additions & 14 deletions apps/web/src/app/user/settings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,14 @@ const languageSchema = z.object({
type FormValues = z.infer<typeof languageSchema>;

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<string>(initialLanguage);
const [isLanguageModalOpen, setLanguageModalOpen] = useState<boolean>(false);

const languages = ["en", "es", "pt"];

const { control, handleSubmit, setValue } = useForm<FormValues>({
defaultValues: {
language: language,
Expand All @@ -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);
Expand All @@ -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();
};
Expand Down Expand Up @@ -92,7 +88,7 @@ export default function Settings({ initialLanguage = "en" }: SettingsProps) {
</h3>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div className="flex flex-col gap-2">
{languages.map((lang, index) => (
{SUPPORTED_LANGUAGES.map((lang, index) => (
<div key={lang}>
<label className="flex items-center gap-2">
<RadioButton
Expand All @@ -102,7 +98,7 @@ export default function Settings({ initialLanguage = "en" }: SettingsProps) {
control={control}
/>
</label>
{index < languages.length - 1 && (
{index < SUPPORTED_LANGUAGES.length - 1 && (
<hr className="my-2 border-surface-border" />
)}
</div>
Expand Down
42 changes: 28 additions & 14 deletions apps/web/src/i18n.ts
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit 0166bd3

Please sign in to comment.