diff --git a/next.config.ts b/next.config.ts index e9ffa30..b22af96 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,5 @@ -import type { NextConfig } from "next"; +import type { NextConfig } from 'next'; -const nextConfig: NextConfig = { - /* config options here */ -}; +const nextConfig: NextConfig = {}; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index 0b5546e..8ea4c83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,13 +8,9 @@ "name": "pasargad_bricks", "version": "0.1.0", "dependencies": { - "i18next": "^25.2.1", - "i18next-browser-languagedetector": "^8.1.0", - "i18next-http-backend": "^3.0.2", "next": "15.3.3", "react": "^19.0.0", - "react-dom": "^19.0.0", - "react-i18next": "^15.5.2" + "react-dom": "^19.0.0" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -57,14 +53,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.4.tgz", - "integrity": "sha512-t3yaEOuGu9NlIZ+hIeGbBjFtZT7j2cb2tg0fuaJKeGotchRjjLfrBA9Kwf8quhpP1EUuxModQg04q/mBwyg8uA==", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@emnapi/core": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", @@ -5762,7 +5750,7 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "devOptional": true, + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/package.json b/package.json index e3e0dec..502a45c 100644 --- a/package.json +++ b/package.json @@ -9,13 +9,9 @@ "lint": "next lint" }, "dependencies": { - "i18next": "^25.2.1", - "i18next-browser-languagedetector": "^8.1.0", - "i18next-http-backend": "^3.0.2", "next": "15.3.3", "react": "^19.0.0", - "react-dom": "^19.0.0", - "react-i18next": "^15.5.2" + "react-dom": "^19.0.0" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/src/app/aaa/page.tsx b/src/app/aaa/page.tsx index bfcfb19..0dec3c1 100644 --- a/src/app/aaa/page.tsx +++ b/src/app/aaa/page.tsx @@ -10,7 +10,7 @@ export default async function Home() { const data = await getData(); return ( -
+
ssss
); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 8ffc72c..a697ecd 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,24 +1,24 @@ 'use client' import type { Metadata } from 'next'; import './globals.css'; -import { I18nextProvider } from 'react-i18next'; -import i18n from '../config/i18n'; +import { cookies } from 'next/headers'; // export const metadata: Metadata = { // title: 'Create Next App', // description: 'Generated by create next app', // }; -export default function RootLayout({ +export default async function RootLayout({ children, -}: { +}: Readonly<{ children: React.ReactNode; -}) { +}>) { + const cookieStore = await cookies(); + const locale = cookieStore.get('lang')?.value || 'en'; + return ( - - - {children} - + + {children} ); } diff --git a/src/app/page.tsx b/src/app/page.tsx index e4dbb97..5a4ec34 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,6 +1,8 @@ // ... existing imports ... import Header from '@/components/layout/header'; import Navbar from '@/components/layout/navbar'; +import ChangeLangButton from '@/components/layout/languageSwitcher'; +import { changeLanguage, t } from '@/locales/translates'; async function getData() { // Simulate a delay to show server-side data fetching @@ -8,13 +10,18 @@ async function getData() { return { message: 'This data was fetched on the server!' }; } +const change = () => { + changeLanguage('fa'); +}; + export default async function Home() { const data = await getData(); return ( -
+
- {/* */} + {t('pages_home_title') as string} +
- ); + ) } diff --git a/src/assets/constants/routeFactory/index.ts b/src/assets/constants/routeFactory/index.ts index 9c0c981..8fbba9e 100644 --- a/src/assets/constants/routeFactory/index.ts +++ b/src/assets/constants/routeFactory/index.ts @@ -1,13 +1,12 @@ -import { useTranslation } from 'react-i18next'; -const { t } = useTranslation('routes'); +import { t, translates } from '@/locales/translates'; export default { home: { - title: t('home'), + title: t('pages_home_title'), route: () => '/', }, about: { - title: t('about'), + title: t('pages_about_title'), route: () => '/about', }, }; diff --git a/src/components/layout/languageSwitcher.tsx b/src/components/layout/languageSwitcher.tsx new file mode 100644 index 0000000..b67f6f9 --- /dev/null +++ b/src/components/layout/languageSwitcher.tsx @@ -0,0 +1,17 @@ +'use client'; +import { changeLanguage, TLocales } from '@/locales/translates'; +import Button from '../Button'; + +export default function ChangeLangButton() { + const changeLang = (lang: TLocales) => { + changeLanguage(lang); + window.location.reload(); // Reload to get new SSR content + }; + + return ( + <> + + + + ); +} diff --git a/src/components/uikit/button/index.tsx b/src/components/uikit/button/index.tsx index 70d262b..8d2b6dd 100644 --- a/src/components/uikit/button/index.tsx +++ b/src/components/uikit/button/index.tsx @@ -1,3 +1,4 @@ +'use client'; import React, { ButtonHTMLAttributes, ReactNode } from 'react'; interface ButtonProps extends ButtonHTMLAttributes { diff --git a/src/config/i18n.ts b/src/config/i18n.ts deleted file mode 100644 index 25d9662..0000000 --- a/src/config/i18n.ts +++ /dev/null @@ -1,19 +0,0 @@ -import i18n from 'i18next'; -import { initReactI18next } from 'react-i18next'; -import Backend from 'i18next-http-backend'; - -i18n - .use(Backend) - .use(initReactI18next) - .init({ - fallbackLng: 'en', - debug: false, - interpolation: { - escapeValue: false, - }, - backend: { - loadPath: '/locales/{{lng}}/{{ns}}.json', - } - }); - -export default i18n; \ No newline at end of file diff --git a/src/locales/en.ts b/src/locales/en.ts new file mode 100644 index 0000000..ad43bcc --- /dev/null +++ b/src/locales/en.ts @@ -0,0 +1,10 @@ +const en = { + pages_home_title: 'Home', + pages_home_description: 'Welcome to the home page', + pages_about_title: 'About Us', + pages_about_description: 'Learn more about us on this page', + pages_contact_title: 'Contact Us', + pages_contact_description: 'Get in touch with us through this page', +}; + +export default en; diff --git a/src/locales/fa.ts b/src/locales/fa.ts new file mode 100644 index 0000000..94567b7 --- /dev/null +++ b/src/locales/fa.ts @@ -0,0 +1,12 @@ +import { Translates } from './translates'; + +const fa = { + pages_home_title: 'صفحه‌ی اصلی', + pages_home_description: 'Welcome to the home page', + pages_about_title: 'About Us', + pages_about_description: 'Learn more about us on this page', + pages_contact_title: 'Contact Us', + pages_contact_description: 'Get in touch with us through this page', +} as Translates; + +export default fa; diff --git a/src/locales/translates.ts b/src/locales/translates.ts new file mode 100644 index 0000000..17adc01 --- /dev/null +++ b/src/locales/translates.ts @@ -0,0 +1,57 @@ +import en from './en'; +import fa from './fa'; + +// Universal getCookie function +export function getCookie(name: string): string | undefined { + if (typeof window !== 'undefined') { + // Client-side + const match = document.cookie.match( + new RegExp('(^| )' + name + '=([^;]+)'), + ); + return match ? decodeURIComponent(match[2]) : undefined; + } else { + // Server-side (Next.js App Router) + try { + // Dynamically import to avoid errors in client bundle + const { cookies } = require('next/headers'); + return cookies().get(name)?.value; + } catch { + return undefined; + } + } +} + +// Universal setCookie function to change language +export function changeLanguage(lang: TLocales, days = 365) { + if (typeof window !== 'undefined') { + // Client-side + const expires = new Date(Date.now() + days * 864e5).toUTCString(); + document.cookie = `lang=${encodeURIComponent(lang)}; expires=${expires}; path=/`; + } else { + // Server-side (Next.js App Router) + try { + const { cookies } = require('next/headers'); + cookies().set('lang', lang, { path: '/', maxAge: days * 24 * 60 * 60 }); + } catch { + // No-op on server if not available + } + } +} + +export const locales = { + en: en as Translates, + fa, +} as Record; + +export const translates = (key: keyof Translates): string | (() => string) => { + const lang = (getCookie('lang') || 'en') as TLocales; + return locales[lang][key] || locales.en[key] || key; +}; + +export const t = (key: keyof Translates) => translates(key); + +export type TLocales = 'en' | 'fa'; + +export type Translates = { + [K in keyof typeof en]: string | (() => string); +}; diff --git a/tailwind.config.ts b/tailwind.config.ts index ccdc167..5240217 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -9,51 +9,51 @@ const config: Config = { ], theme: { // Move your custom colors directly here to override default Tailwind colors - extend: { - colors: { - // Custom colors - primary: { - light: '#6366F1', - DEFAULT: '#6366F1', - dark: '#FA003F', + colors: { + // Custom colors + primary: { + light: '#6366F1', + DEFAULT: '#6366F1', + dark: '#FA003F', + }, + secondary: { + light: '#A78BFA', + DEFAULT: '#8B5CF6', + dark: '#7C3AED', + }, + textColorLight: { + light: '#FFFFFF', + DEFAULT: '#FFFFFF', + dark: '#FA003F', + }, + textColorGray: { + light: '#222222', + DEFAULT: '#222222', + dark: '#FA003F', + }, + bgPrimary: { + light: '#FFFFFF', + DEFAULT: '#FFFFFF', + dark: '#FA003F', + }, + gray: { + light: { + 100: '#EBEBEB', + 200: '#F5F5F5', + 300: '#81848A', + 400: '#777', + 500: '#222', }, - secondary: { - light: '#A78BFA', - DEFAULT: '#8B5CF6', - dark: '#7C3AED', - }, - textColorLight: { - light: '#FFFFFF', - DEFAULT: '#FFFFFF', - dark: '#FA003F', - }, - textColorGray: { - light: '#222222', - DEFAULT: '#222222', - dark: '#FA003F', - }, - bgPrimary: { - light: '#FFFFFF', - DEFAULT: '#FFFFFF', - dark: '#FA003F', - }, - gray: { - light: { - 100: '#EBEBEB', - 200: '#F5F5F5', - 300: '#81848A', - 400: '#777', - 500: '#222', - }, - dark: { - 100: '#EBEBEB', - 200: '#F5F5F5', - 300: '#81848A', - 400: '#777', - 500: '#222', - }, + dark: { + 100: '#EBEBEB', + 200: '#F5F5F5', + 300: '#81848A', + 400: '#777', + 500: '#222', }, }, + }, + extend: { backgroundImage: { 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 'gradient-conic':