From 96bd006e4c43f3a032ef840cca93b49f7e571bc5 Mon Sep 17 00:00:00 2001 From: Angelo Reale <12191809+angeloreale@users.noreply.github.com> Date: Thu, 8 Aug 2024 14:24:10 +0100 Subject: [PATCH 01/21] ar(fix) [DPTM-2]: Ensure autonomous Dev environment (#35) * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment --- .env.public | 4 +++- README.md | 3 ++- lib/auth/constants.ts | 6 +++--- next.config.js | 2 +- src/app/components/client/elements/link-decorator.tsx | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.env.public b/.env.public index 35595232..16c37935 100644 --- a/.env.public +++ b/.env.public @@ -10,6 +10,8 @@ # env specific API_HOST_DEV= API_HOST=http://localhost:3001 +NEXUS_HOST=http://localhost:3000 +MAIN_URL=http://localhost:2999 # context specific GITHUB_ID= @@ -48,7 +50,7 @@ MONGODB_DEFAULT_ORG=yourorg # @@@ WARNING: THESE APPEAR IN THE BROWSER OF EVERY VISITOR @@@ # base path: client (browser) -NEXT_PUBLIC_NEXUS_HOST= +NEXT_PUBLIC_MAIN_URL=http://localhost:2999 NEXT_PUBLIC_NEXUS_BASE_PATH= NEXT_PUBLIC_NEXUS_NAME=Nexus NEXT_PUBLIC_NEXUS_LOGO_PATH= diff --git a/README.md b/README.md index 869398a5..e8d3e2b8 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ npm run dev Documentation: TBD -Staging Environment: https://alpha.dreampip.com +Dev Environment: https://dev.nyx.dreampip.com +Prod Environment: https://nyx.dreampip.com License: HPL3-ECO-AND-ANC 2021—Present diff --git a/lib/auth/constants.ts b/lib/auth/constants.ts index 6c4ff121..5f5fe18a 100644 --- a/lib/auth/constants.ts +++ b/lib/auth/constants.ts @@ -11,7 +11,7 @@ const methods = { signIn: () => {}, signOut: async () => { try { - const response = await fetch(`${process.env.NEXT_PUBLIC_NEXUS_HOST}/api/v1/auth/signout`, { + const response = await fetch(`${process.env.NEXT_PUBLIC_MAIN_URL}/api/v1/auth/signout`, { method: 'POST', headers: { Accept: 'application/json', @@ -26,7 +26,7 @@ const methods = { }, getCsrf: async () => { try { - const response = await fetch(`${process.env.NEXT_PUBLIC_NEXUS_HOST}/api/v1/auth/csrf`, { + const response = await fetch(`${process.env.NEXT_PUBLIC_MAIN_URL}/api/v1/auth/csrf`, { method: 'GET', headers: { Accept: 'application/json', @@ -41,7 +41,7 @@ const methods = { }, getSession: async (params = { cookies: '' }) => { try { - const response = await fetch(`${process.env.NEXT_PUBLIC_NEXUS_HOST}/api/v1/auth/session`, { + const response = await fetch(`${process.env.NEXT_PUBLIC_MAIN_URL}/api/v1/auth/session`, { method: 'GET', headers: { Accept: 'application/json', diff --git a/next.config.js b/next.config.js index 212f38ae..832de795 100644 --- a/next.config.js +++ b/next.config.js @@ -1,7 +1,7 @@ /** @type {import('next').NextConfig} */ const { withSentryConfig } = require('@sentry/nextjs'); const nextConfig = { - assetPrefix: process.env.MAIN_URL || 'https://nyx.dreampip.com', + assetPrefix: process.env.NEXUS_HOST || 'https://nyx.dreampip.com', transpilePackages: ['next-auth'], images: { remotePatterns: [ diff --git a/src/app/components/client/elements/link-decorator.tsx b/src/app/components/client/elements/link-decorator.tsx index 95d9e7c8..04bac103 100644 --- a/src/app/components/client/elements/link-decorator.tsx +++ b/src/app/components/client/elements/link-decorator.tsx @@ -4,6 +4,6 @@ import { Link as DPLink } from "@dreampipcom/oneiros" export const DPLinkDec = (props: any) => { return ( - + ); }; From 876e3e5139c50c50cf72344e5d22c37d5fef9b67 Mon Sep 17 00:00:00 2001 From: Angelo Reale <12191809+angeloreale@users.noreply.github.com> Date: Thu, 8 Aug 2024 15:30:14 +0100 Subject: [PATCH 02/21] [DPTM-2] Dev env (#37) * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment --- src/middleware.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/middleware.ts b/src/middleware.ts index 7ccf4cc5..532acf15 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -6,6 +6,12 @@ export const config = { matcher: ['/api/: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, +}; + 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', @@ -17,6 +23,11 @@ const headers: Record = { }; export function middleware(request: NextRequest) { + 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'); From c6f1d83590b1a9739f32d61dcfd23d0884e5d385 Mon Sep 17 00:00:00 2001 From: Angelo Reale <12191809+angeloreale@users.noreply.github.com> Date: Thu, 8 Aug 2024 15:40:46 +0100 Subject: [PATCH 03/21] [DPTM-2] dev env (#38) * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment --- next.config.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/next.config.js b/next.config.js index 832de795..c11205c5 100644 --- a/next.config.js +++ b/next.config.js @@ -11,6 +11,11 @@ const nextConfig = { }, ], }, + experimental: { + serverActions: { + allowedOrigins: [process.env.NEXUS_HOST, process.env.MAIN_URL, process.env.API_HOST], + }, + }, async redirects() { return [ { From 35683b56a3e86795d409347f075277c6ac5e9897 Mon Sep 17 00:00:00 2001 From: Angelo Reale <12191809+angeloreale@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:04:06 +0100 Subject: [PATCH 04/21] [DPTM-2]: dev env (#39) * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment --------- Signed-off-by: Angelo Reale <12191809+angeloreale@users.noreply.github.com> --- next.config.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/next.config.js b/next.config.js index c11205c5..946d4ceb 100644 --- a/next.config.js +++ b/next.config.js @@ -13,7 +13,12 @@ const nextConfig = { }, experimental: { serverActions: { - allowedOrigins: [process.env.NEXUS_HOST, process.env.MAIN_URL, process.env.API_HOST], + allowedOrigins: [ + 'https://dev.nyx.dreampip.com', + 'https://nyx.dreampip.com', + 'https://beta.dreampip.com', + 'https://www.dreampip.com', + ], }, }, async redirects() { From 7745fba75375eb17066fb2815f6d3e7e8c9236e1 Mon Sep 17 00:00:00 2001 From: Angelo Reale <12191809+angeloreale@users.noreply.github.com> Date: Thu, 8 Aug 2024 22:03:00 +0100 Subject: [PATCH 05/21] [DPTM-2]: dev env (#40) * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment * ar(fix) [DPTM-2]: Ensure autonomous Dev environment --------- Signed-off-by: Angelo Reale <12191809+angeloreale@users.noreply.github.com> --- next.config.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/next.config.js b/next.config.js index 946d4ceb..92e254d8 100644 --- a/next.config.js +++ b/next.config.js @@ -13,12 +13,7 @@ const nextConfig = { }, experimental: { serverActions: { - allowedOrigins: [ - 'https://dev.nyx.dreampip.com', - 'https://nyx.dreampip.com', - 'https://beta.dreampip.com', - 'https://www.dreampip.com', - ], + allowedOrigins: ['dreampip.com', '*.dreampip.com'], }, }, async redirects() { From 6e68b887050b7b6d5b58119355f470fa21fe6fc4 Mon Sep 17 00:00:00 2001 From: Angelo Reale <12191809+angeloreale@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:38:00 +0100 Subject: [PATCH 06/21] ar(feat) [DPCP-37]: Localization Part 1: Add Locale Support (#41) * 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 * ar(feat) [DPCP-37]: Localization * ar(feat) [DPCP-37]: Localization * ar(feat) [DPCP-37]: Localization --- lib/constants/client.ts | 1 + lib/constants/server.ts | 33 +++++++ lib/state/context-global.ts | 1 + lib/state/providers.tsx | 10 +- lib/types/contexts.d.ts | 1 + package-lock.json | 19 ++++ package.json | 1 + .../components/client/blocks/index.ts | 0 .../components/client/blocks/topnav-view.tsx | 0 .../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 | 0 .../client/elements/signup-view.tsx | 0 .../client/elements/usersettings-view.tsx | 0 .../{ => [lang]}/components/client/index.ts | 0 src/app/{ => [lang]}/components/index.ts | 0 src/app/{ => [lang]}/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 .../{ => [lang]}/components/server/index.ts | 0 .../components/server/navbar-controller.tsx | 0 .../components/server/signup-controller.tsx | 0 .../server/usersettings-controller.tsx | 0 src/app/{ => [lang]}/components/toolbar.tsx | 0 src/app/{ => [lang]}/dash/error/page.tsx | 0 .../dash/services/hypnos/[mode]/page.tsx | 0 .../dash/services/rickmorty/[mode]/page.tsx | 0 src/app/{ => [lang]}/dash/signin/page.tsx | 0 src/app/{ => [lang]}/dash/verify/page.tsx | 0 src/app/[lang]/favicon.ico | Bin 0 -> 39535 bytes .../{ => [lang]}/gateway/client/actions.ts | 0 src/app/{ => [lang]}/gateway/index.ts | 0 .../{ => [lang]}/gateway/server/actions.ts | 0 .../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]/global-error.jsx | 19 ++++ src/app/{ => [lang]}/globals.css | 0 src/app/{ => [lang]}/layout.tsx | 7 +- src/app/{ => [lang]}/page.tsx | 0 src/app/{ => [lang]}/styles/page.module.css | 0 src/middleware.ts | 90 +++++++++++++----- tsconfig.json | 34 +++---- 50 files changed, 170 insertions(+), 46 deletions(-) create mode 100644 lib/constants/client.ts create mode 100644 lib/constants/server.ts rename src/app/{ => [lang]}/components/client/blocks/index.ts (100%) rename src/app/{ => [lang]}/components/client/blocks/topnav-view.tsx (100%) rename src/app/{ => [lang]}/components/client/elements/calendar-view.tsx (100%) rename src/app/{ => [lang]}/components/client/elements/index.ts (100%) rename src/app/{ => [lang]}/components/client/elements/link-decorator.tsx (100%) rename src/app/{ => [lang]}/components/client/elements/list-view.tsx (100%) rename src/app/{ => [lang]}/components/client/elements/map-view.tsx (100%) rename src/app/{ => [lang]}/components/client/elements/navbar-view.tsx (100%) rename src/app/{ => [lang]}/components/client/elements/signin-view.tsx (100%) rename src/app/{ => [lang]}/components/client/elements/signup-view.tsx (100%) rename src/app/{ => [lang]}/components/client/elements/usersettings-view.tsx (100%) rename src/app/{ => [lang]}/components/client/index.ts (100%) rename src/app/{ => [lang]}/components/index.ts (100%) rename src/app/{ => [lang]}/components/navbar.tsx (100%) rename src/app/{ => [lang]}/components/server/blocks/hypnos-public-list.tsx (100%) rename src/app/{ => [lang]}/components/server/blocks/index.ts (100%) rename src/app/{ => [lang]}/components/server/blocks/rm-list.tsx (100%) rename src/app/{ => [lang]}/components/server/blocks/topnav.tsx (100%) rename src/app/{ => [lang]}/components/server/index.ts (100%) rename src/app/{ => [lang]}/components/server/navbar-controller.tsx (100%) rename src/app/{ => [lang]}/components/server/signup-controller.tsx (100%) rename src/app/{ => [lang]}/components/server/usersettings-controller.tsx (100%) rename src/app/{ => [lang]}/components/toolbar.tsx (100%) rename src/app/{ => [lang]}/dash/error/page.tsx (100%) rename src/app/{ => [lang]}/dash/services/hypnos/[mode]/page.tsx (100%) rename src/app/{ => [lang]}/dash/services/rickmorty/[mode]/page.tsx (100%) rename src/app/{ => [lang]}/dash/signin/page.tsx (100%) rename src/app/{ => [lang]}/dash/verify/page.tsx (100%) create mode 100644 src/app/[lang]/favicon.ico rename src/app/{ => [lang]}/gateway/client/actions.ts (100%) rename src/app/{ => [lang]}/gateway/index.ts (100%) rename src/app/{ => [lang]}/gateway/server/actions.ts (100%) rename src/app/{ => [lang]}/gateway/server/hypnos/private/actions.ts (100%) rename src/app/{ => [lang]}/gateway/server/hypnos/private/index.ts (100%) rename src/app/{ => [lang]}/gateway/server/hypnos/public/actions.ts (100%) rename src/app/{ => [lang]}/gateway/server/hypnos/public/index.ts (100%) create mode 100644 src/app/[lang]/global-error.jsx rename src/app/{ => [lang]}/globals.css (100%) rename src/app/{ => [lang]}/layout.tsx (64%) rename src/app/{ => [lang]}/page.tsx (100%) rename src/app/{ => [lang]}/styles/page.module.css (100%) diff --git a/lib/constants/client.ts b/lib/constants/client.ts new file mode 100644 index 00000000..15c9c3fa --- /dev/null +++ b/lib/constants/client.ts @@ -0,0 +1 @@ +// @constants/client.ts diff --git a/lib/constants/server.ts b/lib/constants/server.ts new file mode 100644 index 00000000..323e9ccd --- /dev/null +++ b/lib/constants/server.ts @@ -0,0 +1,33 @@ +// @constants/server.ts + +export const LOCALES = [ + 'en', + 'pt-br', + 'it-it', + 'de-de', + 'fr-fr', + 'es-es', + 'ro', + 'pl-pl', + 'cs-cz', + 'sv-se', + 'et-ee', + 'ja-jp', + 'ru-ru', + 'default', +]; + +export const localeMap = { + pt: 'pt-BR', + it: 'it-IT', + es: 'es-ES', + de: 'de-DE', + fr: 'fr-FR', + ro: 'ro', + sv: 'sv-SE', + cs: 'cs-CZ', + pl: 'pl-PL', + et: 'et-EE', + ja: 'ja-JP', + ru: 'ru-RU', +}; diff --git a/lib/state/context-global.ts b/lib/state/context-global.ts index 6f2a580e..88e1e71e 100644 --- a/lib/state/context-global.ts +++ b/lib/state/context-global.ts @@ -5,6 +5,7 @@ import { createContext } from 'react'; export const GlobalContext = createContext({ theme: 'light', + locale: 'en', setter: undefined, history: [], }); diff --git a/lib/state/providers.tsx b/lib/state/providers.tsx index 0f36efa7..2e8e779a 100644 --- a/lib/state/providers.tsx +++ b/lib/state/providers.tsx @@ -3,22 +3,24 @@ 'use client'; import type { IAuthContext, IGlobalContext, IRMContext, IHypnosPublicContext } from '@types'; import { useContext, useState, useEffect, useRef } from 'react'; + +import { Globals } from '@dreampipcom/oneiros'; + import { AuthContext, GlobalContext, RMContext, HypnosPublicContext } from '@state'; import { useLocalStorage } from '@hooks'; -import { Globals } from '@dreampipcom/oneiros'; -export function RootProviders({ children }: { children: React.ReactNode }) { +export function RootProviders({ children, locale }: { children: React.ReactNode; locale: string }) { const authContext = useContext(AuthContext); const globalContext = useContext(GlobalContext); const [authState, setAuthState] = useState({ ...authContext }); - const [globalState, setGlobalState] = useState({ ...globalContext }); + const [globalState, setGlobalState] = useState({ ...globalContext, locale }); const init = useRef(false); const [storedGlobal, setStoredGlobal] = useLocalStorage('globalSettings', { theme: 'dark' }); const handleGlobalSettingUpdate = (next: any) => { setGlobalState(next); - setStoredGlobal({ theme: next?.theme }); + setStoredGlobal({ theme: next?.theme, locale }); }; useEffect(() => { diff --git a/lib/types/contexts.d.ts b/lib/types/contexts.d.ts index 451385f8..7ab2f052 100644 --- a/lib/types/contexts.d.ts +++ b/lib/types/contexts.d.ts @@ -37,6 +37,7 @@ export interface IAuthContext extends History { export interface IGlobalContext extends History { initd?: boolean; theme?: 'light' | 'dark'; + locale?: string; } // to-do: characters type annotations diff --git a/package-lock.json b/package-lock.json index cfe8e230..3a7c060f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@types/react-dom": "18", "@types/uuid": "9.0.8", "@typescript-eslint/eslint-plugin": "6.19.0", + "accept-language": "^3.0.20", "autoprefixer": "^10.4.19", "bson": "^6.8.0", "clsx": "2.1.0", @@ -1801,6 +1802,15 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/accept-language": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/accept-language/-/accept-language-3.0.20.tgz", + "integrity": "sha512-xklPzRma4aoDEPk0ZfMjeuxB2FP4JBYlAR25OFUqCoOYDjYo6wGwAs49SnTN/MoB5VpnNX9tENfZ+vEIFmHQMQ==", + "dev": true, + "dependencies": { + "bcp47": "^1.1.2" + } + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -2158,6 +2168,15 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/bcp47": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz", + "integrity": "sha512-JnkkL4GUpOvvanH9AZPX38CxhiLsXMBicBY2IAtqiVN8YulGDQybUydWA4W6yAMtw6iShtw+8HEF6cfrTHU+UQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", diff --git a/package.json b/package.json index d077401c..749f9e16 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@types/react-dom": "18", "@types/uuid": "9.0.8", "@typescript-eslint/eslint-plugin": "6.19.0", + "accept-language": "^3.0.20", "autoprefixer": "^10.4.19", "bson": "^6.8.0", "clsx": "2.1.0", diff --git a/src/app/components/client/blocks/index.ts b/src/app/[lang]/components/client/blocks/index.ts similarity index 100% rename from src/app/components/client/blocks/index.ts rename to src/app/[lang]/components/client/blocks/index.ts diff --git a/src/app/components/client/blocks/topnav-view.tsx b/src/app/[lang]/components/client/blocks/topnav-view.tsx similarity index 100% rename from src/app/components/client/blocks/topnav-view.tsx rename to src/app/[lang]/components/client/blocks/topnav-view.tsx diff --git a/src/app/components/client/elements/calendar-view.tsx b/src/app/[lang]/components/client/elements/calendar-view.tsx similarity index 100% rename from src/app/components/client/elements/calendar-view.tsx rename to src/app/[lang]/components/client/elements/calendar-view.tsx diff --git a/src/app/components/client/elements/index.ts b/src/app/[lang]/components/client/elements/index.ts similarity index 100% rename from src/app/components/client/elements/index.ts rename to src/app/[lang]/components/client/elements/index.ts diff --git a/src/app/components/client/elements/link-decorator.tsx b/src/app/[lang]/components/client/elements/link-decorator.tsx similarity index 100% rename from src/app/components/client/elements/link-decorator.tsx rename to src/app/[lang]/components/client/elements/link-decorator.tsx diff --git a/src/app/components/client/elements/list-view.tsx b/src/app/[lang]/components/client/elements/list-view.tsx similarity index 100% rename from src/app/components/client/elements/list-view.tsx rename to src/app/[lang]/components/client/elements/list-view.tsx diff --git a/src/app/components/client/elements/map-view.tsx b/src/app/[lang]/components/client/elements/map-view.tsx similarity index 100% rename from src/app/components/client/elements/map-view.tsx rename to src/app/[lang]/components/client/elements/map-view.tsx diff --git a/src/app/components/client/elements/navbar-view.tsx b/src/app/[lang]/components/client/elements/navbar-view.tsx similarity index 100% rename from src/app/components/client/elements/navbar-view.tsx rename to src/app/[lang]/components/client/elements/navbar-view.tsx diff --git a/src/app/components/client/elements/signin-view.tsx b/src/app/[lang]/components/client/elements/signin-view.tsx similarity index 100% rename from src/app/components/client/elements/signin-view.tsx rename to src/app/[lang]/components/client/elements/signin-view.tsx diff --git a/src/app/components/client/elements/signup-view.tsx b/src/app/[lang]/components/client/elements/signup-view.tsx similarity index 100% rename from src/app/components/client/elements/signup-view.tsx rename to src/app/[lang]/components/client/elements/signup-view.tsx diff --git a/src/app/components/client/elements/usersettings-view.tsx b/src/app/[lang]/components/client/elements/usersettings-view.tsx similarity index 100% rename from src/app/components/client/elements/usersettings-view.tsx rename to src/app/[lang]/components/client/elements/usersettings-view.tsx diff --git a/src/app/components/client/index.ts b/src/app/[lang]/components/client/index.ts similarity index 100% rename from src/app/components/client/index.ts rename to src/app/[lang]/components/client/index.ts diff --git a/src/app/components/index.ts b/src/app/[lang]/components/index.ts similarity index 100% rename from src/app/components/index.ts rename to src/app/[lang]/components/index.ts diff --git a/src/app/components/navbar.tsx b/src/app/[lang]/components/navbar.tsx similarity index 100% rename from src/app/components/navbar.tsx rename to src/app/[lang]/components/navbar.tsx diff --git a/src/app/components/server/blocks/hypnos-public-list.tsx b/src/app/[lang]/components/server/blocks/hypnos-public-list.tsx similarity index 100% rename from src/app/components/server/blocks/hypnos-public-list.tsx rename to src/app/[lang]/components/server/blocks/hypnos-public-list.tsx diff --git a/src/app/components/server/blocks/index.ts b/src/app/[lang]/components/server/blocks/index.ts similarity index 100% rename from src/app/components/server/blocks/index.ts rename to src/app/[lang]/components/server/blocks/index.ts diff --git a/src/app/components/server/blocks/rm-list.tsx b/src/app/[lang]/components/server/blocks/rm-list.tsx similarity index 100% rename from src/app/components/server/blocks/rm-list.tsx rename to src/app/[lang]/components/server/blocks/rm-list.tsx diff --git a/src/app/components/server/blocks/topnav.tsx b/src/app/[lang]/components/server/blocks/topnav.tsx similarity index 100% rename from src/app/components/server/blocks/topnav.tsx rename to src/app/[lang]/components/server/blocks/topnav.tsx diff --git a/src/app/components/server/index.ts b/src/app/[lang]/components/server/index.ts similarity index 100% rename from src/app/components/server/index.ts rename to src/app/[lang]/components/server/index.ts diff --git a/src/app/components/server/navbar-controller.tsx b/src/app/[lang]/components/server/navbar-controller.tsx similarity index 100% rename from src/app/components/server/navbar-controller.tsx rename to src/app/[lang]/components/server/navbar-controller.tsx diff --git a/src/app/components/server/signup-controller.tsx b/src/app/[lang]/components/server/signup-controller.tsx similarity index 100% rename from src/app/components/server/signup-controller.tsx rename to src/app/[lang]/components/server/signup-controller.tsx diff --git a/src/app/components/server/usersettings-controller.tsx b/src/app/[lang]/components/server/usersettings-controller.tsx similarity index 100% rename from src/app/components/server/usersettings-controller.tsx rename to src/app/[lang]/components/server/usersettings-controller.tsx diff --git a/src/app/components/toolbar.tsx b/src/app/[lang]/components/toolbar.tsx similarity index 100% rename from src/app/components/toolbar.tsx rename to src/app/[lang]/components/toolbar.tsx diff --git a/src/app/dash/error/page.tsx b/src/app/[lang]/dash/error/page.tsx similarity index 100% rename from src/app/dash/error/page.tsx rename to src/app/[lang]/dash/error/page.tsx diff --git a/src/app/dash/services/hypnos/[mode]/page.tsx b/src/app/[lang]/dash/services/hypnos/[mode]/page.tsx similarity index 100% rename from src/app/dash/services/hypnos/[mode]/page.tsx rename to src/app/[lang]/dash/services/hypnos/[mode]/page.tsx diff --git a/src/app/dash/services/rickmorty/[mode]/page.tsx b/src/app/[lang]/dash/services/rickmorty/[mode]/page.tsx similarity index 100% rename from src/app/dash/services/rickmorty/[mode]/page.tsx rename to src/app/[lang]/dash/services/rickmorty/[mode]/page.tsx diff --git a/src/app/dash/signin/page.tsx b/src/app/[lang]/dash/signin/page.tsx similarity index 100% rename from src/app/dash/signin/page.tsx rename to src/app/[lang]/dash/signin/page.tsx diff --git a/src/app/dash/verify/page.tsx b/src/app/[lang]/dash/verify/page.tsx similarity index 100% rename from src/app/dash/verify/page.tsx rename to src/app/[lang]/dash/verify/page.tsx diff --git a/src/app/[lang]/favicon.ico b/src/app/[lang]/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4570eb8d9269ad58b17fecbec6d630cded56f507 GIT binary patch literal 39535 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 literal 0 HcmV?d00001 diff --git a/src/app/gateway/client/actions.ts b/src/app/[lang]/gateway/client/actions.ts similarity index 100% rename from src/app/gateway/client/actions.ts rename to src/app/[lang]/gateway/client/actions.ts diff --git a/src/app/gateway/index.ts b/src/app/[lang]/gateway/index.ts similarity index 100% rename from src/app/gateway/index.ts rename to src/app/[lang]/gateway/index.ts diff --git a/src/app/gateway/server/actions.ts b/src/app/[lang]/gateway/server/actions.ts similarity index 100% rename from src/app/gateway/server/actions.ts rename to src/app/[lang]/gateway/server/actions.ts diff --git a/src/app/gateway/server/hypnos/private/actions.ts b/src/app/[lang]/gateway/server/hypnos/private/actions.ts similarity index 100% rename from src/app/gateway/server/hypnos/private/actions.ts rename to src/app/[lang]/gateway/server/hypnos/private/actions.ts diff --git a/src/app/gateway/server/hypnos/private/index.ts b/src/app/[lang]/gateway/server/hypnos/private/index.ts similarity index 100% rename from src/app/gateway/server/hypnos/private/index.ts rename to src/app/[lang]/gateway/server/hypnos/private/index.ts diff --git a/src/app/gateway/server/hypnos/public/actions.ts b/src/app/[lang]/gateway/server/hypnos/public/actions.ts similarity index 100% rename from src/app/gateway/server/hypnos/public/actions.ts rename to src/app/[lang]/gateway/server/hypnos/public/actions.ts diff --git a/src/app/gateway/server/hypnos/public/index.ts b/src/app/[lang]/gateway/server/hypnos/public/index.ts similarity index 100% rename from src/app/gateway/server/hypnos/public/index.ts rename to src/app/[lang]/gateway/server/hypnos/public/index.ts diff --git a/src/app/[lang]/global-error.jsx b/src/app/[lang]/global-error.jsx new file mode 100644 index 00000000..fc093df1 --- /dev/null +++ b/src/app/[lang]/global-error.jsx @@ -0,0 +1,19 @@ +'use client'; + +import * as Sentry from '@sentry/nextjs'; +import Error from 'next/error'; +import { useEffect } from 'react'; + +export default function GlobalError({ error }) { + useEffect(() => { + Sentry.captureException(error); + }, [error]); + + return ( + + + + + + ); +} diff --git a/src/app/globals.css b/src/app/[lang]/globals.css similarity index 100% rename from src/app/globals.css rename to src/app/[lang]/globals.css diff --git a/src/app/layout.tsx b/src/app/[lang]/layout.tsx similarity index 64% rename from src/app/layout.tsx rename to src/app/[lang]/layout.tsx index e80063ac..03686522 100644 --- a/src/app/layout.tsx +++ b/src/app/[lang]/layout.tsx @@ -8,11 +8,14 @@ export const metadata: Metadata = { description: process.env.PATTERNS_DESCRIPTION, }; -export default function RootLayout({ children }: { children: React.ReactNode }) { +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/page.tsx b/src/app/[lang]/page.tsx similarity index 100% rename from src/app/page.tsx rename to src/app/[lang]/page.tsx diff --git a/src/app/styles/page.module.css b/src/app/[lang]/styles/page.module.css similarity index 100% rename from src/app/styles/page.module.css rename to src/app/[lang]/styles/page.module.css diff --git a/src/middleware.ts b/src/middleware.ts index 532acf15..e3b42ee7 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,9 +1,30 @@ // middleware.ts 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 config = { - matcher: ['/api/:path*'], + matcher: ['/api/:path*', '/default/dash/:path*'], }; const allowedOrigins = { @@ -23,32 +44,53 @@ const headers: Record = { }; export function middleware(request: NextRequest) { - 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'); + // 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'; + } - Object.keys(headers).forEach((key: string) => { - response.headers.set(key, headers[key]); - }); + const response = NextResponse.next(); + const pkce = request.cookies.get('next-auth.pkce.code_verifier'); - if (pkce?.value) { - response.cookies.set('next-auth.pkce.code_verifier', pkce.value, { - httpOnly: true, - sameSite: 'none', - path: '/', - secure: true, + Object.keys(headers).forEach((key: string) => { + response.headers.set(key, headers[key]); }); - console.log({ pkce, response, to: request.nextUrl.pathname }); + + 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 NextResponse.rewrite( - new URL( - `${process.env.REMOTE_DEV ? process.env.API_HOST_DEV : process.env.API_HOST}${request.nextUrl.pathname}${request.nextUrl.search}`, - ), - 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); + } } diff --git a/tsconfig.json b/tsconfig.json index 39e999ed..8bac0deb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,16 +19,18 @@ }, ], "paths": { - "@styles": ["./src/app/styles"], - "@styles/*": ["./src/app/styles/*"], - "@atoms": ["./src/app/components/system/atoms"], - "@atoms/*": ["./src/app/components/system/atoms/*"], - "@blocks/client": ["./src/app/components/client/blocks"], - "@blocks/client/*": ["./src/app/components/client/blocks/*"], - "@blocks/server": ["./src/app/components/server/blocks"], - "@blocks/server/*": ["./src/app/components/server/blocks/*"], - "@elements/client": ["./src/app/components/client/elements"], - "@elements/client/*": ["./src/app/components/client/elements/*"], + "@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/*"], + "@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/*"], "@model": ["./lib/model"], "@model/*": ["./lib/model/*"], "@view": ["./lib/view"], @@ -40,20 +42,20 @@ "@types": ["./lib/types"], "@types/*": ["./lib/types/*"], "@auth": ["./lib/auth"], - "@auth/adapter": ["./src/app/api/auth/[...nextauth]"], - "@auth/adapter/*": ["./src/app/api/auth/[...nextauth]/*"], + "@auth/adapter": ["./src/app/[lang]/api/auth/[...nextauth]"], + "@auth/adapter/*": ["./src/app/[lang]/api/auth/[...nextauth]/*"], "@state/*": ["./lib/state/*"], "@state": ["./lib/state"], "@hooks/*": ["./lib/hooks/*"], "@hooks": ["./lib/hooks"], "@actions/*": ["./lib/actions/*"], "@actions": ["./lib/actions"], - "@gateway/*": ["./src/app/gateway/*"], - "@gateway": ["./src/app/gateway"], + "@gateway/*": ["./src/app/[lang]/gateway/*"], + "@gateway": ["./src/app/[lang]/gateway"], "@controller/*": ["./lib/model/interfaces/*"], "@controller": ["./lib/model/interfaces"], - "@components/*": ["./src/app/components/*"], - "@components": ["./src/app/components"], + "@components/*": ["./src/app/[lang]/components/*"], + "@components": ["./src/app/[lang]/components"], }, }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 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 07/21] 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"], From 5ec790ab98fbb2c84868cc43d028d8adbb16f2c4 Mon Sep 17 00:00:00 2001 From: Angelo Reale <12191809+angeloreale@users.noreply.github.com> Date: Wed, 21 Aug 2024 21:25:25 +0100 Subject: [PATCH 08/21] [DPCP-37] [DPCP-91] [DPCP-92] Nits (#43) * 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 * 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 * ar(feat) [DPCP-37]: Localization --- lib/dictionaries/global/cs-cz.json | 8 +++++- lib/dictionaries/global/de-de.json | 8 +++++- lib/dictionaries/global/default.json | 8 +++++- lib/dictionaries/global/en.json | 8 +++++- lib/dictionaries/global/es-es.json | 8 +++++- lib/dictionaries/global/et-ee.json | 8 +++++- lib/dictionaries/global/fr-fr.json | 8 +++++- lib/dictionaries/global/it-it.json | 8 +++++- lib/dictionaries/global/ja-jp.json | 8 +++++- lib/dictionaries/global/pl-pl.json | 8 +++++- lib/dictionaries/global/pt-br.json | 8 +++++- lib/dictionaries/global/ro.json | 8 +++++- lib/dictionaries/global/ru-ru.json | 8 +++++- lib/dictionaries/global/sv-se.json | 8 +++++- next.config.js | 25 ++++++++++++++----- .../client/elements/signin-view.tsx | 4 ++- .../client/elements/signup-view.tsx | 4 +-- src/app/[locale]/dash/services/page.tsx | 13 ++++++++++ src/app/[locale]/layout.tsx | 17 ++++++++----- src/app/layout.tsx | 14 +++++++++++ src/middleware.ts | 7 ++++++ src/middlewares/authMiddleware.ts | 2 +- src/middlewares/i18nDetectMiddleware.ts | 2 +- 23 files changed, 169 insertions(+), 31 deletions(-) create mode 100644 src/app/[locale]/dash/services/page.tsx create mode 100644 src/app/layout.tsx diff --git a/lib/dictionaries/global/cs-cz.json b/lib/dictionaries/global/cs-cz.json index f4595c86..d5ca34e5 100644 --- a/lib/dictionaries/global/cs-cz.json +++ b/lib/dictionaries/global/cs-cz.json @@ -1,13 +1,19 @@ { + "Dashboard": { + "title": "DreamPip — Nástěnka", + "description": "Vaše cesta začíná zde. DreamPip je fintech pro soucit." + }, "NavBar": { "welcome": "Vítejte" }, "SignIn": { + "welcome": "Vítejte", "continue": "Pokračovat", "continue with": "Pokračovat s", "sign in": "Přihlásit", "sign out": "Odhlásit", - "your email": "Váš e-mail" + "your email": "Váš e-mail", + "i hope you make yourself at home": "Doufám, že se uvidíš jako doma" }, "CardGrid": { "staff picks": "Výběr zaměstnanců" diff --git a/lib/dictionaries/global/de-de.json b/lib/dictionaries/global/de-de.json index 7a5729b5..37748c62 100644 --- a/lib/dictionaries/global/de-de.json +++ b/lib/dictionaries/global/de-de.json @@ -1,13 +1,19 @@ { + "Dashboard": { + "title": "DreamPip — Dashboard", + "description": "Ihre Reise beginnt hier. DreamPip ist Finanztechnologie für Mitgefühl." + }, "NavBar": { "welcome": "Willkommen" }, "SignIn": { + "welcome": "Willkommen", "continue": "Weiter", "continue with": "Weiter mit", "sign in": "Anmelden", "sign out": "Abmelden", - "your email": "Deine E-Mail" + "your email": "Deine E-Mail", + "i hope you make yourself at home": "Ich hoffe, du fühlst dich wie zu Hause" }, "CardGrid": { "staff picks": "Auswahl des Personals" diff --git a/lib/dictionaries/global/default.json b/lib/dictionaries/global/default.json index bc506591..9238009a 100644 --- a/lib/dictionaries/global/default.json +++ b/lib/dictionaries/global/default.json @@ -1,13 +1,19 @@ { + "Dashboard": { + "title": "DreamPip — Dashboard", + "description": "Your journey starts here. DreamPip is fintech for compassion." + }, "NavBar": { "welcome": "Welcome" }, "SignIn": { + "welcome": "Welcome", "continue": "Continue", "continue with": "Continue with", "sign in": "Sign in", "sign out": "Sign out", - "your email": "Your email" + "your email": "Your email", + "i hope you make yourself at home": "I hope you make yourself at home" }, "CardGrid": { "staff picks": "Staff Picks" diff --git a/lib/dictionaries/global/en.json b/lib/dictionaries/global/en.json index ae88cfc3..e8038033 100644 --- a/lib/dictionaries/global/en.json +++ b/lib/dictionaries/global/en.json @@ -1,12 +1,18 @@ { + "Dashboard": { + "title": "DreamPip — Dashboard", + "description": "Your journey starts here. DreamPip is fintech for compassion." + }, "NavBar": { "welcome": "Welcome" }, "SignIn": { + "welcome": "Welcome", "continue": "Continue", "continue with": "Continue with", "sign in": "Sign in", - "sign out": "Sign out" + "sign out": "Sign out", + "i hope you make yourself at home": "I hope you make yourself at home" }, "CardGrid": { "staff picks": "Staff Picks" diff --git a/lib/dictionaries/global/es-es.json b/lib/dictionaries/global/es-es.json index 1a804560..2674d280 100644 --- a/lib/dictionaries/global/es-es.json +++ b/lib/dictionaries/global/es-es.json @@ -1,13 +1,19 @@ { + "Dashboard": { + "title": "DreamPip — Panel de control", + "description": "Tu viaje comienza aquí. DreamPip es fintech para la compasión." + }, "NavBar": { "welcome": "Bienvenido" }, "SignIn": { + "welcome": "Bienvenido", "continue": "Continuar", "continue with": "Continuar con", "sign in": "Iniciar sesión", "sign out": "Cerrar sesión", - "your email": "Tu correo electrónico" + "your email": "Tu correo electrónico", + "i hope you make yourself at home": "Espero que te sientas como en casa" }, "CardGrid": { "staff picks": "Selecciones del personal" diff --git a/lib/dictionaries/global/et-ee.json b/lib/dictionaries/global/et-ee.json index 1896dd6c..ad95a264 100644 --- a/lib/dictionaries/global/et-ee.json +++ b/lib/dictionaries/global/et-ee.json @@ -1,13 +1,19 @@ { + "Dashboard": { + "title": "DreamPip — Juhtpaneel", + "description": "Teie teekond algab siin. DreamPip on finantstehnoloogia kaastunde jaoks." + }, "NavBar": { "welcome": "Tere tulemast" }, "SignIn": { + "welcome": "Tere tulemast", "continue": "Jätka", "continue with": "Jätka", "sign in": "Logi sisse", "sign out": "Logi välja", - "your email": "Sinu e-post" + "your email": "Sinu e-post", + "i hope you make yourself at home": "Loodan, et tunned end koduselt" }, "CardGrid": { "staff picks": "Töötajate valikud" diff --git a/lib/dictionaries/global/fr-fr.json b/lib/dictionaries/global/fr-fr.json index 21db518c..f8fc85cf 100644 --- a/lib/dictionaries/global/fr-fr.json +++ b/lib/dictionaries/global/fr-fr.json @@ -1,13 +1,19 @@ { + "Dashboard": { + "title": "DreamPip — Tableau de bord", + "description": "Votre voyage commence ici. DreamPip est de la fintech pour la compassion." + }, "NavBar": { "welcome": "Bienvenue" }, "SignIn": { + "welcome": "Bienvenue", "continue": "Continuer", "continue with": "Continuer avec", "sign in": "Se connecter", "sign out": "Se déconnecter", - "your email": "Votre adresse e-mail" + "your email": "Votre adresse e-mail", + "i hope you make yourself at home": "J’espère que vous vous sentirez comme chez vous" }, "CardGrid": { "staff picks": "Choix du personnel" diff --git a/lib/dictionaries/global/it-it.json b/lib/dictionaries/global/it-it.json index 7cd1f4a4..4fbbde0e 100644 --- a/lib/dictionaries/global/it-it.json +++ b/lib/dictionaries/global/it-it.json @@ -1,13 +1,19 @@ { + "Dashboard": { + "title": "DreamPip — Pannello di controllo", + "description": "Il tuo viaggio inizia qui. DreamPip è un'azienda fintech per la compassione." + }, "NavBar": { "welcome": "Benvenuto" }, "SignIn": { + "welcome": "Benvenuto", "continue": "Prosegue", "continue with": "Prosegue con", "sign in": "Accedi", "sign out": "Scollega", - "your email": "La sua mail" + "your email": "La sua mail", + "i hope you make yourself at home": "Spero che tu ti senta come a casa" }, "CardGrid": { "staff picks": "Preferiti dello Staff" diff --git a/lib/dictionaries/global/ja-jp.json b/lib/dictionaries/global/ja-jp.json index 2d3c71e9..1cba18b9 100644 --- a/lib/dictionaries/global/ja-jp.json +++ b/lib/dictionaries/global/ja-jp.json @@ -1,13 +1,19 @@ { + "Dashboard": { + "title": "DreamPip — ダッシュボード", + "description": "あなたの旅はここから始まります。DreamPipは共感のためのフィンテックです。" + }, "NavBar": { "welcome": "ようこそ" }, "SignIn": { + "welcome": "ようこそ", "continue": "続行", "continue with": "続行する", "sign in": "サインイン", "sign out": "ログアウト", - "your email": "あなたのメール" + "your email": "あなたのメール", + "i hope you make yourself at home": "くつろいでくださいね" }, "CardGrid": { "staff picks": "スタッフおすすめ" diff --git a/lib/dictionaries/global/pl-pl.json b/lib/dictionaries/global/pl-pl.json index 095d9adb..4472faa3 100644 --- a/lib/dictionaries/global/pl-pl.json +++ b/lib/dictionaries/global/pl-pl.json @@ -1,13 +1,19 @@ { + "Dashboard": { + "title": "DreamPip — Panel", + "description": "Twoja podróż zaczyna się tutaj. DreamPip to finanse technologiczne dla współczucia." + }, "NavBar": { "welcome": "Witaj" }, "SignIn": { + "welcome": "Witaj", "continue": "Kontynuuj", "continue with": "Kontynuuj z", "sign in": "Zaloguj się", "sign out": "Wyloguj się", - "your email": "Twój adres email" + "your email": "Twój adres email", + "i hope you make yourself at home": "Mam nadzieję, że będziesz się czuł jak u siebie w domu" }, "CardGrid": { "staff picks": "Wybór pracowników" diff --git a/lib/dictionaries/global/pt-br.json b/lib/dictionaries/global/pt-br.json index 8708a40f..f0dfdc9d 100644 --- a/lib/dictionaries/global/pt-br.json +++ b/lib/dictionaries/global/pt-br.json @@ -1,11 +1,17 @@ { + "Dashboard": { + "title": "DreamPip — Painel", + "description": "Sua jornada começa aqui. DreamPip é uma fintech para a compaixão." + }, "NavBar": { "welcome": "Bem-vindo" }, "SignIn": { + "welcome": "Bem-vindo", "continue": "Continuar", "continue with": "Continuar com", "sign in": "Entrar", "sign out": "Sair", - "your email": "Seu email" + "your email": "Seu email", + "i hope you make yourself at home": "Espero que você se sinta em casa" }, "CardGrid": { "staff picks": "Escolhas da Equipe" } } diff --git a/lib/dictionaries/global/ro.json b/lib/dictionaries/global/ro.json index 81defd72..b44b6d9d 100644 --- a/lib/dictionaries/global/ro.json +++ b/lib/dictionaries/global/ro.json @@ -1,13 +1,19 @@ { + "Dashboard": { + "title": "DreamPip — Tablou de bord", + "description": "Călătoria ta începe aici. DreamPip este fintech pentru compasiune." + }, "NavBar": { "welcome": "Bine ai venit" }, "SignIn": { + "welcome": "Bine ai venit", "continue": "Continuați", "continue with": "Continuați cu", "sign in": "Conectați-vă", "sign out": "Deconectați-vă", - "your email": "Adresa ta de email" + "your email": "Adresa ta de email", + "i hope you make yourself at home": "Sper că te simți ca acasă" }, "CardGrid": { "staff picks": "Alegerile personalului" diff --git a/lib/dictionaries/global/ru-ru.json b/lib/dictionaries/global/ru-ru.json index 7f392d86..0ba10ea4 100644 --- a/lib/dictionaries/global/ru-ru.json +++ b/lib/dictionaries/global/ru-ru.json @@ -1,13 +1,19 @@ { + "Dashboard": { + "title": "DreamPip — Панель", + "description": "Ваше путешествие начинается здесь. DreamPip - финтех для сострадания." + }, "NavBar": { "welcome": "Добро пожаловать" }, "SignIn": { + "welcome": "Добро пожаловать", "continue": "Продолжить", "continue with": "Продолжить с", "sign in": "Войти", "sign out": "Выйти", - "your email": "Ваш электронный адрес" + "your email": "Ваш электронный адрес", + "i hope you make yourself at home": "Я надеюсь, что вы почувствуете себя как дома" }, "CardGrid": { "staff picks": "Выбор персонала" diff --git a/lib/dictionaries/global/sv-se.json b/lib/dictionaries/global/sv-se.json index 112636b6..d3889943 100644 --- a/lib/dictionaries/global/sv-se.json +++ b/lib/dictionaries/global/sv-se.json @@ -1,13 +1,19 @@ { + "Dashboard": { + "title": "DreamPip — Instrumentpanel", + "description": "Din resa börjar här. DreamPip är fintech för medkänsla." + }, "NavBar": { "welcome": "Välkommen" }, "SignIn": { + "welcome": "Välkommen", "continue": "Fortsätt", "continue with": "Fortsätt med", "sign in": "Logga in", "sign out": "Logga ut", - "your email": "Din e-post" + "your email": "Din e-post", + "i hope you make yourself at home": "Jag hoppas att du känner dig som hemma" }, "CardGrid": { "staff picks": "Personals val" diff --git a/next.config.js b/next.config.js index 306993fb..e4d39424 100644 --- a/next.config.js +++ b/next.config.js @@ -4,7 +4,10 @@ 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', + assetPrefix: + (process.env.VERCEL_ENV && process.env.VERCEL_ENV !== 'production' + ? `https://${process.env.VERCEL_URL}` + : process.env.NEXUS_HOST) || 'https://nyx.dreampip.com', transpilePackages: ['next-auth'], images: { remotePatterns: [ @@ -31,11 +34,21 @@ const nextConfig = { destination: '/dash/services/rickmorty/list', permanent: false, }, - // { - // source: '/signin', - // destination: '/dash/signin', - // permanent: false, - // }, + { + source: '/', + destination: '/default/dash/signin', + permanent: false, + }, + { + source: '/dash', + destination: '/default/dash/signin', + permanent: false, + }, + { + source: '/dash/:path*', + destination: '/default/dash/:path*', + permanent: false, + }, ]; }, }; diff --git a/src/app/[locale]/components/client/elements/signin-view.tsx b/src/app/[locale]/components/client/elements/signin-view.tsx index 5f0a1462..88625979 100644 --- a/src/app/[locale]/components/client/elements/signin-view.tsx +++ b/src/app/[locale]/components/client/elements/signin-view.tsx @@ -48,5 +48,7 @@ export const VSignIn = ({ user }: VSignInProps) => { ); - return navigate('/api/v1/auth/signin')}>{t('sign in')}; + return { + navigate('/dash/signin'); + }}>{t('sign in')}; }; diff --git a/src/app/[locale]/components/client/elements/signup-view.tsx b/src/app/[locale]/components/client/elements/signup-view.tsx index e670a550..4648e0d8 100644 --- a/src/app/[locale]/components/client/elements/signup-view.tsx +++ b/src/app/[locale]/components/client/elements/signup-view.tsx @@ -94,8 +94,8 @@ export const VSignUp = ({ providers, user }: VSignUpProps) => { if (user || authd) { return
- Welcome, {coercedName}. I hope you make yourself at home. - + {t('welcome')}, {coercedName}. {t('i hope you make yourself at home')}. +
} diff --git a/src/app/[locale]/dash/services/page.tsx b/src/app/[locale]/dash/services/page.tsx new file mode 100644 index 00000000..47679737 --- /dev/null +++ b/src/app/[locale]/dash/services/page.tsx @@ -0,0 +1,13 @@ +// page.tsx +import { DPPublicListings } from '@blocks/server'; + +export default function Home({ params }: any) { + const mode = params?.mode || 'list'; + return ( +
+
+ +
+
+ ); +} diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx index 70047dab..9d313e5c 100644 --- a/src/app/[locale]/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -2,13 +2,18 @@ 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 { getLocale, getMessages, getTranslations } from 'next-intl/server'; import './globals.css'; -export const metadata: Metadata = { - title: process.env.PATTERNS_TITLE, - description: process.env.PATTERNS_DESCRIPTION, -}; +export async function generateMetadata(): Promise { + // fetch data + const t = await getTranslations('Dashboard'); + + return { + title: t('title'), + description: t('description'), + }; +} export default async function RootLayout({ children, params }: { children: React.ReactNode; params: any }) { const { locale: orig } = params; @@ -18,7 +23,7 @@ export default async function RootLayout({ children, params }: { children: React const libLocale = await getLocale(); return ( - + diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 00000000..0f5bdae7 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,14 @@ +import type { Metadata } from 'next'; + +export const metadata: Metadata = { + title: 'DreamPip — Dashboard', + description: 'Your journey starts here. DreamPip is fintech for compassion.', +}; + +export default async function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/src/middleware.ts b/src/middleware.ts index 924edb08..21ebd02f 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -10,7 +10,14 @@ export const config = { 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 blocklist = ['_next']; + export default async function middleware(request: NextRequest) { + const url = request?.nextUrl?.pathname; + if (blocklist.some((pattern) => url?.includes(pattern))) { + console.log('--- PATTERN BOCKED: ---', url); + return NextResponse.next(); + } // if a response is returned, return it otherwise call `next()` for (const fn of middlewares) { const response = await fn(request); diff --git a/src/middlewares/authMiddleware.ts b/src/middlewares/authMiddleware.ts index 1746ff23..dd5699a1 100644 --- a/src/middlewares/authMiddleware.ts +++ b/src/middlewares/authMiddleware.ts @@ -19,7 +19,7 @@ const headers: Record = { }; export const authMiddleware = async (request: NextRequest) => { - console.log('--- ran: API MIDDLEWARE ---'); + console.log('--- ran: API MIDDLEWARE ---', request.nextUrl.href); // API COOKIES if (request.nextUrl.pathname.startsWith('/api')) { const origin = request.headers.get('x-forwarded-host') || ''; diff --git a/src/middlewares/i18nDetectMiddleware.ts b/src/middlewares/i18nDetectMiddleware.ts index e5fd1cc0..06a741e5 100644 --- a/src/middlewares/i18nDetectMiddleware.ts +++ b/src/middlewares/i18nDetectMiddleware.ts @@ -25,7 +25,7 @@ const supportedLocales = [ acceptLanguage.languages(supportedLocales); export const i18nDetectMiddleware = async (request: NextRequest) => { - console.log('--- ran: I18N DETECT MIDDLEWARE ---'); + console.log('--- ran: I18N DETECT MIDDLEWARE ---', request.nextUrl.href); // LOCALIZATION if ( !/\.(.*)$/.test(request.nextUrl.pathname) && From 707cecfa66b0578e573a5fa2d44485a28b973b9d Mon Sep 17 00:00:00 2001 From: Angelo Reale <12191809+angeloreale@users.noreply.github.com> Date: Wed, 21 Aug 2024 21:39:49 +0100 Subject: [PATCH 09/21] ar(fix): services --- next.config.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/next.config.js b/next.config.js index e4d39424..11b2ac3f 100644 --- a/next.config.js +++ b/next.config.js @@ -25,13 +25,13 @@ const nextConfig = { async redirects() { return [ { - source: '/dash/services/hypnos', - destination: '/dash/services/hypnos/list', + source: '/dash/services/:service', + destination: '/dash/services/:service/list', permanent: false, }, { - source: '/dash/services/rickmorty', - destination: '/dash/services/rickmorty/list', + source: '/:locale/dash/services/:service', + destination: '/:locale/dash/services/:service/list', permanent: false, }, { From 138e59de58c13db207a78166e76e6782be500cb5 Mon Sep 17 00:00:00 2001 From: Angelo Reale <12191809+angeloreale@users.noreply.github.com> Date: Fri, 23 Aug 2024 23:09:47 +0100 Subject: [PATCH 10/21] ar(fix) apple pkce --- src/middlewares/authMiddleware.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/middlewares/authMiddleware.ts b/src/middlewares/authMiddleware.ts index dd5699a1..7b2356cb 100644 --- a/src/middlewares/authMiddleware.ts +++ b/src/middlewares/authMiddleware.ts @@ -28,14 +28,14 @@ export const authMiddleware = async (request: NextRequest) => { } const response = NextResponse.next(); - const pkce = request.cookies.get('next-auth.pkce.code_verifier'); + const pkce = request.cookies.get('authjs.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, { + response.cookies.set('authjs.pkce.code_verifier', pkce.value, { httpOnly: true, sameSite: 'none', path: '/', From 8aeced17863a1e5604725c9311ee238cd5834629 Mon Sep 17 00:00:00 2001 From: Angelo Reale <12191809+angeloreale@users.noreply.github.com> Date: Fri, 23 Aug 2024 23:32:51 +0100 Subject: [PATCH 11/21] ar(fix) apple pkce --- src/app/[locale]/components/client/elements/signup-view.tsx | 2 +- src/middlewares/authMiddleware.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/[locale]/components/client/elements/signup-view.tsx b/src/app/[locale]/components/client/elements/signup-view.tsx index 4648e0d8..51ebff0d 100644 --- a/src/app/[locale]/components/client/elements/signup-view.tsx +++ b/src/app/[locale]/components/client/elements/signup-view.tsx @@ -132,7 +132,7 @@ export const VSignUp = ({ providers, user }: VSignUpProps) => { )}