From 9a5d2bd504b68770a4e4659e0be14411ffcd6570 Mon Sep 17 00:00:00 2001 From: Angelo Reale <12191809+angeloreale@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:42:32 +0100 Subject: [PATCH] ar(feat) [DPCP-37]: Localization Part 2: Dictionaries (#42) * ar(feat) [DPCP-37]: Localization * ar(feat) [DPCP-37]: Localization * ar(feat) [DPCP-37]: Localization * ar(feat) [DPCP-37]: Localization * ar(feat) [DPCP-37]: Localization * ar(feat) [DPCP-37]: Localization * ar(feat) [DPCP-37]: Localization * ar(feat) [DPCP-37]: Localization * ar(feat) [DPCP-37]: Localization * ar(feat) [DPCP-37]: Localization * ar(feat) [DPCP-37]: Localization * ar(feat) [DPCP-37]: Localization --- lib/dictionaries/global/cs-cz.json | 15 +++ lib/dictionaries/global/de-de.json | 15 +++ lib/dictionaries/global/default.json | 15 +++ lib/dictionaries/global/en.json | 14 +++ lib/dictionaries/global/es-es.json | 15 +++ lib/dictionaries/global/et-ee.json | 15 +++ lib/dictionaries/global/fr-fr.json | 15 +++ lib/dictionaries/global/it-it.json | 15 +++ lib/dictionaries/global/ja-jp.json | 15 +++ lib/dictionaries/global/pl-pl.json | 15 +++ lib/dictionaries/global/pt-br.json | 11 ++ lib/dictionaries/global/ro.json | 15 +++ lib/dictionaries/global/ru-ru.json | 15 +++ lib/dictionaries/global/sv-se.json | 15 +++ lib/state/providers.tsx | 2 +- next.config.js | 5 +- package-lock.json | 105 ++++++++++++++++++ package.json | 1 + src/app/[lang]/layout.tsx | 25 ----- .../components/client/blocks/index.ts | 0 .../components/client/blocks/topnav-view.tsx | 10 +- .../client/elements/calendar-view.tsx | 0 .../components/client/elements/index.ts | 0 .../client/elements/link-decorator.tsx | 0 .../components/client/elements/list-view.tsx | 0 .../components/client/elements/map-view.tsx | 0 .../client/elements/navbar-view.tsx | 0 .../client/elements/signin-view.tsx | 7 +- .../client/elements/signup-view.tsx | 9 +- .../client/elements/usersettings-view.tsx | 0 .../components/client/index.ts | 0 .../{[lang] => [locale]}/components/index.ts | 0 .../components/navbar.tsx | 0 .../server/blocks/hypnos-public-list.tsx | 0 .../components/server/blocks/index.ts | 0 .../components/server/blocks/rm-list.tsx | 0 .../components/server/blocks/topnav.tsx | 0 .../components/server/index.ts | 0 .../components/server/navbar-controller.tsx | 0 .../components/server/signup-controller.tsx | 0 .../server/usersettings-controller.tsx | 0 .../components/toolbar.tsx | 0 .../{[lang] => [locale]}/dash/error/page.tsx | 0 .../dash/services/hypnos/[mode]/page.tsx | 0 .../dash/services/rickmorty/[mode]/page.tsx | 0 .../{[lang] => [locale]}/dash/signin/page.tsx | 0 .../{[lang] => [locale]}/dash/verify/page.tsx | 0 src/app/{[lang] => [locale]}/favicon.ico | Bin .../gateway/client/actions.ts | 0 src/app/{[lang] => [locale]}/gateway/index.ts | 2 +- .../gateway/server/actions.ts | 14 ++- .../gateway/server/hypnos/private/actions.ts | 0 .../gateway/server/hypnos/private/index.ts | 0 .../gateway/server/hypnos/public/actions.ts | 0 .../gateway/server/hypnos/public/index.ts | 0 src/app/{[lang] => [locale]}/global-error.jsx | 0 src/app/{[lang] => [locale]}/globals.css | 0 src/app/[locale]/i18n.ts | 13 +++ src/app/[locale]/layout.tsx | 32 ++++++ src/app/{[lang] => [locale]}/page.tsx | 0 .../styles/page.module.css | 0 src/middleware.ts | 97 ++-------------- src/middlewares/authMiddleware.ts | 54 +++++++++ src/middlewares/i18nDetectMiddleware.ts | 58 ++++++++++ tsconfig.json | 32 +++--- 65 files changed, 532 insertions(+), 139 deletions(-) create mode 100644 lib/dictionaries/global/cs-cz.json create mode 100644 lib/dictionaries/global/de-de.json create mode 100644 lib/dictionaries/global/default.json create mode 100644 lib/dictionaries/global/en.json create mode 100644 lib/dictionaries/global/es-es.json create mode 100644 lib/dictionaries/global/et-ee.json create mode 100644 lib/dictionaries/global/fr-fr.json create mode 100644 lib/dictionaries/global/it-it.json create mode 100644 lib/dictionaries/global/ja-jp.json create mode 100644 lib/dictionaries/global/pl-pl.json create mode 100644 lib/dictionaries/global/pt-br.json create mode 100644 lib/dictionaries/global/ro.json create mode 100644 lib/dictionaries/global/ru-ru.json create mode 100644 lib/dictionaries/global/sv-se.json delete mode 100644 src/app/[lang]/layout.tsx rename src/app/{[lang] => [locale]}/components/client/blocks/index.ts (100%) rename src/app/{[lang] => [locale]}/components/client/blocks/topnav-view.tsx (94%) rename src/app/{[lang] => [locale]}/components/client/elements/calendar-view.tsx (100%) rename src/app/{[lang] => [locale]}/components/client/elements/index.ts (100%) rename src/app/{[lang] => [locale]}/components/client/elements/link-decorator.tsx (100%) rename src/app/{[lang] => [locale]}/components/client/elements/list-view.tsx (100%) rename src/app/{[lang] => [locale]}/components/client/elements/map-view.tsx (100%) rename src/app/{[lang] => [locale]}/components/client/elements/navbar-view.tsx (100%) rename src/app/{[lang] => [locale]}/components/client/elements/signin-view.tsx (83%) rename src/app/{[lang] => [locale]}/components/client/elements/signup-view.tsx (95%) rename src/app/{[lang] => [locale]}/components/client/elements/usersettings-view.tsx (100%) rename src/app/{[lang] => [locale]}/components/client/index.ts (100%) rename src/app/{[lang] => [locale]}/components/index.ts (100%) rename src/app/{[lang] => [locale]}/components/navbar.tsx (100%) rename src/app/{[lang] => [locale]}/components/server/blocks/hypnos-public-list.tsx (100%) rename src/app/{[lang] => [locale]}/components/server/blocks/index.ts (100%) rename src/app/{[lang] => [locale]}/components/server/blocks/rm-list.tsx (100%) rename src/app/{[lang] => [locale]}/components/server/blocks/topnav.tsx (100%) rename src/app/{[lang] => [locale]}/components/server/index.ts (100%) rename src/app/{[lang] => [locale]}/components/server/navbar-controller.tsx (100%) rename src/app/{[lang] => [locale]}/components/server/signup-controller.tsx (100%) rename src/app/{[lang] => [locale]}/components/server/usersettings-controller.tsx (100%) rename src/app/{[lang] => [locale]}/components/toolbar.tsx (100%) rename src/app/{[lang] => [locale]}/dash/error/page.tsx (100%) rename src/app/{[lang] => [locale]}/dash/services/hypnos/[mode]/page.tsx (100%) rename src/app/{[lang] => [locale]}/dash/services/rickmorty/[mode]/page.tsx (100%) rename src/app/{[lang] => [locale]}/dash/signin/page.tsx (100%) rename src/app/{[lang] => [locale]}/dash/verify/page.tsx (100%) rename src/app/{[lang] => [locale]}/favicon.ico (100%) rename src/app/{[lang] => [locale]}/gateway/client/actions.ts (100%) rename src/app/{[lang] => [locale]}/gateway/index.ts (70%) rename src/app/{[lang] => [locale]}/gateway/server/actions.ts (76%) rename src/app/{[lang] => [locale]}/gateway/server/hypnos/private/actions.ts (100%) rename src/app/{[lang] => [locale]}/gateway/server/hypnos/private/index.ts (100%) rename src/app/{[lang] => [locale]}/gateway/server/hypnos/public/actions.ts (100%) rename src/app/{[lang] => [locale]}/gateway/server/hypnos/public/index.ts (100%) rename src/app/{[lang] => [locale]}/global-error.jsx (100%) rename src/app/{[lang] => [locale]}/globals.css (100%) create mode 100644 src/app/[locale]/i18n.ts create mode 100644 src/app/[locale]/layout.tsx rename src/app/{[lang] => [locale]}/page.tsx (100%) rename src/app/{[lang] => [locale]}/styles/page.module.css (100%) create mode 100644 src/middlewares/authMiddleware.ts create mode 100644 src/middlewares/i18nDetectMiddleware.ts diff --git a/lib/dictionaries/global/cs-cz.json b/lib/dictionaries/global/cs-cz.json new file mode 100644 index 00000000..f4595c86 --- /dev/null +++ b/lib/dictionaries/global/cs-cz.json @@ -0,0 +1,15 @@ +{ + "NavBar": { + "welcome": "Vítejte" + }, + "SignIn": { + "continue": "Pokračovat", + "continue with": "Pokračovat s", + "sign in": "Přihlásit", + "sign out": "Odhlásit", + "your email": "Váš e-mail" + }, + "CardGrid": { + "staff picks": "Výběr zaměstnanců" + } +} diff --git a/lib/dictionaries/global/de-de.json b/lib/dictionaries/global/de-de.json new file mode 100644 index 00000000..7a5729b5 --- /dev/null +++ b/lib/dictionaries/global/de-de.json @@ -0,0 +1,15 @@ +{ + "NavBar": { + "welcome": "Willkommen" + }, + "SignIn": { + "continue": "Weiter", + "continue with": "Weiter mit", + "sign in": "Anmelden", + "sign out": "Abmelden", + "your email": "Deine E-Mail" + }, + "CardGrid": { + "staff picks": "Auswahl des Personals" + } +} diff --git a/lib/dictionaries/global/default.json b/lib/dictionaries/global/default.json new file mode 100644 index 00000000..bc506591 --- /dev/null +++ b/lib/dictionaries/global/default.json @@ -0,0 +1,15 @@ +{ + "NavBar": { + "welcome": "Welcome" + }, + "SignIn": { + "continue": "Continue", + "continue with": "Continue with", + "sign in": "Sign in", + "sign out": "Sign out", + "your email": "Your email" + }, + "CardGrid": { + "staff picks": "Staff Picks" + } +} diff --git a/lib/dictionaries/global/en.json b/lib/dictionaries/global/en.json new file mode 100644 index 00000000..ae88cfc3 --- /dev/null +++ b/lib/dictionaries/global/en.json @@ -0,0 +1,14 @@ +{ + "NavBar": { + "welcome": "Welcome" + }, + "SignIn": { + "continue": "Continue", + "continue with": "Continue with", + "sign in": "Sign in", + "sign out": "Sign out" + }, + "CardGrid": { + "staff picks": "Staff Picks" + } +} diff --git a/lib/dictionaries/global/es-es.json b/lib/dictionaries/global/es-es.json new file mode 100644 index 00000000..1a804560 --- /dev/null +++ b/lib/dictionaries/global/es-es.json @@ -0,0 +1,15 @@ +{ + "NavBar": { + "welcome": "Bienvenido" + }, + "SignIn": { + "continue": "Continuar", + "continue with": "Continuar con", + "sign in": "Iniciar sesión", + "sign out": "Cerrar sesión", + "your email": "Tu correo electrónico" + }, + "CardGrid": { + "staff picks": "Selecciones del personal" + } +} diff --git a/lib/dictionaries/global/et-ee.json b/lib/dictionaries/global/et-ee.json new file mode 100644 index 00000000..1896dd6c --- /dev/null +++ b/lib/dictionaries/global/et-ee.json @@ -0,0 +1,15 @@ +{ + "NavBar": { + "welcome": "Tere tulemast" + }, + "SignIn": { + "continue": "Jätka", + "continue with": "Jätka", + "sign in": "Logi sisse", + "sign out": "Logi välja", + "your email": "Sinu e-post" + }, + "CardGrid": { + "staff picks": "Töötajate valikud" + } +} diff --git a/lib/dictionaries/global/fr-fr.json b/lib/dictionaries/global/fr-fr.json new file mode 100644 index 00000000..21db518c --- /dev/null +++ b/lib/dictionaries/global/fr-fr.json @@ -0,0 +1,15 @@ +{ + "NavBar": { + "welcome": "Bienvenue" + }, + "SignIn": { + "continue": "Continuer", + "continue with": "Continuer avec", + "sign in": "Se connecter", + "sign out": "Se déconnecter", + "your email": "Votre adresse e-mail" + }, + "CardGrid": { + "staff picks": "Choix du personnel" + } +} diff --git a/lib/dictionaries/global/it-it.json b/lib/dictionaries/global/it-it.json new file mode 100644 index 00000000..7cd1f4a4 --- /dev/null +++ b/lib/dictionaries/global/it-it.json @@ -0,0 +1,15 @@ +{ + "NavBar": { + "welcome": "Benvenuto" + }, + "SignIn": { + "continue": "Prosegue", + "continue with": "Prosegue con", + "sign in": "Accedi", + "sign out": "Scollega", + "your email": "La sua mail" + }, + "CardGrid": { + "staff picks": "Preferiti dello Staff" + } +} diff --git a/lib/dictionaries/global/ja-jp.json b/lib/dictionaries/global/ja-jp.json new file mode 100644 index 00000000..2d3c71e9 --- /dev/null +++ b/lib/dictionaries/global/ja-jp.json @@ -0,0 +1,15 @@ +{ + "NavBar": { + "welcome": "ようこそ" + }, + "SignIn": { + "continue": "続行", + "continue with": "続行する", + "sign in": "サインイン", + "sign out": "ログアウト", + "your email": "あなたのメール" + }, + "CardGrid": { + "staff picks": "スタッフおすすめ" + } +} diff --git a/lib/dictionaries/global/pl-pl.json b/lib/dictionaries/global/pl-pl.json new file mode 100644 index 00000000..095d9adb --- /dev/null +++ b/lib/dictionaries/global/pl-pl.json @@ -0,0 +1,15 @@ +{ + "NavBar": { + "welcome": "Witaj" + }, + "SignIn": { + "continue": "Kontynuuj", + "continue with": "Kontynuuj z", + "sign in": "Zaloguj się", + "sign out": "Wyloguj się", + "your email": "Twój adres email" + }, + "CardGrid": { + "staff picks": "Wybór pracowników" + } +} diff --git a/lib/dictionaries/global/pt-br.json b/lib/dictionaries/global/pt-br.json new file mode 100644 index 00000000..8708a40f --- /dev/null +++ b/lib/dictionaries/global/pt-br.json @@ -0,0 +1,11 @@ +{ + "NavBar": { "welcome": "Bem-vindo" }, + "SignIn": { + "continue": "Continuar", + "continue with": "Continuar com", + "sign in": "Entrar", + "sign out": "Sair", + "your email": "Seu email" + }, + "CardGrid": { "staff picks": "Escolhas da Equipe" } +} diff --git a/lib/dictionaries/global/ro.json b/lib/dictionaries/global/ro.json new file mode 100644 index 00000000..81defd72 --- /dev/null +++ b/lib/dictionaries/global/ro.json @@ -0,0 +1,15 @@ +{ + "NavBar": { + "welcome": "Bine ai venit" + }, + "SignIn": { + "continue": "Continuați", + "continue with": "Continuați cu", + "sign in": "Conectați-vă", + "sign out": "Deconectați-vă", + "your email": "Adresa ta de email" + }, + "CardGrid": { + "staff picks": "Alegerile personalului" + } +} diff --git a/lib/dictionaries/global/ru-ru.json b/lib/dictionaries/global/ru-ru.json new file mode 100644 index 00000000..7f392d86 --- /dev/null +++ b/lib/dictionaries/global/ru-ru.json @@ -0,0 +1,15 @@ +{ + "NavBar": { + "welcome": "Добро пожаловать" + }, + "SignIn": { + "continue": "Продолжить", + "continue with": "Продолжить с", + "sign in": "Войти", + "sign out": "Выйти", + "your email": "Ваш электронный адрес" + }, + "CardGrid": { + "staff picks": "Выбор персонала" + } +} diff --git a/lib/dictionaries/global/sv-se.json b/lib/dictionaries/global/sv-se.json new file mode 100644 index 00000000..112636b6 --- /dev/null +++ b/lib/dictionaries/global/sv-se.json @@ -0,0 +1,15 @@ +{ + "NavBar": { + "welcome": "Välkommen" + }, + "SignIn": { + "continue": "Fortsätt", + "continue with": "Fortsätt med", + "sign in": "Logga in", + "sign out": "Logga ut", + "your email": "Din e-post" + }, + "CardGrid": { + "staff picks": "Personals val" + } +} diff --git a/lib/state/providers.tsx b/lib/state/providers.tsx index 2e8e779a..a3998cbe 100644 --- a/lib/state/providers.tsx +++ b/lib/state/providers.tsx @@ -16,7 +16,7 @@ export function RootProviders({ children, locale }: { children: React.ReactNode; const [globalState, setGlobalState] = useState({ ...globalContext, locale }); const init = useRef(false); - const [storedGlobal, setStoredGlobal] = useLocalStorage('globalSettings', { theme: 'dark' }); + const [storedGlobal, setStoredGlobal] = useLocalStorage('globalSettings', { theme: 'dark', locale }); const handleGlobalSettingUpdate = (next: any) => { setGlobalState(next); diff --git a/next.config.js b/next.config.js index 92e254d8..306993fb 100644 --- a/next.config.js +++ b/next.config.js @@ -1,5 +1,8 @@ /** @type {import('next').NextConfig} */ const { withSentryConfig } = require('@sentry/nextjs'); +const createNextIntlPlugin = require('next-intl/plugin'); +const withNextIntl = createNextIntlPlugin('./src/app/[locale]/i18n.ts'); + const nextConfig = { assetPrefix: process.env.NEXUS_HOST || 'https://nyx.dreampip.com', transpilePackages: ['next-auth'], @@ -37,7 +40,7 @@ const nextConfig = { }, }; -module.exports = nextConfig; +module.exports = withNextIntl(nextConfig); module.exports = withSentryConfig( module.exports, diff --git a/package-lock.json b/package-lock.json index 3a7c060f..78bab80c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "eslint-config-next": "14.0.4", "husky": "9.0.11", "lodash": "4.17.21", + "next-intl": "^3.17.4", "postcss": "^8.4.38", "prettier": "3.2.4", "server-only": "0.0.1", @@ -511,6 +512,55 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz", + "integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==", + "dev": true, + "dependencies": { + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", + "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", + "dev": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.8.tgz", + "integrity": "sha512-nBZJYmhpcSX0WeJ5SDYUkZ42AgR3xiyhNCsQweFx3cz/ULJjym8bHAzWKvG5e2+1XO98dBYC0fWeeAECAVSwLA==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/icu-skeleton-parser": "1.8.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.2.tgz", + "integrity": "sha512-k4ERKgw7aKGWJZgTarIcNEmvyTVD9FYh0mTrrBMHZ1b8hUu6iOJ4SzsZlo3UNAvHYa+PnvntIwRPt1/vy4nA9Q==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", + "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", + "dev": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -4083,6 +4133,18 @@ "node": ">= 0.4" } }, + "node_modules/intl-messageformat": { + "version": "10.5.14", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.14.tgz", + "integrity": "sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "2.0.0", + "@formatjs/fast-memoize": "2.2.0", + "@formatjs/icu-messageformat-parser": "2.7.8", + "tslib": "^2.4.0" + } + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -4826,6 +4888,15 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next": { "version": "14.2.4", "resolved": "https://registry.npmjs.org/next/-/next-14.2.4.tgz", @@ -4876,6 +4947,27 @@ } } }, + "node_modules/next-intl": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-3.17.4.tgz", + "integrity": "sha512-ro3yNIaMNVhCmCdG6u9R00HllMdJXsGdKkBaBq75iM0sSnjLr7IytiGmCuZsUMDqCnGswXfXvs/FjI/lC8OAOw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "/~https://github.com/sponsors/amannn" + } + ], + "dependencies": { + "@formatjs/intl-localematcher": "^0.5.4", + "negotiator": "^0.6.3", + "use-intl": "^3.17.4" + }, + "peerDependencies": { + "next": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -6534,6 +6626,19 @@ "punycode": "^2.1.0" } }, + "node_modules/use-intl": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-3.17.4.tgz", + "integrity": "sha512-6t3tScvli9TvIBwordjZul59ubYzStcMTCgYJEkEikVGqBJKzfpdpifZhRTU7CxgSoB63rt9+AOPGKklXvtebA==", + "dev": true, + "dependencies": { + "@formatjs/fast-memoize": "^2.2.0", + "intl-messageformat": "^10.5.14" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 749f9e16..4bffe52f 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "eslint-config-next": "14.0.4", "husky": "9.0.11", "lodash": "4.17.21", + "next-intl": "^3.17.4", "postcss": "^8.4.38", "prettier": "3.2.4", "server-only": "0.0.1", diff --git a/src/app/[lang]/layout.tsx b/src/app/[lang]/layout.tsx deleted file mode 100644 index 03686522..00000000 --- a/src/app/[lang]/layout.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import type { Metadata } from 'next'; -import { DPTopNav } from '@blocks/server'; -import { RootProviders } from '@state'; -import './globals.css'; - -export const metadata: Metadata = { - title: process.env.PATTERNS_TITLE, - description: process.env.PATTERNS_DESCRIPTION, -}; - -export default function RootLayout({ children, params }: { children: React.ReactNode; params: any }) { - const { lang: orig } = params; - const locale = orig === 'default' ? 'en' : orig; - - return ( - - - - - {children} - - - - ); -} diff --git a/src/app/[lang]/components/client/blocks/index.ts b/src/app/[locale]/components/client/blocks/index.ts similarity index 100% rename from src/app/[lang]/components/client/blocks/index.ts rename to src/app/[locale]/components/client/blocks/index.ts diff --git a/src/app/[lang]/components/client/blocks/topnav-view.tsx b/src/app/[locale]/components/client/blocks/topnav-view.tsx similarity index 94% rename from src/app/[lang]/components/client/blocks/topnav-view.tsx rename to src/app/[locale]/components/client/blocks/topnav-view.tsx index bdc8f7d4..3c2a395f 100644 --- a/src/app/[lang]/components/client/blocks/topnav-view.tsx +++ b/src/app/[locale]/components/client/blocks/topnav-view.tsx @@ -1,13 +1,15 @@ // @block/topnav-view.tsx 'use client'; import type { UserSchema } from '@types'; -import { getSession } from '@auth' +import { getSession } from '@auth'; import { useContext, useRef, useEffect, useState, useMemo } from 'react'; import { AuthContext, GlobalContext } from '@state'; import { ASwitchThemes, ALogIn } from '@actions'; import { navigate } from '@gateway'; import { AudioPlayer, Button as DPButton, EGridVariant, Grid as DPGrid, EBleedVariant, Typography as DPTypo, TypographyVariant, ESystemIcon } from "@dreampipcom/oneiros"; import { VSignIn, InternalLink } from '@elements/client'; +import { useTranslations } from 'next-intl'; + interface IAuthProvider { id?: string; @@ -24,8 +26,10 @@ export const VTopNav = ({ user }: VTopNavProps) => { const authContext = useContext(AuthContext); const globalContext = useContext(GlobalContext); + const t = useTranslations('NavBar'); + const { authd, name } = authContext; - const { theme } = globalContext; + const { theme, locale } = globalContext; const initd = useRef(false); @@ -66,7 +70,7 @@ export const VTopNav = ({ user }: VTopNavProps) => {
- Welcome, {coercedName} + {t('welcome')}, {coercedName} Rick Morty diff --git a/src/app/[lang]/components/client/elements/calendar-view.tsx b/src/app/[locale]/components/client/elements/calendar-view.tsx similarity index 100% rename from src/app/[lang]/components/client/elements/calendar-view.tsx rename to src/app/[locale]/components/client/elements/calendar-view.tsx diff --git a/src/app/[lang]/components/client/elements/index.ts b/src/app/[locale]/components/client/elements/index.ts similarity index 100% rename from src/app/[lang]/components/client/elements/index.ts rename to src/app/[locale]/components/client/elements/index.ts diff --git a/src/app/[lang]/components/client/elements/link-decorator.tsx b/src/app/[locale]/components/client/elements/link-decorator.tsx similarity index 100% rename from src/app/[lang]/components/client/elements/link-decorator.tsx rename to src/app/[locale]/components/client/elements/link-decorator.tsx diff --git a/src/app/[lang]/components/client/elements/list-view.tsx b/src/app/[locale]/components/client/elements/list-view.tsx similarity index 100% rename from src/app/[lang]/components/client/elements/list-view.tsx rename to src/app/[locale]/components/client/elements/list-view.tsx diff --git a/src/app/[lang]/components/client/elements/map-view.tsx b/src/app/[locale]/components/client/elements/map-view.tsx similarity index 100% rename from src/app/[lang]/components/client/elements/map-view.tsx rename to src/app/[locale]/components/client/elements/map-view.tsx diff --git a/src/app/[lang]/components/client/elements/navbar-view.tsx b/src/app/[locale]/components/client/elements/navbar-view.tsx similarity index 100% rename from src/app/[lang]/components/client/elements/navbar-view.tsx rename to src/app/[locale]/components/client/elements/navbar-view.tsx diff --git a/src/app/[lang]/components/client/elements/signin-view.tsx b/src/app/[locale]/components/client/elements/signin-view.tsx similarity index 83% rename from src/app/[lang]/components/client/elements/signin-view.tsx rename to src/app/[locale]/components/client/elements/signin-view.tsx index f776c5eb..5f0a1462 100644 --- a/src/app/[lang]/components/client/elements/signin-view.tsx +++ b/src/app/[locale]/components/client/elements/signin-view.tsx @@ -1,6 +1,7 @@ // signin-view.ts 'use client'; import { useContext, useEffect, useRef } from 'react'; +import { useTranslations } from 'next-intl'; import { signOut } from '@auth'; import { AuthContext, GlobalContext } from '@state'; import { ALogOut } from '@actions'; @@ -32,6 +33,8 @@ export const VSignIn = ({ user }: VSignInProps) => { const [, unloadUser] = ALogOut({}); const initd = useRef(false); + const t = useTranslations('SignIn'); + const handleSignOut = async () => { unloadUser(); @@ -41,9 +44,9 @@ export const VSignIn = ({ user }: VSignInProps) => { if (user || authd) return (
- Sign out + {t('sign out')}
); - return navigate('/api/v1/auth/signin')}>Sign in; + return navigate('/api/v1/auth/signin')}>{t('sign in')}; }; diff --git a/src/app/[lang]/components/client/elements/signup-view.tsx b/src/app/[locale]/components/client/elements/signup-view.tsx similarity index 95% rename from src/app/[lang]/components/client/elements/signup-view.tsx rename to src/app/[locale]/components/client/elements/signup-view.tsx index 6b0d5950..e670a550 100644 --- a/src/app/[lang]/components/client/elements/signup-view.tsx +++ b/src/app/[locale]/components/client/elements/signup-view.tsx @@ -2,6 +2,7 @@ 'use client'; import { clsx } from "clsx"; import { useContext, useEffect, useRef, useState } from 'react'; +import { useTranslations } from 'next-intl'; import { signIn, signOut, getCsrf } from "@auth"; import { AuthContext } from '@state'; import { ALogIn, ALogOut } from '@actions'; @@ -47,6 +48,8 @@ export const VSignUp = ({ providers, user }: VSignUpProps) => { const signInUrl = '/api/v1/auth/signin' + const t = useTranslations('SignIn'); + const callbackUrl = process.env.NEXT_PUBLIC_NEXUS_BASE_PATH || "/" @@ -111,12 +114,12 @@ export const VSignUp = ({ providers, user }: VSignUpProps) => { name="email" value={email} onChange={(e) => setEmail(e)} - label="Your email" + label={t("your email")} className="pb-a1" placeholder="jack@doe.com" />
@@ -135,7 +138,7 @@ export const VSignUp = ({ providers, user }: VSignUpProps) => { )} diff --git a/src/app/[lang]/components/client/elements/usersettings-view.tsx b/src/app/[locale]/components/client/elements/usersettings-view.tsx similarity index 100% rename from src/app/[lang]/components/client/elements/usersettings-view.tsx rename to src/app/[locale]/components/client/elements/usersettings-view.tsx diff --git a/src/app/[lang]/components/client/index.ts b/src/app/[locale]/components/client/index.ts similarity index 100% rename from src/app/[lang]/components/client/index.ts rename to src/app/[locale]/components/client/index.ts diff --git a/src/app/[lang]/components/index.ts b/src/app/[locale]/components/index.ts similarity index 100% rename from src/app/[lang]/components/index.ts rename to src/app/[locale]/components/index.ts diff --git a/src/app/[lang]/components/navbar.tsx b/src/app/[locale]/components/navbar.tsx similarity index 100% rename from src/app/[lang]/components/navbar.tsx rename to src/app/[locale]/components/navbar.tsx diff --git a/src/app/[lang]/components/server/blocks/hypnos-public-list.tsx b/src/app/[locale]/components/server/blocks/hypnos-public-list.tsx similarity index 100% rename from src/app/[lang]/components/server/blocks/hypnos-public-list.tsx rename to src/app/[locale]/components/server/blocks/hypnos-public-list.tsx diff --git a/src/app/[lang]/components/server/blocks/index.ts b/src/app/[locale]/components/server/blocks/index.ts similarity index 100% rename from src/app/[lang]/components/server/blocks/index.ts rename to src/app/[locale]/components/server/blocks/index.ts diff --git a/src/app/[lang]/components/server/blocks/rm-list.tsx b/src/app/[locale]/components/server/blocks/rm-list.tsx similarity index 100% rename from src/app/[lang]/components/server/blocks/rm-list.tsx rename to src/app/[locale]/components/server/blocks/rm-list.tsx diff --git a/src/app/[lang]/components/server/blocks/topnav.tsx b/src/app/[locale]/components/server/blocks/topnav.tsx similarity index 100% rename from src/app/[lang]/components/server/blocks/topnav.tsx rename to src/app/[locale]/components/server/blocks/topnav.tsx diff --git a/src/app/[lang]/components/server/index.ts b/src/app/[locale]/components/server/index.ts similarity index 100% rename from src/app/[lang]/components/server/index.ts rename to src/app/[locale]/components/server/index.ts diff --git a/src/app/[lang]/components/server/navbar-controller.tsx b/src/app/[locale]/components/server/navbar-controller.tsx similarity index 100% rename from src/app/[lang]/components/server/navbar-controller.tsx rename to src/app/[locale]/components/server/navbar-controller.tsx diff --git a/src/app/[lang]/components/server/signup-controller.tsx b/src/app/[locale]/components/server/signup-controller.tsx similarity index 100% rename from src/app/[lang]/components/server/signup-controller.tsx rename to src/app/[locale]/components/server/signup-controller.tsx diff --git a/src/app/[lang]/components/server/usersettings-controller.tsx b/src/app/[locale]/components/server/usersettings-controller.tsx similarity index 100% rename from src/app/[lang]/components/server/usersettings-controller.tsx rename to src/app/[locale]/components/server/usersettings-controller.tsx diff --git a/src/app/[lang]/components/toolbar.tsx b/src/app/[locale]/components/toolbar.tsx similarity index 100% rename from src/app/[lang]/components/toolbar.tsx rename to src/app/[locale]/components/toolbar.tsx diff --git a/src/app/[lang]/dash/error/page.tsx b/src/app/[locale]/dash/error/page.tsx similarity index 100% rename from src/app/[lang]/dash/error/page.tsx rename to src/app/[locale]/dash/error/page.tsx diff --git a/src/app/[lang]/dash/services/hypnos/[mode]/page.tsx b/src/app/[locale]/dash/services/hypnos/[mode]/page.tsx similarity index 100% rename from src/app/[lang]/dash/services/hypnos/[mode]/page.tsx rename to src/app/[locale]/dash/services/hypnos/[mode]/page.tsx diff --git a/src/app/[lang]/dash/services/rickmorty/[mode]/page.tsx b/src/app/[locale]/dash/services/rickmorty/[mode]/page.tsx similarity index 100% rename from src/app/[lang]/dash/services/rickmorty/[mode]/page.tsx rename to src/app/[locale]/dash/services/rickmorty/[mode]/page.tsx diff --git a/src/app/[lang]/dash/signin/page.tsx b/src/app/[locale]/dash/signin/page.tsx similarity index 100% rename from src/app/[lang]/dash/signin/page.tsx rename to src/app/[locale]/dash/signin/page.tsx diff --git a/src/app/[lang]/dash/verify/page.tsx b/src/app/[locale]/dash/verify/page.tsx similarity index 100% rename from src/app/[lang]/dash/verify/page.tsx rename to src/app/[locale]/dash/verify/page.tsx diff --git a/src/app/[lang]/favicon.ico b/src/app/[locale]/favicon.ico similarity index 100% rename from src/app/[lang]/favicon.ico rename to src/app/[locale]/favicon.ico diff --git a/src/app/[lang]/gateway/client/actions.ts b/src/app/[locale]/gateway/client/actions.ts similarity index 100% rename from src/app/[lang]/gateway/client/actions.ts rename to src/app/[locale]/gateway/client/actions.ts diff --git a/src/app/[lang]/gateway/index.ts b/src/app/[locale]/gateway/index.ts similarity index 70% rename from src/app/[lang]/gateway/index.ts rename to src/app/[locale]/gateway/index.ts index fa20fcbf..bef6d9b8 100644 --- a/src/app/[lang]/gateway/index.ts +++ b/src/app/[locale]/gateway/index.ts @@ -4,7 +4,7 @@ export { navigate, setCookie, getCookie } from './client/actions'; // server -export { getUser, loadChars, reloadChars, getChars } from './server/actions'; +export { getUser, loadChars, reloadChars, getChars, getUserLocale, setUserLocale } from './server/actions'; // hypnos-public export { loadHypnosPublicListings } from './server/hypnos/public'; diff --git a/src/app/[lang]/gateway/server/actions.ts b/src/app/[locale]/gateway/server/actions.ts similarity index 76% rename from src/app/[lang]/gateway/server/actions.ts rename to src/app/[locale]/gateway/server/actions.ts index df3d921f..9db70a1a 100644 --- a/src/app/[lang]/gateway/server/actions.ts +++ b/src/app/[locale]/gateway/server/actions.ts @@ -2,7 +2,7 @@ // actions.ts 'use server'; // import type { UserSchema } from '@types'; -import { cookies } from 'next/headers'; +import { cookies, headers } from 'next/headers'; import { getRMCharacters } from '@controller'; import { decorateRMCharacters } from '@model'; import { getSession } from '@auth'; @@ -43,3 +43,15 @@ export async function getUser() { // we might need to decorate users in the future, // reference decorateRMCharactes() } + +const COOKIE_NAME = 'NEXT_LOCALE'; +const defaultLocale = 'default'; + +export async function getUserLocale() { + const headersList = headers(); + return headersList.get('x-dp-locale') || cookies().get(COOKIE_NAME)?.value || defaultLocale; +} + +export async function setUserLocale(locale: string) { + cookies().set(COOKIE_NAME, locale); +} diff --git a/src/app/[lang]/gateway/server/hypnos/private/actions.ts b/src/app/[locale]/gateway/server/hypnos/private/actions.ts similarity index 100% rename from src/app/[lang]/gateway/server/hypnos/private/actions.ts rename to src/app/[locale]/gateway/server/hypnos/private/actions.ts diff --git a/src/app/[lang]/gateway/server/hypnos/private/index.ts b/src/app/[locale]/gateway/server/hypnos/private/index.ts similarity index 100% rename from src/app/[lang]/gateway/server/hypnos/private/index.ts rename to src/app/[locale]/gateway/server/hypnos/private/index.ts diff --git a/src/app/[lang]/gateway/server/hypnos/public/actions.ts b/src/app/[locale]/gateway/server/hypnos/public/actions.ts similarity index 100% rename from src/app/[lang]/gateway/server/hypnos/public/actions.ts rename to src/app/[locale]/gateway/server/hypnos/public/actions.ts diff --git a/src/app/[lang]/gateway/server/hypnos/public/index.ts b/src/app/[locale]/gateway/server/hypnos/public/index.ts similarity index 100% rename from src/app/[lang]/gateway/server/hypnos/public/index.ts rename to src/app/[locale]/gateway/server/hypnos/public/index.ts diff --git a/src/app/[lang]/global-error.jsx b/src/app/[locale]/global-error.jsx similarity index 100% rename from src/app/[lang]/global-error.jsx rename to src/app/[locale]/global-error.jsx diff --git a/src/app/[lang]/globals.css b/src/app/[locale]/globals.css similarity index 100% rename from src/app/[lang]/globals.css rename to src/app/[locale]/globals.css diff --git a/src/app/[locale]/i18n.ts b/src/app/[locale]/i18n.ts new file mode 100644 index 00000000..28d3b67f --- /dev/null +++ b/src/app/[locale]/i18n.ts @@ -0,0 +1,13 @@ +import { getRequestConfig } from 'next-intl/server'; +import { getUserLocale } from '@gateway'; + +export default getRequestConfig(async () => { + const userLocale = await getUserLocale(); + + const locale = userLocale || 'en'; + + return { + locale, + messages: (await import(`../../../lib/dictionaries/global/${locale}.json`)).default, + }; +}); diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx new file mode 100644 index 00000000..70047dab --- /dev/null +++ b/src/app/[locale]/layout.tsx @@ -0,0 +1,32 @@ +import type { Metadata } from 'next'; +import { DPTopNav } from '@blocks/server'; +import { RootProviders } from '@state'; +import { NextIntlClientProvider } from 'next-intl'; +import { getLocale, getMessages } from 'next-intl/server'; +import './globals.css'; + +export const metadata: Metadata = { + title: process.env.PATTERNS_TITLE, + description: process.env.PATTERNS_DESCRIPTION, +}; + +export default async function RootLayout({ children, params }: { children: React.ReactNode; params: any }) { + const { locale: orig } = params; + const locale = orig === 'default' ? 'en' : orig; + + const messages = await getMessages(); + const libLocale = await getLocale(); + + return ( + + + + + + {children} + + + + + ); +} diff --git a/src/app/[lang]/page.tsx b/src/app/[locale]/page.tsx similarity index 100% rename from src/app/[lang]/page.tsx rename to src/app/[locale]/page.tsx diff --git a/src/app/[lang]/styles/page.module.css b/src/app/[locale]/styles/page.module.css similarity index 100% rename from src/app/[lang]/styles/page.module.css rename to src/app/[locale]/styles/page.module.css diff --git a/src/middleware.ts b/src/middleware.ts index e3b42ee7..924edb08 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,96 +1,21 @@ -// middleware.ts +// @middleware import type { NextRequest } from 'next/server'; import { NextResponse } from 'next/server'; -import acceptLanguage from 'accept-language'; -import { localeMap, LOCALES } from '@constants/server'; +import { authMiddleware } from './middlewares/authMiddleware'; +import { i18nDetectMiddleware } from './middlewares/i18nDetectMiddleware'; -const supportedLocales = [ - 'en', - 'it-IT', - 'pt-BR', - 'it', - 'pt', - 'ro', - 'ru', - 'pl-PL', - 'de', - 'fr', - 'ja-JP', - 'sv-SE', - 'et-EE', - 'cs-CZ', -]; - -acceptLanguage.languages(supportedLocales); +export const middlewares = [authMiddleware, i18nDetectMiddleware]; export const config = { - matcher: ['/api/:path*', '/default/dash/:path*'], -}; - -const allowedOrigins = { - [`${process.env.MAIN_URL}`]: process.env.MAIN_URL, - [`${process.env.NEXUS_HOST}`]: process.env.NEXUS_HOST, - [`${process.env.API_HOST}`]: process.env.API_HOST, + matcher: ['/api/:path*', '/(default|cs-cz|de-de|en|es-es|et-ee|fr-fr|it-it|ja-jp|pl-pl|ro|ru-ru|sv-se)/:path*'], }; -const headers: Record = { - 'Access-Control-Allow-Origin': process.env.MAIN_URL || 'https://www.dreampip.com', - 'Cache-Control': 'maxage=0, s-maxage=300, stale-while-revalidate=300', - // DEV-DEBUG: - // 'content-type': 'application/json', - // 'Access-Control-Allow-Origin': 'http://localhost:2999', - 'Access-Control-Allow-Credentials': 'true', - 'Access-Control-Allow-Headers': 'baggage, sentry-trace', -}; - -export function middleware(request: NextRequest) { - // API COOKIES - if (request.nextUrl.pathname.startsWith('/api')) { - const origin = request.headers.get('x-forwarded-host') || ''; - if (origin !== process.env.MAIN_URL) { - headers['Access-Control-Allow-Origin'] = allowedOrigins[origin] || 'https://www.dreampip.com'; - } - - const response = NextResponse.next(); - const pkce = request.cookies.get('next-auth.pkce.code_verifier'); - - Object.keys(headers).forEach((key: string) => { - response.headers.set(key, headers[key]); - }); - - if (pkce?.value) { - response.cookies.set('next-auth.pkce.code_verifier', pkce.value, { - httpOnly: true, - sameSite: 'none', - path: '/', - secure: true, - }); - console.log({ pkce, response, to: request.nextUrl.pathname }); - } - return NextResponse.rewrite( - new URL( - `${process.env.REMOTE_DEV ? process.env.API_HOST_DEV : process.env.API_HOST}${request.nextUrl.pathname}${request.nextUrl.search}`, - ), - response, - ); +export default async function middleware(request: NextRequest) { + // if a response is returned, return it otherwise call `next()` + for (const fn of middlewares) { + const response = await fn(request); + if (response) return response; } - // LOCALIZATION - if ( - !/\.(.*)$/.test(request.nextUrl.pathname) && - (LOCALES.every((locale) => !request.nextUrl.href.includes(locale)) || - request.nextUrl.pathname.startsWith('/default')) - ) { - const newUrl = request.nextUrl.clone(); - const headers = request.headers.get('accept-language'); - - if (!headers) return NextResponse.rewrite(newUrl); - const savedLocale = request?.cookies?.get('NEXT_LOCALE'); - const newlocale = (savedLocale?.value || - acceptLanguage?.get(headers)?.toLocaleLowerCase() || - 'en') as keyof typeof localeMap; - newUrl.pathname = newUrl?.pathname?.replace('/default', localeMap[newlocale] || newlocale); - - return NextResponse.redirect(newUrl); - } + return NextResponse.next(); } diff --git a/src/middlewares/authMiddleware.ts b/src/middlewares/authMiddleware.ts new file mode 100644 index 00000000..1746ff23 --- /dev/null +++ b/src/middlewares/authMiddleware.ts @@ -0,0 +1,54 @@ +// middleware.ts +import type { NextRequest } from 'next/server'; +import { NextResponse } from 'next/server'; + +const allowedOrigins = { + [`${process.env.MAIN_URL}`]: process.env.MAIN_URL, + [`${process.env.NEXUS_HOST}`]: process.env.NEXUS_HOST, + [`${process.env.API_HOST}`]: process.env.API_HOST, +}; + +const headers: Record = { + 'Access-Control-Allow-Origin': process.env.MAIN_URL || 'https://www.dreampip.com', + 'Cache-Control': 'maxage=0, s-maxage=300, stale-while-revalidate=300', + // DEV-DEBUG: + // 'content-type': 'application/json', + // 'Access-Control-Allow-Origin': 'http://localhost:2999', + 'Access-Control-Allow-Credentials': 'true', + 'Access-Control-Allow-Headers': 'baggage, sentry-trace', +}; + +export const authMiddleware = async (request: NextRequest) => { + console.log('--- ran: API MIDDLEWARE ---'); + // API COOKIES + if (request.nextUrl.pathname.startsWith('/api')) { + const origin = request.headers.get('x-forwarded-host') || ''; + if (origin !== process.env.MAIN_URL) { + headers['Access-Control-Allow-Origin'] = allowedOrigins[origin] || 'https://www.dreampip.com'; + } + + const response = NextResponse.next(); + const pkce = request.cookies.get('next-auth.pkce.code_verifier'); + + Object.keys(headers).forEach((key: string) => { + response.headers.set(key, headers[key]); + }); + + if (pkce?.value) { + response.cookies.set('next-auth.pkce.code_verifier', pkce.value, { + httpOnly: true, + sameSite: 'none', + path: '/', + secure: true, + }); + console.log({ pkce, response, to: request.nextUrl.pathname }); + } + return NextResponse.rewrite( + new URL( + `${process.env.REMOTE_DEV ? process.env.API_HOST_DEV : process.env.API_HOST}${request.nextUrl.pathname}${request.nextUrl.search}`, + ), + response, + ); + } + return; +}; diff --git a/src/middlewares/i18nDetectMiddleware.ts b/src/middlewares/i18nDetectMiddleware.ts new file mode 100644 index 00000000..e5fd1cc0 --- /dev/null +++ b/src/middlewares/i18nDetectMiddleware.ts @@ -0,0 +1,58 @@ +// middleware.ts +'use server'; +import type { NextRequest } from 'next/server'; +import { NextResponse } from 'next/server'; +import acceptLanguage from 'accept-language'; +import { localeMap, LOCALES } from '@constants/server'; + +const supportedLocales = [ + 'en', + 'it-IT', + 'pt-BR', + 'it', + 'pt', + 'ro', + 'ru', + 'pl-PL', + 'de', + 'fr', + 'ja-JP', + 'sv-SE', + 'et-EE', + 'cs-CZ', +]; + +acceptLanguage.languages(supportedLocales); + +export const i18nDetectMiddleware = async (request: NextRequest) => { + console.log('--- ran: I18N DETECT MIDDLEWARE ---'); + // LOCALIZATION + if ( + !/\.(.*)$/.test(request.nextUrl.pathname) && + (LOCALES.every((locale) => !request.nextUrl.href.includes(locale)) || + request.nextUrl.pathname.startsWith('/default')) + ) { + const newUrl = request.nextUrl.clone(); + const headers = request.headers.get('accept-language'); + + if (!headers) return NextResponse.rewrite(newUrl); + const savedLocale = request?.cookies?.get('NEXT_LOCALE'); + const newlocale = (savedLocale?.value || + acceptLanguage?.get(headers)?.toLocaleLowerCase() || + 'en') as keyof typeof localeMap; + newUrl.pathname = newUrl?.pathname?.replace('/default', localeMap[newlocale] || newlocale); + + return NextResponse.redirect(newUrl); + } + + if (!/\.(.*)$/.test(request.nextUrl.pathname) && LOCALES.some((locale) => !request.nextUrl.href.includes(locale))) { + const newHeaders = new Headers(request.headers); + newHeaders.set('x-dp-locale', request.nextUrl.pathname.split('/')[1]); + return NextResponse.next({ + request: { + headers: newHeaders, + }, + }); + } + return; +}; diff --git a/tsconfig.json b/tsconfig.json index 8bac0deb..b164b986 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,18 +19,18 @@ }, ], "paths": { - "@styles": ["./src/app/[lang]/styles"], - "@styles/*": ["./src/app/[lang]/styles/*"], - "@atoms": ["./src/app/[lang]/components/system/atoms"], - "@atoms/*": ["./src/app/[lang]/components/system/atoms/*"], - "@blocks/client": ["./src/app/[lang]/components/client/blocks"], - "@blocks/client/*": ["./src/app/[lang]/components/client/blocks/*"], - "@blocks/server": ["./src/app/[lang]/components/server/blocks"], - "@blocks/server/*": ["./src/app/[lang]/components/server/blocks/*"], + "@styles": ["./src/app/[locale]/styles"], + "@styles/*": ["./src/app/[locale]/styles/*"], + "@atoms": ["./src/app/[locale]/components/system/atoms"], + "@atoms/*": ["./src/app/[locale]/components/system/atoms/*"], + "@blocks/client": ["./src/app/[locale]/components/client/blocks"], + "@blocks/client/*": ["./src/app/[locale]/components/client/blocks/*"], + "@blocks/server": ["./src/app/[locale]/components/server/blocks"], + "@blocks/server/*": ["./src/app/[locale]/components/server/blocks/*"], "@constants/server": ["./lib/constants/server"], "@constants/client": ["./lib/constants/client"], - "@elements/client": ["./src/app/[lang]/components/client/elements"], - "@elements/client/*": ["./src/app/[lang]/components/client/elements/*"], + "@elements/client": ["./src/app/[locale]/components/client/elements"], + "@elements/client/*": ["./src/app/[locale]/components/client/elements/*"], "@model": ["./lib/model"], "@model/*": ["./lib/model/*"], "@view": ["./lib/view"], @@ -42,20 +42,20 @@ "@types": ["./lib/types"], "@types/*": ["./lib/types/*"], "@auth": ["./lib/auth"], - "@auth/adapter": ["./src/app/[lang]/api/auth/[...nextauth]"], - "@auth/adapter/*": ["./src/app/[lang]/api/auth/[...nextauth]/*"], + "@auth/adapter": ["./src/app/[locale]/api/auth/[...nextauth]"], + "@auth/adapter/*": ["./src/app/[locale]/api/auth/[...nextauth]/*"], "@state/*": ["./lib/state/*"], "@state": ["./lib/state"], "@hooks/*": ["./lib/hooks/*"], "@hooks": ["./lib/hooks"], "@actions/*": ["./lib/actions/*"], "@actions": ["./lib/actions"], - "@gateway/*": ["./src/app/[lang]/gateway/*"], - "@gateway": ["./src/app/[lang]/gateway"], + "@gateway/*": ["./src/app/[locale]/gateway/*"], + "@gateway": ["./src/app/[locale]/gateway"], "@controller/*": ["./lib/model/interfaces/*"], "@controller": ["./lib/model/interfaces"], - "@components/*": ["./src/app/[lang]/components/*"], - "@components": ["./src/app/[lang]/components"], + "@components/*": ["./src/app/[locale]/components/*"], + "@components": ["./src/app/[locale]/components"], }, }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],