Phase 1: Infrastructure setup - Install next-intl and create basic i18n structure

- Install next-intl package
- Create LanguageProvider hook with localStorage persistence
- Create IntlProvider component for next-intl integration
- Create Providers wrapper component
- Update layout.tsx to include providers and set default lang to 'es'
- Create initial translation files (common.json) for es, en, ca
- Fix pre-existing TypeScript errors in various pages

All tests passing, build successful.
This commit is contained in:
counterweight 2025-12-25 21:50:34 +01:00
parent 1a47b3643f
commit f7553df05d
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
15 changed files with 940 additions and 22 deletions

View file

@ -63,7 +63,7 @@ export default function AdminPriceHistoryPage() {
<div style={styles.tableHeader}>
<h2 style={styles.tableTitle}>Bitcoin Price History</h2>
<div style={styles.headerActions}>
<span style={styles.totalCount}>{records.length} records</span>
<span style={styles.totalCount}>{records?.length ?? 0} records</span>
<button onClick={fetchRecords} style={styles.refreshBtn} disabled={isLoadingData}>
Refresh
</button>
@ -98,7 +98,7 @@ export default function AdminPriceHistoryPage() {
</td>
</tr>
)}
{!error && !isLoadingData && records.length === 0 && (
{!error && !isLoadingData && (records?.length ?? 0) === 0 && (
<tr>
<td colSpan={4} style={styles.emptyRow}>
No price records yet. Click &quot;Fetch Now&quot; to get the current price.
@ -107,7 +107,7 @@ export default function AdminPriceHistoryPage() {
)}
{!error &&
!isLoadingData &&
records.map((record) => (
(records ?? []).map((record) => (
<tr key={record.id} style={styles.tr}>
<td style={styles.td}>{record.source}</td>
<td style={styles.td}>{record.pair}</td>

View file

@ -5,6 +5,7 @@ import { Permission } from "../../auth-context";
import { adminApi } from "../../api";
import { Header } from "../../components/Header";
import { SatsDisplay } from "../../components/SatsDisplay";
import { StatusBadge } from "../../components/StatusBadge";
import { EmptyState } from "../../components/EmptyState";
import { ConfirmationButton } from "../../components/ConfirmationButton";
import { useRequireAuth } from "../../hooks/useRequireAuth";
@ -275,7 +276,9 @@ export default function AdminTradesPage() {
</span>
</div>
<StatusBadge tradeStatus={trade.status} style={{ marginTop: "0.5rem" }} />
<StatusBadge tradeStatus={trade.status} style={{ marginTop: "0.5rem" }}>
{""}
</StatusBadge>
</div>
{/* Actions */}

View file

@ -0,0 +1,34 @@
"use client";
import { NextIntlClientProvider } from "next-intl";
import { ReactNode, useMemo } from "react";
import { useLanguage } from "../hooks/useLanguage";
// Import all locale messages
import esCommon from "../../locales/es/common.json";
import enCommon from "../../locales/en/common.json";
import caCommon from "../../locales/ca/common.json";
const messages = {
es: { common: esCommon },
en: { common: enCommon },
ca: { common: caCommon },
};
interface IntlProviderProps {
children: ReactNode;
}
export function IntlProvider({ children }: IntlProviderProps) {
const { locale } = useLanguage();
const localeMessages = useMemo(() => {
return messages[locale] || messages.es;
}, [locale]);
return (
<NextIntlClientProvider locale={locale} messages={localeMessages}>
{children}
</NextIntlClientProvider>
);
}

View file

@ -0,0 +1,17 @@
"use client";
import { ReactNode } from "react";
import { LanguageProvider } from "../hooks/useLanguage";
import { IntlProvider } from "./IntlProvider";
interface ProvidersProps {
children: ReactNode;
}
export function Providers({ children }: ProvidersProps) {
return (
<LanguageProvider>
<IntlProvider>{children}</IntlProvider>
</LanguageProvider>
);
}

View file

@ -0,0 +1,54 @@
"use client";
import { useState, useEffect, createContext, useContext, ReactNode } from "react";
export type Locale = "es" | "en" | "ca";
const LOCALE_STORAGE_KEY = "arbret-locale";
const DEFAULT_LOCALE: Locale = "es";
interface LanguageContextType {
locale: Locale;
setLocale: (locale: Locale) => void;
}
const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
export function LanguageProvider({ children }: { children: ReactNode }) {
const [locale, setLocaleState] = useState<Locale>(DEFAULT_LOCALE);
const [isHydrated, setIsHydrated] = useState(false);
// Load locale from localStorage on mount
useEffect(() => {
const stored = localStorage.getItem(LOCALE_STORAGE_KEY);
if (stored && (stored === "es" || stored === "en" || stored === "ca")) {
setLocaleState(stored as Locale);
}
setIsHydrated(true);
}, []);
// Update HTML lang attribute when locale changes
useEffect(() => {
if (isHydrated) {
document.documentElement.lang = locale;
}
}, [locale, isHydrated]);
const setLocale = (newLocale: Locale) => {
setLocaleState(newLocale);
localStorage.setItem(LOCALE_STORAGE_KEY, newLocale);
document.documentElement.lang = newLocale;
};
return (
<LanguageContext.Provider value={{ locale, setLocale }}>{children}</LanguageContext.Provider>
);
}
export function useLanguage() {
const context = useContext(LanguageContext);
if (context === undefined) {
throw new Error("useLanguage must be used within a LanguageProvider");
}
return context;
}

View file

@ -0,0 +1,8 @@
"use client";
import { useTranslations as useNextIntlTranslations } from "next-intl";
export function useTranslation(namespace?: string) {
const t = useNextIntlTranslations(namespace);
return t;
}

View file

@ -4,6 +4,7 @@ import { useState } from "react";
import { invitesApi } from "../api";
import { PageLayout } from "../components/PageLayout";
import { StatusBadge } from "../components/StatusBadge";
import { EmptyState } from "../components/EmptyState";
import { useRequireAuth } from "../hooks/useRequireAuth";
import { useAsyncData } from "../hooks/useAsyncData";
import { components } from "../generated/api";
@ -49,9 +50,9 @@ export default function InvitesPage() {
};
const { READY, SPENT, REVOKED } = constants.inviteStatuses;
const readyInvites = invites.filter((i) => i.status === READY);
const spentInvites = invites.filter((i) => i.status === SPENT);
const revokedInvites = invites.filter((i) => i.status === REVOKED);
const readyInvites = (invites ?? []).filter((i) => i.status === READY);
const spentInvites = (invites ?? []).filter((i) => i.status === SPENT);
const revokedInvites = (invites ?? []).filter((i) => i.status === REVOKED);
return (
<PageLayout
@ -67,7 +68,7 @@ export default function InvitesPage() {
</p>
</div>
{invites.length === 0 ? (
{(invites?.length ?? 0) === 0 ? (
<EmptyState
message="You don't have any invites yet."
hint="Contact an admin if you need invite codes to share."

View file

@ -1,8 +1,9 @@
import { AuthProvider } from "./auth-context";
import { Providers } from "./components/Providers";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<html lang="es">
<head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
@ -37,7 +38,9 @@ export default function RootLayout({ children }: { children: React.ReactNode })
`}</style>
</head>
<body>
<AuthProvider>{children}</AuthProvider>
<Providers>
<AuthProvider>{children}</AuthProvider>
</Providers>
</body>
</html>
);

View file

@ -1,6 +1,6 @@
"use client";
import { CSSProperties } from "react";
import { CSSProperties, useState } from "react";
import { useParams, useRouter } from "next/navigation";
import { Permission } from "../../auth-context";
import { tradesApi } from "../../api";
@ -23,6 +23,7 @@ export default function TradeDetailPage() {
const router = useRouter();
const params = useParams();
const publicId = params?.id as string | undefined;
const [cancelError, setCancelError] = useState<string | null>(null);
const { user, isLoading, isAuthorized } = useRequireAuth({
requiredPermission: Permission.VIEW_OWN_EXCHANGES,
@ -78,6 +79,10 @@ export default function TradeDetailPage() {
);
}
if (!trade) {
return null;
}
const isBuy = trade.direction === "buy";
return (
@ -97,7 +102,7 @@ export default function TradeDetailPage() {
<div style={styles.detailGrid}>
<div style={styles.detailRow}>
<span style={styles.detailLabel}>Status:</span>
<StatusBadge tradeStatus={trade.status} />
<StatusBadge tradeStatus={trade.status}>{""}</StatusBadge>
</div>
<div style={styles.detailRow}>
<span style={styles.detailLabel}>Time:</span>
@ -194,6 +199,7 @@ export default function TradeDetailPage() {
</div>
</div>
{cancelError && <div style={bannerStyles.errorBanner}>{cancelError}</div>}
{trade.status === "booked" && (
<div style={styles.actionSection}>
<button
@ -209,7 +215,7 @@ export default function TradeDetailPage() {
await tradesApi.cancelTrade(trade.public_id);
router.push("/trades");
} catch (err) {
setError(err instanceof Error ? err.message : "Failed to cancel trade");
setCancelError(err instanceof Error ? err.message : "Failed to cancel trade");
}
}}
style={buttonStyles.secondaryButton}

View file

@ -8,6 +8,7 @@ import { PageLayout } from "../components/PageLayout";
import { SatsDisplay } from "../components/SatsDisplay";
import { StatusBadge } from "../components/StatusBadge";
import { EmptyState } from "../components/EmptyState";
import { ConfirmationButton } from "../components/ConfirmationButton";
import { useRequireAuth } from "../hooks/useRequireAuth";
import { useAsyncData } from "../hooks/useAsyncData";
import { useMutation } from "../hooks/useMutation";
@ -54,10 +55,10 @@ export default function TradesPage() {
}
};
const upcomingTrades = trades.filter(
const upcomingTrades = (trades ?? []).filter(
(t) => t.status === "booked" && new Date(t.slot_start) > new Date()
);
const pastOrFinalTrades = trades.filter(
const pastOrFinalTrades = (trades ?? []).filter(
(t) => t.status !== "booked" || new Date(t.slot_start) <= new Date()
);
@ -74,7 +75,7 @@ export default function TradesPage() {
{isLoadingTrades ? (
<EmptyState message="Loading trades..." isLoading={true} />
) : trades.length === 0 ? (
) : (trades?.length ?? 0) === 0 ? (
<EmptyState
message="You don't have any trades yet."
action={
@ -140,7 +141,9 @@ export default function TradesPage() {
/BTC
</span>
</div>
<StatusBadge tradeStatus={trade.status} style={{ marginTop: "0.5rem" }} />
<StatusBadge tradeStatus={trade.status} style={{ marginTop: "0.5rem" }}>
{""}
</StatusBadge>
</div>
<div style={tradeCardStyles.buttonGroup}>
@ -224,7 +227,7 @@ export default function TradesPage() {
</span>
</div>
<div style={tradeCardStyles.buttonGroup}>
<StatusBadge tradeStatus={trade.status} />
<StatusBadge tradeStatus={trade.status}>{""}</StatusBadge>
<button
onClick={(e) => {
e.stopPropagation();

View file

@ -0,0 +1,15 @@
{
"loading": "Carregant...",
"error": "Error",
"save": "Desar",
"cancel": "Cancel·lar",
"back": "Enrere",
"continue": "Continuar",
"confirm": "Confirmar",
"delete": "Eliminar",
"edit": "Editar",
"close": "Tancar",
"yes": "Sí",
"no": "No",
"retry": "Tornar a intentar"
}

View file

@ -0,0 +1,15 @@
{
"loading": "Loading...",
"error": "Error",
"save": "Save",
"cancel": "Cancel",
"back": "Back",
"continue": "Continue",
"confirm": "Confirm",
"delete": "Delete",
"edit": "Edit",
"close": "Close",
"yes": "Yes",
"no": "No",
"retry": "Retry"
}

View file

@ -0,0 +1,15 @@
{
"loading": "Cargando...",
"error": "Error",
"save": "Guardar",
"cancel": "Cancelar",
"back": "Atrás",
"continue": "Continuar",
"confirm": "Confirmar",
"delete": "Eliminar",
"edit": "Editar",
"close": "Cerrar",
"yes": "Sí",
"no": "No",
"retry": "Reintentar"
}

View file

@ -10,6 +10,7 @@
"dependencies": {
"bech32": "^2.0.0",
"next": "15.1.2",
"next-intl": "^4.6.1",
"react": "19.0.0",
"react-dom": "19.0.0"
},
@ -1093,6 +1094,66 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@formatjs/ecma402-abstract": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz",
"integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==",
"license": "MIT",
"dependencies": {
"@formatjs/fast-memoize": "2.2.7",
"@formatjs/intl-localematcher": "0.6.2",
"decimal.js": "^10.4.3",
"tslib": "^2.8.0"
}
},
"node_modules/@formatjs/ecma402-abstract/node_modules/@formatjs/intl-localematcher": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz",
"integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==",
"license": "MIT",
"dependencies": {
"tslib": "^2.8.0"
}
},
"node_modules/@formatjs/fast-memoize": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz",
"integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==",
"license": "MIT",
"dependencies": {
"tslib": "^2.8.0"
}
},
"node_modules/@formatjs/icu-messageformat-parser": {
"version": "2.11.4",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz",
"integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "2.3.6",
"@formatjs/icu-skeleton-parser": "1.8.16",
"tslib": "^2.8.0"
}
},
"node_modules/@formatjs/icu-skeleton-parser": {
"version": "1.8.16",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz",
"integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==",
"license": "MIT",
"dependencies": {
"@formatjs/ecma402-abstract": "2.3.6",
"tslib": "^2.8.0"
}
},
"node_modules/@formatjs/intl-localematcher": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.10.tgz",
"integrity": "sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==",
"license": "MIT",
"dependencies": {
"tslib": "2"
}
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@ -1718,6 +1779,313 @@
"node": ">= 10"
}
},
"node_modules/@parcel/watcher": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
"integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"detect-libc": "^1.0.3",
"is-glob": "^4.0.3",
"micromatch": "^4.0.5",
"node-addon-api": "^7.0.0"
},
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"@parcel/watcher-android-arm64": "2.5.1",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-freebsd-x64": "2.5.1",
"@parcel/watcher-linux-arm-glibc": "2.5.1",
"@parcel/watcher-linux-arm-musl": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-ia32": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1"
}
},
"node_modules/@parcel/watcher-android-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
"integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
"integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-darwin-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
"integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-freebsd-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
"integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
"integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
"integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
"integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-arm64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
"integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-glibc": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
"integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-linux-x64-musl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-arm64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
"integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-ia32": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
"integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
"cpu": [
"ia32"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher-win32-x64": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
"integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/@parcel/watcher/node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
"license": "Apache-2.0",
"bin": {
"detect-libc": "bin/detect-libc.js"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@ -2106,6 +2474,172 @@
"win32"
]
},
"node_modules/@schummar/icu-type-parser": {
"version": "1.21.5",
"resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz",
"integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==",
"license": "MIT"
},
"node_modules/@swc/core-darwin-arm64": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.7.tgz",
"integrity": "sha512-+hNVUfezUid7LeSHqnhoC6Gh3BROABxjlDNInuZ/fie1RUxaEX4qzDwdTgozJELgHhvYxyPIg1ro8ibnKtgO4g==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-darwin-x64": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.7.tgz",
"integrity": "sha512-ZAFuvtSYZTuXPcrhanaD5eyp27H8LlDzx2NAeVyH0FchYcuXf0h5/k3GL9ZU6Jw9eQ63R1E8KBgpXEJlgRwZUQ==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm-gnueabihf": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.7.tgz",
"integrity": "sha512-K3HTYocpqnOw8KcD8SBFxiDHjIma7G/X+bLdfWqf+qzETNBrzOub/IEkq9UaeupaJiZJkPptr/2EhEXXWryS/A==",
"cpu": [
"arm"
],
"license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.7.tgz",
"integrity": "sha512-HCnVIlsLnCtQ3uXcXgWrvQ6SAraskLA9QJo9ykTnqTH6TvUYqEta+TdTdGjzngD6TOE7XjlAiUs/RBtU8Z0t+Q==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-arm64-musl": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.7.tgz",
"integrity": "sha512-/OOp9UZBg4v2q9+x/U21Jtld0Wb8ghzBScwhscI7YvoSh4E8RALaJ1msV8V8AKkBkZH7FUAFB7Vbv0oVzZsezA==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-x64-gnu": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.7.tgz",
"integrity": "sha512-VBbs4gtD4XQxrHuQ2/2+TDZpPQQgrOHYRnS6SyJW+dw0Nj/OomRqH+n5Z4e/TgKRRbieufipeIGvADYC/90PYQ==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-linux-x64-musl": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.7.tgz",
"integrity": "sha512-kVuy2unodso6p0rMauS2zby8/bhzoGRYxBDyD6i2tls/fEYAE74oP0VPFzxIyHaIjK1SN6u5TgvV9MpyJ5xVug==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-arm64-msvc": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.7.tgz",
"integrity": "sha512-uddYoo5Xmo1XKLhAnh4NBIyy5d0xk33x1sX3nIJboFySLNz878ksCFCZ3IBqrt1Za0gaoIWoOSSSk0eNhAc/sw==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-ia32-msvc": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.7.tgz",
"integrity": "sha512-rqq8JjNMLx3QNlh0aPTtN/4+BGLEHC94rj9mkH1stoNRf3ra6IksNHMHy+V1HUqElEgcZyx+0yeXx3eLOTcoFw==",
"cpu": [
"ia32"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/core-win32-x64-msvc": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.7.tgz",
"integrity": "sha512-4BK06EGdPnuplgcNhmSbOIiLdRgHYX3v1nl4HXo5uo4GZMfllXaCyBUes+0ePRfwbn9OFgVhCWPcYYjMT6hycQ==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=10"
}
},
"node_modules/@swc/counter": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
@ -2121,6 +2655,15 @@
"tslib": "^2.8.0"
}
},
"node_modules/@swc/types": {
"version": "0.1.25",
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz",
"integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==",
"license": "Apache-2.0",
"dependencies": {
"@swc/counter": "^0.1.3"
}
},
"node_modules/@testing-library/dom": {
"version": "10.4.1",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
@ -2822,6 +3365,18 @@
"balanced-match": "^1.0.0"
}
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/browserslist": {
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
@ -3131,7 +3686,6 @@
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
"dev": true,
"license": "MIT"
},
"node_modules/deep-eql": {
@ -3570,6 +4124,18 @@
"node": ">=16.0.0"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@ -3851,6 +4417,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/intl-messageformat": {
"version": "10.7.18",
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.18.tgz",
"integrity": "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==",
"license": "BSD-3-Clause",
"dependencies": {
"@formatjs/ecma402-abstract": "2.3.6",
"@formatjs/fast-memoize": "2.2.7",
"@formatjs/icu-messageformat-parser": "2.11.4",
"tslib": "^2.8.0"
}
},
"node_modules/is-arrayish": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz",
@ -3862,7 +4440,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@ -3882,7 +4459,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
@ -3891,6 +4467,15 @@
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/is-potential-custom-element-name": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
@ -4218,6 +4803,31 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/micromatch/node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
@ -4273,6 +4883,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/next": {
"version": "15.1.2",
"resolved": "https://registry.npmjs.org/next/-/next-15.1.2.tgz",
@ -4328,6 +4947,98 @@
}
}
},
"node_modules/next-intl": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.6.1.tgz",
"integrity": "sha512-KlWgWtKLBPUsTPgxqwyjws1wCMD2QKxLlVjeeGj53DC1JWfKmBShKOrhIP0NznZrRQ0GleeoDUeHSETmyyIFeA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/amannn"
}
],
"license": "MIT",
"dependencies": {
"@formatjs/intl-localematcher": "^0.5.4",
"@parcel/watcher": "^2.4.1",
"@swc/core": "^1.15.2",
"negotiator": "^1.0.0",
"next-intl-swc-plugin-extractor": "^4.6.1",
"po-parser": "^2.0.0",
"use-intl": "^4.6.1"
},
"peerDependencies": {
"next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0",
"typescript": "^5.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/next-intl-swc-plugin-extractor": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/next-intl-swc-plugin-extractor/-/next-intl-swc-plugin-extractor-4.6.1.tgz",
"integrity": "sha512-+HHNeVERfSvuPDF7LYVn3pxst5Rf7EYdUTw7C7WIrYhcLaKiZ1b9oSRkTQddAN3mifDMCfHqO4kAQ/pcKiBl3A==",
"license": "MIT"
},
"node_modules/next-intl/node_modules/@swc/core": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.7.tgz",
"integrity": "sha512-kTGB8XI7P+pTKW83tnUEDVP4zduF951u3UAOn5eTi0vyW6MvL56A3+ggMdfuVFtDI0/DsbSzf5z34HVBbuScWw==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@swc/counter": "^0.1.3",
"@swc/types": "^0.1.25"
},
"engines": {
"node": ">=10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/swc"
},
"optionalDependencies": {
"@swc/core-darwin-arm64": "1.15.7",
"@swc/core-darwin-x64": "1.15.7",
"@swc/core-linux-arm-gnueabihf": "1.15.7",
"@swc/core-linux-arm64-gnu": "1.15.7",
"@swc/core-linux-arm64-musl": "1.15.7",
"@swc/core-linux-x64-gnu": "1.15.7",
"@swc/core-linux-x64-musl": "1.15.7",
"@swc/core-win32-arm64-msvc": "1.15.7",
"@swc/core-win32-ia32-msvc": "1.15.7",
"@swc/core-win32-x64-msvc": "1.15.7"
},
"peerDependencies": {
"@swc/helpers": ">=0.5.17"
},
"peerDependenciesMeta": {
"@swc/helpers": {
"optional": true
}
}
},
"node_modules/next-intl/node_modules/@swc/helpers": {
"version": "0.5.18",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.18.tgz",
"integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==",
"license": "Apache-2.0",
"optional": true,
"peer": true,
"dependencies": {
"tslib": "^2.8.0"
}
},
"node_modules/node-addon-api": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"license": "MIT"
},
"node_modules/node-releases": {
"version": "2.0.27",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
@ -4586,6 +5297,12 @@
"node": ">=4"
}
},
"node_modules/po-parser": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.0.0.tgz",
"integrity": "sha512-SZvoKi3PoI/hHa2V9je9CW7Xgxl4dvO74cvaa6tWShIHT51FkPxje6pt0gTJznJrU67ix91nDaQp2hUxkOYhKA==",
"license": "MIT"
},
"node_modules/postcss": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
@ -5202,6 +5919,18 @@
"dev": true,
"license": "MIT"
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/tough-cookie": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
@ -5277,7 +6006,7 @@
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@ -5359,6 +6088,20 @@
"punycode": "^2.1.0"
}
},
"node_modules/use-intl": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.6.1.tgz",
"integrity": "sha512-mUIj6QvJZ7Rk33mLDxRziz1YiBBAnIji8YW4TXXMdYHtaPEbVucrXD3iKQGAqJhbVn0VnjrEtIKYO1B18mfSJw==",
"license": "MIT",
"dependencies": {
"@formatjs/fast-memoize": "^2.2.0",
"@schummar/icu-type-parser": "1.21.5",
"intl-messageformat": "^10.5.14"
},
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
}
},
"node_modules/vite": {
"version": "5.4.21",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",

View file

@ -19,6 +19,7 @@
"dependencies": {
"bech32": "^2.0.0",
"next": "15.1.2",
"next-intl": "^4.6.1",
"react": "19.0.0",
"react-dom": "19.0.0"
},