diff --git a/package-lock.json b/package-lock.json
index c8d172d..081d470 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
"@emotion/styled": "^11.14.1",
"@mui/material": "^7.2.0",
"i18next": "^25.3.0",
+ "i18next-browser-languagedetector": "^8.2.0",
"i18next-http-backend": "^3.0.2",
"react": "^19.1.0",
"react-dom": "^19.1.0",
@@ -3039,6 +3040,15 @@
}
}
},
+ "node_modules/i18next-browser-languagedetector": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.0.tgz",
+ "integrity": "sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.23.2"
+ }
+ },
"node_modules/i18next-http-backend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.2.tgz",
diff --git a/package.json b/package.json
index ed0a945..f20b020 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"@emotion/styled": "^11.14.1",
"@mui/material": "^7.2.0",
"i18next": "^25.3.0",
+ "i18next-browser-languagedetector": "^8.2.0",
"i18next-http-backend": "^3.0.2",
"react": "^19.1.0",
"react-dom": "^19.1.0",
diff --git a/public/i18n/en.json b/public/i18n/en.json
deleted file mode 100644
index df5f62e..0000000
--- a/public/i18n/en.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "title": "Loyalty Club"
-}
diff --git a/public/i18n/fa.json b/public/i18n/fa.json
deleted file mode 100644
index 3c6a9d4..0000000
--- a/public/i18n/fa.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "title": "باشگاه مشتریان"
-}
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
new file mode 100644
index 0000000..f63d94d
--- /dev/null
+++ b/public/locales/en/common.json
@@ -0,0 +1,3 @@
+{
+ "helloWorld": "hello world"
+}
\ No newline at end of file
diff --git a/public/locales/fa/common.json b/public/locales/fa/common.json
new file mode 100644
index 0000000..7a3cc8e
--- /dev/null
+++ b/public/locales/fa/common.json
@@ -0,0 +1,3 @@
+{
+ "helloWorld": "سلام دنیا"
+}
\ No newline at end of file
diff --git a/src/App.tsx b/src/App.tsx
index f053091..4f4cadd 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,10 +1,20 @@
+import { CssBaseline } from '@mui/material';
import './App.css';
+import { useTranslation } from 'react-i18next';
+import { LanguageManager } from './components/LanguageManager';
function App() {
+ const { t } = useTranslation();
+
return (
-
-
Hello World
-
+ <>
+
+
+
+
{t('helloWorld')}
+
The main content and router will go here.
+
+ >
);
}
diff --git a/src/components/LanguageManager.tsx b/src/components/LanguageManager.tsx
new file mode 100644
index 0000000..52072b1
--- /dev/null
+++ b/src/components/LanguageManager.tsx
@@ -0,0 +1,30 @@
+import { useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
+
+/**
+ * This component listens to i18next language changes and applies
+ * side effects to the document. It renders no visible UI.
+ */
+export const LanguageManager = () => {
+ const { i18n } = useTranslation();
+
+ useEffect(() => {
+ const handleLanguageChange = (lng: string) => {
+ document.documentElement.dir = i18n.dir(lng);
+ document.documentElement.lang = lng;
+ };
+
+ // Set initial values on component mount
+ handleLanguageChange(i18n.language);
+
+ // Listen for future language changes
+ i18n.on('languageChanged', handleLanguageChange);
+
+ // Cleanup the event listener on unmount
+ return () => {
+ i18n.off('languageChanged', handleLanguageChange);
+ };
+ }, [i18n]);
+
+ return null;
+};
diff --git a/src/config/i18n.ts b/src/config/i18n.ts
new file mode 100644
index 0000000..c05d74f
--- /dev/null
+++ b/src/config/i18n.ts
@@ -0,0 +1,22 @@
+import i18n from 'i18next';
+import { initReactI18next } from 'react-i18next';
+import LanguageDetector from 'i18next-browser-languagedetector';
+import HttpApi from 'i18next-http-backend';
+
+i18n
+ .use(HttpApi) // Loads translations from your /public/locales folder
+ .use(LanguageDetector) // Detects user language
+ .use(initReactI18next) // Passes i18n down to react-i18next
+ .init({
+ supportedLngs: ['en', 'fa'], // Supported languages
+ fallbackLng: 'fa',
+ detection: {
+ order: ['localStorage', 'cookie', 'navigator'],
+ caches: ['localStorage', 'cookie'],
+ lookupLocalStorage: 'language', // The key to use in localStorage
+ },
+ ns: ['common'], // Add new namespaces here
+ defaultNS: 'common',
+ });
+
+export default i18n;
diff --git a/src/contexts/AppThemeContext.ts b/src/contexts/AppThemeContext.ts
deleted file mode 100644
index 75dced3..0000000
--- a/src/contexts/AppThemeContext.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { AppTheme } from '@/types/theme';
-import { createContext } from 'react';
-
-export interface AppThemeContextModel {
- mode: AppTheme;
- changeTheme: (theme: AppTheme) => void;
-}
-// The context is used by the LangaugeProvider
-export const AppThemeContext = createContext({
- mode: 'default',
- changeTheme: () => {},
-});
diff --git a/src/contexts/LangaugeContext.ts b/src/contexts/LangaugeContext.ts
deleted file mode 100644
index a2e84a5..0000000
--- a/src/contexts/LangaugeContext.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { type Language } from '@/types/language';
-import { createContext } from 'react';
-
-export interface LangaugeContextModel {
- language: Language;
- changeLanguage: (langauge: Language) => void;
-}
-
-// The context is used by the LangaugeProvider
-export const LangaugeContext = createContext({
- language: 'fa',
- changeLanguage: () => {},
-});
diff --git a/src/hooks/useAppTheme.ts b/src/hooks/useAppTheme.ts
deleted file mode 100644
index ceffc07..0000000
--- a/src/hooks/useAppTheme.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import {
- AppThemeContext,
- type AppThemeContextModel,
-} from '@/contexts/AppThemeContext';
-import { useTheme, type Theme } from '@mui/material';
-import { useContext } from 'react';
-
-export interface AppThemeHookModel extends AppThemeContextModel {
- theme: Theme;
-}
-
-export const useAppTheme = (): AppThemeHookModel => {
- const appThemeContext = useContext(AppThemeContext);
- const muiTheme = useTheme();
-
- return {
- mode: appThemeContext.mode,
- changeTheme: appThemeContext.changeTheme,
- theme: muiTheme,
- };
-};
diff --git a/src/hooks/useLanguage.ts b/src/hooks/useLanguage.ts
deleted file mode 100644
index b176261..0000000
--- a/src/hooks/useLanguage.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import {
- LangaugeContext,
- type LangaugeContextModel,
-} from '@/contexts/LangaugeContext';
-import { useContext } from 'react';
-
-export const useLangauge = (): LangaugeContextModel => {
- return useContext(LangaugeContext);
-};
diff --git a/src/lib/i18n.ts b/src/lib/i18n.ts
deleted file mode 100644
index 6466d4c..0000000
--- a/src/lib/i18n.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import i18next from 'i18next';
-import I18NextHttpBackend from 'i18next-http-backend';
-import { initReactI18next } from 'react-i18next';
-
-i18next
- .use(I18NextHttpBackend)
- .use(initReactI18next)
- .init({
- lng: 'en',
- interpolation: {
- escapeValue: false,
- },
- backend: {
- loadPath: `${window.location.origin}/i18n/{{lng}}.json`,
- },
- });
diff --git a/src/main.tsx b/src/main.tsx
index 58f5869..be3d73c 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,18 +1,13 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
-import './lib/i18n';
import App from './App';
-import { LanguageProvider } from './providers/LanguageProvider';
-import { createTheme, ThemeProvider } from '@mui/material';
-import { AppThemeProvider } from './providers/ThemeProvider';
+import { AppProviders } from './providers/AppProvider';
createRoot(document.getElementById('root')!).render(
-
-
-
-
-
+
+
+
,
);
diff --git a/src/providers/AppProvider.tsx b/src/providers/AppProvider.tsx
new file mode 100644
index 0000000..195619e
--- /dev/null
+++ b/src/providers/AppProvider.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+import { I18nextProvider } from 'react-i18next';
+import i18n from '@/config/i18n';
+
+export const AppProviders: React.FC<{ children: React.ReactNode }> = ({
+ children,
+}) => {
+ return {children};
+};
diff --git a/src/providers/LanguageProvider.tsx b/src/providers/LanguageProvider.tsx
deleted file mode 100644
index 046bd83..0000000
--- a/src/providers/LanguageProvider.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import type { Language } from '@/types/language';
-import { changeLanguage } from 'i18next';
-import { useLayoutEffect, type JSX, type PropsWithChildren } from 'react';
-import { useLocalStorage } from '@/hooks/useLocalStorage';
-import { LangaugeContext } from '@/contexts/LangaugeContext';
-
-export const LanguageProvider = (props: PropsWithChildren): JSX.Element => {
- const [currentLanguage, setCurrentLangauge] = useLocalStorage(
- 'language',
- 'fa',
- );
-
- useLayoutEffect(() => {
- changeLanguage(currentLanguage);
- document.documentElement.dir = currentLanguage === 'fa' ? 'rtl' : 'ltr';
- document.documentElement.lang = currentLanguage;
- }, [currentLanguage]);
-
- return (
-
- {props.children}
-
- );
-};
diff --git a/src/providers/ThemeProvider.tsx b/src/providers/ThemeProvider.tsx
deleted file mode 100644
index 8aaa8ba..0000000
--- a/src/providers/ThemeProvider.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { useLayoutEffect, type JSX, type PropsWithChildren } from 'react';
-import { useLocalStorage } from '@/hooks/useLocalStorage';
-import type { AppTheme } from '@/types/theme';
-import { AppThemeContext } from '@/contexts/AppThemeContext';
-import { createTheme, ThemeProvider } from '@mui/material';
-import { useLangauge } from '@/hooks/useLanguage';
-
-export const AppThemeProvider = (props: PropsWithChildren): JSX.Element => {
- const { language } = useLangauge();
- const [currentThemeMode, setCurrentTheme] = useLocalStorage(
- 'theme',
- 'default',
- );
-
- const muiTheme = createTheme({
- direction: language === 'fa' ? 'rtl' : 'ltr',
- });
-
- useLayoutEffect(() => {
- document.body.setAttribute('theme', currentThemeMode);
- }, [currentThemeMode]);
-
- return (
-
- {props.children}
-
- );
-};
diff --git a/src/types/language.ts b/src/types/language.ts
deleted file mode 100644
index ca7f950..0000000
--- a/src/types/language.ts
+++ /dev/null
@@ -1 +0,0 @@
-export type Language = 'en' | 'fa';
diff --git a/src/types/theme.ts b/src/types/theme.ts
deleted file mode 100644
index 18e81a8..0000000
--- a/src/types/theme.ts
+++ /dev/null
@@ -1 +0,0 @@
-export type AppTheme = 'default' | 'dark';