diff --git a/apps/login/i18nConfig.js b/apps/login/i18nConfig.js deleted file mode 100644 index d17db506c9a..00000000000 --- a/apps/login/i18nConfig.js +++ /dev/null @@ -1,7 +0,0 @@ -const i18nConfig = { - locales: ["en", "de", "it"], - defaultLocale: "en", - prefixDefault: false, -}; - -export default i18nConfig; diff --git a/apps/login/locales/de.json b/apps/login/locales/de.json new file mode 100644 index 00000000000..50bbb084d72 --- /dev/null +++ b/apps/login/locales/de.json @@ -0,0 +1,6 @@ +{ + "loginname": { + "title": "Willkommen zurück!", + "description": "Geben Sie Ihre Anmeldedaten ein." + } +} diff --git a/apps/login/locales/de/loginname.json b/apps/login/locales/de/loginname.json deleted file mode 100644 index 939b98fd9dd..00000000000 --- a/apps/login/locales/de/loginname.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Willkommen zurück!", - "description": "Geben Sie Ihre Anmeldedaten ein." -} diff --git a/apps/login/locales/en.json b/apps/login/locales/en.json new file mode 100644 index 00000000000..a783dc0154e --- /dev/null +++ b/apps/login/locales/en.json @@ -0,0 +1,6 @@ +{ + "loginname": { + "title": "Welcome back!", + "description": "Enter your login data." + } +} diff --git a/apps/login/locales/en/loginname.json b/apps/login/locales/en/loginname.json deleted file mode 100644 index 6a7d1d3b203..00000000000 --- a/apps/login/locales/en/loginname.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Welcome back!", - "description": "Enter your login data." -} diff --git a/apps/login/locales/it/loginname.json b/apps/login/locales/it/loginname.json deleted file mode 100644 index 6a7d1d3b203..00000000000 --- a/apps/login/locales/it/loginname.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Welcome back!", - "description": "Enter your login data." -} diff --git a/apps/login/next.config.mjs b/apps/login/next.config.mjs index 94df2408a08..e2906b1fa89 100755 --- a/apps/login/next.config.mjs +++ b/apps/login/next.config.mjs @@ -1,3 +1,7 @@ +import createNextIntlPlugin from "next-intl/plugin"; + +const withNextIntl = createNextIntlPlugin(); + /** @type {import('next').NextConfig} */ const secureHeaders = [ @@ -59,4 +63,4 @@ const nextConfig = { }, }; -export default nextConfig; +export default withNextIntl(nextConfig); diff --git a/apps/login/package.json b/apps/login/package.json index 5fcfb4c0ca7..d1cf31d7d18 100644 --- a/apps/login/package.json +++ b/apps/login/package.json @@ -42,18 +42,15 @@ "@zitadel/proto": "workspace:*", "clsx": "1.2.1", "copy-to-clipboard": "^3.3.3", - "i18next": "^23.15.2", - "i18next-resources-to-backend": "^1.2.1", "moment": "^2.29.4", "next": "14.2.14", - "next-i18n-router": "^5.5.1", + "next-intl": "^3.20.0", "next-themes": "^0.2.1", "nice-grpc": "2.0.1", "qrcode.react": "^3.1.0", "react": "^18.3.1", "react-dom": "18.3.1", "react-hook-form": "7.39.5", - "react-i18next": "^15.0.2", "swr": "^2.2.0", "tinycolor2": "1.4.2" }, diff --git a/apps/login/src/app/[locale]/accounts/page.tsx b/apps/login/src/app/accounts/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/accounts/page.tsx rename to apps/login/src/app/accounts/page.tsx diff --git a/apps/login/src/app/[locale]/error.tsx b/apps/login/src/app/error.tsx similarity index 100% rename from apps/login/src/app/[locale]/error.tsx rename to apps/login/src/app/error.tsx diff --git a/apps/login/src/app/i18n.js b/apps/login/src/app/i18n.js deleted file mode 100644 index c38751e5149..00000000000 --- a/apps/login/src/app/i18n.js +++ /dev/null @@ -1,41 +0,0 @@ -import i18nConfig from "i18nConfig"; -import { createInstance } from "i18next"; -import resourcesToBackend from "i18next-resources-to-backend"; -import { initReactI18next } from "react-i18next/initReactI18next"; - -export default async function initTranslations( - locale, - namespaces, - i18nInstance, - resources, -) { - i18nInstance = i18nInstance || createInstance(); - - i18nInstance.use(initReactI18next); - - if (!resources) { - i18nInstance.use( - resourcesToBackend( - (language, namespace) => - import(`/locales/${language}/${namespace}.json`), - ), - ); - } - - await i18nInstance.init({ - lng: locale, - resources, - fallbackLng: i18nConfig.defaultLocale, - supportedLngs: i18nConfig.locales, - defaultNS: namespaces[0], - fallbackNS: namespaces[0], - ns: namespaces, - preload: resources ? [] : i18nConfig.locales, - }); - - return { - i18n: i18nInstance, - resources: i18nInstance.services.resourceStore.data, - t: i18nInstance.t, - }; -} diff --git a/apps/login/src/app/[locale]/idp/[provider]/failure/page.tsx b/apps/login/src/app/idp/[provider]/failure/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/idp/[provider]/failure/page.tsx rename to apps/login/src/app/idp/[provider]/failure/page.tsx diff --git a/apps/login/src/app/[locale]/idp/[provider]/success/page.tsx b/apps/login/src/app/idp/[provider]/success/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/idp/[provider]/success/page.tsx rename to apps/login/src/app/idp/[provider]/success/page.tsx diff --git a/apps/login/src/app/[locale]/idp/page.tsx b/apps/login/src/app/idp/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/idp/page.tsx rename to apps/login/src/app/idp/page.tsx diff --git a/apps/login/src/app/[locale]/layout.tsx b/apps/login/src/app/layout.tsx similarity index 74% rename from apps/login/src/app/[locale]/layout.tsx rename to apps/login/src/app/layout.tsx index 54aaf7bd766..bf5e7a052bd 100644 --- a/apps/login/src/app/[locale]/layout.tsx +++ b/apps/login/src/app/layout.tsx @@ -2,16 +2,13 @@ import "@/styles/globals.scss"; import { AddressBar } from "@/components/address-bar"; import { GlobalNav } from "@/components/global-nav"; -import { LanguageSwitcher } from "@/components/language-switcher"; import { Theme } from "@/components/theme"; import { ThemeProvider } from "@/components/theme-provider"; -import { TranslationsProvider } from "@/components/translations-provider"; import { Analytics } from "@vercel/analytics/react"; -import i18nConfig from "i18nConfig"; -import { dir } from "i18next"; +import { NextIntlClientProvider } from "next-intl"; +import { getLocale, getMessages } from "next-intl/server"; import { Lato } from "next/font/google"; import { ReactNode } from "react"; -import initTranslations from "../i18n"; const lato = Lato({ weight: ["400", "700", "900"], @@ -20,19 +17,13 @@ const lato = Lato({ export const revalidate = 60; // revalidate every minute -export function generateStaticParams() { - return i18nConfig.locales.map((locale) => ({ locale })); -} - export default async function RootLayout({ children, - params: { locale, hl }, }: { children: ReactNode; - params: { locale: string; hl: string }; }) { - const i18nNamespaces = ["loginname"]; - const { t, resources } = await initTranslations(locale, i18nNamespaces); + const locale = await getLocale(); + const messages = await getMessages(); // later only shown with dev mode enabled const showNav = process.env.DEBUG === "true"; @@ -43,18 +34,13 @@ export default async function RootLayout({ return ( - +
) : (
- + {/* */}
)} @@ -91,7 +77,7 @@ export default async function RootLayout({
-
+
diff --git a/apps/login/src/app/[locale]/loginname/page.tsx b/apps/login/src/app/loginname/page.tsx similarity index 90% rename from apps/login/src/app/[locale]/loginname/page.tsx rename to apps/login/src/app/loginname/page.tsx index bd4f6a5a070..196323b4f86 100644 --- a/apps/login/src/app/[locale]/loginname/page.tsx +++ b/apps/login/src/app/loginname/page.tsx @@ -1,4 +1,3 @@ -import initTranslations from "@/app/i18n"; import { DynamicTheme } from "@/components/dynamic-theme"; import { SignInWithIdp } from "@/components/sign-in-with-idp"; import { UsernameForm } from "@/components/username-form"; @@ -9,6 +8,7 @@ import { settingsService, } from "@/lib/zitadel"; import { makeReqCtx } from "@zitadel/client/v2"; +import { getLocale, getTranslations } from "next-intl/server"; function getIdentityProviders(orgId?: string) { return settingsService @@ -20,12 +20,12 @@ function getIdentityProviders(orgId?: string) { export default async function Page({ searchParams, - params: { locale }, }: { searchParams: Record; - params: { locale: string }; }) { - const { t } = await initTranslations(locale, ["loginname"]); + // const t = useTranslations("loginname"); + const locale = getLocale(); + const t = await getTranslations({ locale, namespace: "loginname" }); const loginName = searchParams?.loginName; const authRequestId = searchParams?.authRequestId; diff --git a/apps/login/src/app/[locale]/me/change-password/page.tsx b/apps/login/src/app/me/change-password/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/me/change-password/page.tsx rename to apps/login/src/app/me/change-password/page.tsx diff --git a/apps/login/src/app/[locale]/mfa/page.tsx b/apps/login/src/app/mfa/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/mfa/page.tsx rename to apps/login/src/app/mfa/page.tsx diff --git a/apps/login/src/app/[locale]/mfa/set/page.tsx b/apps/login/src/app/mfa/set/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/mfa/set/page.tsx rename to apps/login/src/app/mfa/set/page.tsx diff --git a/apps/login/src/app/[locale]/otp/[method]/page.tsx b/apps/login/src/app/otp/[method]/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/otp/[method]/page.tsx rename to apps/login/src/app/otp/[method]/page.tsx diff --git a/apps/login/src/app/[locale]/otp/[method]/set/page.tsx b/apps/login/src/app/otp/[method]/set/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/otp/[method]/set/page.tsx rename to apps/login/src/app/otp/[method]/set/page.tsx diff --git a/apps/login/src/app/[locale]/overview/page.tsx b/apps/login/src/app/overview/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/overview/page.tsx rename to apps/login/src/app/overview/page.tsx diff --git a/apps/login/src/app/[locale]/page.tsx b/apps/login/src/app/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/page.tsx rename to apps/login/src/app/page.tsx diff --git a/apps/login/src/app/[locale]/passkey/page.tsx b/apps/login/src/app/passkey/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/passkey/page.tsx rename to apps/login/src/app/passkey/page.tsx diff --git a/apps/login/src/app/[locale]/passkey/set/page.tsx b/apps/login/src/app/passkey/set/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/passkey/set/page.tsx rename to apps/login/src/app/passkey/set/page.tsx diff --git a/apps/login/src/app/[locale]/password/page.tsx b/apps/login/src/app/password/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/password/page.tsx rename to apps/login/src/app/password/page.tsx diff --git a/apps/login/src/app/[locale]/register/page.tsx b/apps/login/src/app/register/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/register/page.tsx rename to apps/login/src/app/register/page.tsx diff --git a/apps/login/src/app/[locale]/signedin/page.tsx b/apps/login/src/app/signedin/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/signedin/page.tsx rename to apps/login/src/app/signedin/page.tsx diff --git a/apps/login/src/app/[locale]/u2f/page.tsx b/apps/login/src/app/u2f/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/u2f/page.tsx rename to apps/login/src/app/u2f/page.tsx diff --git a/apps/login/src/app/[locale]/u2f/set/page.tsx b/apps/login/src/app/u2f/set/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/u2f/set/page.tsx rename to apps/login/src/app/u2f/set/page.tsx diff --git a/apps/login/src/app/[locale]/verify/page.tsx b/apps/login/src/app/verify/page.tsx similarity index 100% rename from apps/login/src/app/[locale]/verify/page.tsx rename to apps/login/src/app/verify/page.tsx diff --git a/apps/login/src/components/language-switcher.tsx b/apps/login/src/components/language-switcher.tsx index 10ec8b762ea..3a8657d6b96 100644 --- a/apps/login/src/components/language-switcher.tsx +++ b/apps/login/src/components/language-switcher.tsx @@ -1,5 +1,6 @@ "use client"; +import { setLanguageCookie } from "@/lib/cookies"; import { Listbox, ListboxButton, @@ -8,10 +9,9 @@ import { Transition, } from "@headlessui/react"; import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/24/outline"; +import { useTranslations } from "next-intl"; import { usePathname, useRouter } from "next/navigation"; import { Fragment, useState } from "react"; -import { useTranslation } from "react-i18next"; -import i18nConfig from "../../i18nConfig"; interface Lang { id: number; @@ -46,9 +46,9 @@ type Props = { }; export function LanguageSwitcher({ locale }: Props) { - const { i18n } = useTranslation(); + const { i18n } = useTranslations(); - const currentLocale = locale || i18n.language || i18nConfig.defaultLocale; + const currentLocale = locale || i18n.language || "en"; const [selected, setSelected] = useState( LANGS.find((l) => l.code === currentLocale) || LANGS[0], @@ -57,27 +57,27 @@ export function LanguageSwitcher({ locale }: Props) { const router = useRouter(); const currentPathname = usePathname(); - const handleChange = (language: Lang) => { + const handleChange = async (language: Lang) => { setSelected(language); const newLocale = language.code; - // set cookie for next-i18n-router - const days = 30; - const date = new Date(); - date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); - const expires = date.toUTCString(); - document.cookie = `NEXT_LOCALE=${newLocale};expires=${expires};path=/`; + + // set cookie + // const days = 30; + // const date = new Date(); + // date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); + // const expires = date.toUTCString(); + // document.cookie = `NEXT_LOCALE=${newLocale};expires=${expires};path=/`; // redirect to the new locale path - if ( - currentLocale === i18nConfig.defaultLocale && - !i18nConfig.prefixDefault - ) { - router.push("/" + newLocale + currentPathname); - } else { - router.push( - currentPathname.replace(`/${currentLocale}`, `/${newLocale}`), - ); - } + // if (currentLocale === "en" /*i18nConfig.defaultLocale*/) { + // router.push("/" + newLocale + currentPathname); + // } else { + // router.push( + // currentPathname.replace(`/${currentLocale}`, `/${newLocale}`), + // ); + // } + + await setLanguageCookie(newLocale); router.refresh(); }; diff --git a/apps/login/src/components/translations-provider.tsx b/apps/login/src/components/translations-provider.tsx deleted file mode 100644 index d4692a5e6d9..00000000000 --- a/apps/login/src/components/translations-provider.tsx +++ /dev/null @@ -1,26 +0,0 @@ -"use client"; - -import { createInstance } from "i18next"; -import React from "react"; -import { I18nextProvider } from "react-i18next"; -import initTranslations from "../app/i18n"; - -type Props = { - locale: string; - children: React.ReactNode; - namespaces: string[]; - resources: Record>; -}; - -export function TranslationsProvider({ - children, - locale, - namespaces, - resources, -}: Props) { - const i18n = createInstance(); - - initTranslations(locale, namespaces, i18n, resources); - - return {children}; -} diff --git a/apps/login/src/en/loginname.json b/apps/login/src/en/loginname.json deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/apps/login/src/i18n/request.ts b/apps/login/src/i18n/request.ts new file mode 100644 index 00000000000..6cb19f91e72 --- /dev/null +++ b/apps/login/src/i18n/request.ts @@ -0,0 +1,15 @@ +import { getRequestConfig } from "next-intl/server"; +import { cookies } from "next/headers"; + +export default getRequestConfig(async () => { + // Provide a static locale, fetch a user setting, + // read from `cookies()`, `headers()`, etc. + + const cookiesList = cookies(); + const locale = cookiesList.get("NEXT_LOCALE")?.value ?? "en"; + + return { + locale, + messages: (await import(`../../locales/${locale}.json`)).default, + }; +}); diff --git a/apps/login/src/lib/cookies.ts b/apps/login/src/lib/cookies.ts index 8f4cbcc6f49..f9c8debf1c9 100644 --- a/apps/login/src/lib/cookies.ts +++ b/apps/login/src/lib/cookies.ts @@ -17,7 +17,7 @@ type SessionCookie = Cookie & T; function setSessionHttpOnlyCookie(sessions: SessionCookie[]) { const cookiesList = cookies(); - // @ts-ignore + return cookiesList.set({ name: "sessions", value: JSON.stringify(sessions), @@ -26,6 +26,17 @@ function setSessionHttpOnlyCookie(sessions: SessionCookie[]) { }); } +export async function setLanguageCookie(language: string) { + const cookiesList = cookies(); + // @ts-ignore + return cookiesList.set({ + name: "NEXT_LOCALE", + value: JSON.stringify(language), + httpOnly: true, + path: "/", + }); +} + export async function addSessionToCookie( session: SessionCookie, cleanup: boolean = false, diff --git a/apps/login/src/middleware.ts b/apps/login/src/middleware.ts index bf365459686..58252a50aec 100644 --- a/apps/login/src/middleware.ts +++ b/apps/login/src/middleware.ts @@ -1,15 +1,9 @@ -import { i18nRouter } from "next-i18n-router"; import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; -import i18nConfig from "../i18nConfig"; const INSTANCE = process.env.ZITADEL_API_URL; const SERVICE_USER_ID = process.env.ZITADEL_SERVICE_USER_ID as string; -export const config = { - matcher: "/((?!api|static|.*\\..*|_next).*)", -}; - export function middleware(request: NextRequest) { // OIDC specific routes if ( @@ -44,6 +38,4 @@ export function middleware(request: NextRequest) { headers: responseHeaders, }); } - - return i18nRouter(request, i18nConfig); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e054d6e4ecb..45eadb617ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -77,21 +77,15 @@ importers: copy-to-clipboard: specifier: ^3.3.3 version: 3.3.3 - i18next: - specifier: ^23.15.2 - version: 23.15.2 - i18next-resources-to-backend: - specifier: ^1.2.1 - version: 1.2.1 moment: specifier: ^2.29.4 version: 2.30.1 next: specifier: 14.2.14 version: 14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1) - next-i18n-router: - specifier: ^5.5.1 - version: 5.5.1 + next-intl: + specifier: ^3.20.0 + version: 3.20.0(next@14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1))(react@18.3.1) next-themes: specifier: ^0.2.1 version: 0.2.1(next@14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -110,9 +104,6 @@ importers: react-hook-form: specifier: 7.39.5 version: 7.39.5(react@18.3.1) - react-i18next: - specifier: ^15.0.2 - version: 15.0.2(i18next@23.15.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) swr: specifier: ^2.2.0 version: 2.2.5(react@18.3.1) @@ -872,6 +863,18 @@ packages: '@floating-ui/utils@0.2.8': resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==} + '@formatjs/ecma402-abstract@2.1.0': + resolution: {integrity: sha512-SE2V2PE03K9U/YQZ3nxEOysRkQ/CfSwLHR789Uk9N0PTiWT6I+17UTDI97zYEwC1mbnjefqmtjbL8nunjPwGjw==} + + '@formatjs/fast-memoize@2.2.0': + resolution: {integrity: sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==} + + '@formatjs/icu-messageformat-parser@2.7.9': + resolution: {integrity: sha512-9Z5buDRMsTbplXknvRlDmnpWhZrayNVcVvkH0+SSz8Ll4XD/7Tcn8m1IjxM3iBJSwQbxwxb7/g0Fkx3d4j2osw==} + + '@formatjs/icu-skeleton-parser@1.8.3': + resolution: {integrity: sha512-TsKAP013ayZFbWWR2KWy+f9QVZh0yDFTPK3yE4OqU2gnzafvmKTodRtJLVpfZmpXWJ5y7BWD1AsyT14mcbLzig==} + '@formatjs/intl-localematcher@0.5.4': resolution: {integrity: sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==} @@ -2563,9 +2566,6 @@ packages: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} - html-parse-stringify@3.0.1: - resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==} - http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -2597,12 +2597,6 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} - i18next-resources-to-backend@1.2.1: - resolution: {integrity: sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==} - - i18next@23.15.2: - resolution: {integrity: sha512-zcPSWzCvw6uKnuYHIqs4W7hTuB9e3AFcSdZgvCWoPXIZsBjBd4djN2/2uOHIB+1DFFkQnMBXvhNg7J3WyCuywQ==} - iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -2655,6 +2649,9 @@ packages: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} + intl-messageformat@10.6.0: + resolution: {integrity: sha512-AYKl/DY1nl75pJU8EK681JOVL40uQTNJe3yEMXKfydDFoz+5hNrM/PqjchueSMKGKCZKBVgeexqZwy3uC2B36Q==} + is-arguments@1.1.1: resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} engines: {node: '>= 0.4'} @@ -3187,8 +3184,11 @@ packages: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} - next-i18n-router@5.5.1: - resolution: {integrity: sha512-uJGYUAQS33LbRT3Jx+kurR/E79iPQo1jWZUYmc+614UkPt58k2XYyGloSvHR74b21i4K/d6eksdBj6T2WojjdA==} + next-intl@3.20.0: + resolution: {integrity: sha512-0bCZcc38HfAZk/T+PNNcnJZknC+caS5rBK+WYRd1HsOL5O6puEu2H3kya8oT9s8piHjrTf7P0UHeahOFleOnrw==} + 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 next-themes@0.2.1: resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==} @@ -3691,19 +3691,6 @@ packages: peerDependencies: react: ^16.8.0 || ^17 || ^18 - react-i18next@15.0.2: - resolution: {integrity: sha512-z0W3/RES9Idv3MmJUcf0mDNeeMOUXe+xoL0kPfQPbDoZHmni/XsIoq5zgT2MCFUiau283GuBUK578uD/mkAbLQ==} - peerDependencies: - i18next: '>= 23.2.3' - react: '>= 16.8.0' - react-dom: '*' - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -4396,6 +4383,11 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + use-intl@3.20.0: + resolution: {integrity: sha512-5WQs6yZVWI9K7vw3134P0bhKNp4mi8NbmqKOCuhD9nQUMTKdmpBXwjk62+axwvEbj4XrZxj4X93mQMLXU5ZsCg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + use-sync-external-store@1.2.2: resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} peerDependencies: @@ -4484,10 +4476,6 @@ packages: jsdom: optional: true - void-elements@3.1.0: - resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} - engines: {node: '>=0.10.0'} - w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -5203,6 +5191,27 @@ snapshots: '@floating-ui/utils@0.2.8': {} + '@formatjs/ecma402-abstract@2.1.0': + dependencies: + '@formatjs/fast-memoize': 2.2.0 + '@formatjs/intl-localematcher': 0.5.4 + tslib: 2.7.0 + + '@formatjs/fast-memoize@2.2.0': + dependencies: + tslib: 2.7.0 + + '@formatjs/icu-messageformat-parser@2.7.9': + dependencies: + '@formatjs/ecma402-abstract': 2.1.0 + '@formatjs/icu-skeleton-parser': 1.8.3 + tslib: 2.7.0 + + '@formatjs/icu-skeleton-parser@1.8.3': + dependencies: + '@formatjs/ecma402-abstract': 2.1.0 + tslib: 2.7.0 + '@formatjs/intl-localematcher@0.5.4': dependencies: tslib: 2.7.0 @@ -7174,10 +7183,6 @@ snapshots: dependencies: whatwg-encoding: 3.1.1 - html-parse-stringify@3.0.1: - dependencies: - void-elements: 3.1.0 - http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 @@ -7213,14 +7218,6 @@ snapshots: human-signals@5.0.0: {} - i18next-resources-to-backend@1.2.1: - dependencies: - '@babel/runtime': 7.25.7 - - i18next@23.15.2: - dependencies: - '@babel/runtime': 7.25.7 - iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -7263,6 +7260,13 @@ snapshots: hasown: 2.0.2 side-channel: 1.0.6 + intl-messageformat@10.6.0: + dependencies: + '@formatjs/ecma402-abstract': 2.1.0 + '@formatjs/fast-memoize': 2.2.0 + '@formatjs/icu-messageformat-parser': 2.7.9 + tslib: 2.7.0 + is-arguments@1.1.1: dependencies: call-bind: 1.0.7 @@ -7779,10 +7783,13 @@ snapshots: negotiator@0.6.3: {} - next-i18n-router@5.5.1: + next-intl@3.20.0(next@14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1))(react@18.3.1): dependencies: '@formatjs/intl-localematcher': 0.5.4 negotiator: 0.6.3 + next: 14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1) + react: 18.3.1 + use-intl: 3.20.0(react@18.3.1) next-themes@0.2.1(next@14.2.14(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.79.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: @@ -8195,15 +8202,6 @@ snapshots: dependencies: react: 18.3.1 - react-i18next@15.0.2(i18next@23.15.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.25.7 - html-parse-stringify: 3.0.1 - i18next: 23.15.2 - react: 18.3.1 - optionalDependencies: - react-dom: 18.3.1(react@18.3.1) - react-is@16.13.1: {} react-is@17.0.2: {} @@ -8985,6 +8983,12 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 + use-intl@3.20.0(react@18.3.1): + dependencies: + '@formatjs/fast-memoize': 2.2.0 + intl-messageformat: 10.6.0 + react: 18.3.1 + use-sync-external-store@1.2.2(react@18.3.1): dependencies: react: 18.3.1 @@ -9077,8 +9081,6 @@ snapshots: - supports-color - terser - void-elements@3.1.0: {} - w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0