) {
try {
- const result = await addToCartAction(data);
+ const result = await addToCartAction(data, currency?.code);
if (result.error) {
notify({
diff --git a/examples/core/src/components/product/variations/VariationProductProvider.tsx b/examples/core/src/components/product/variations/VariationProductProvider.tsx
index 45652cd2..a2cad866 100644
--- a/examples/core/src/components/product/variations/VariationProductProvider.tsx
+++ b/examples/core/src/components/product/variations/VariationProductProvider.tsx
@@ -25,7 +25,7 @@ import {
createEmptyOptionDict,
mapOptionsToVariation,
} from "./util/map-options-to-variations";
-import type { Location, ProductMeta } from "@epcc-sdk/sdks-shopper";
+import type { Location, ProductMeta, ResponseCurrency } from "@epcc-sdk/sdks-shopper";
export interface VariationProductProvider {
product: ProductData;
@@ -33,6 +33,7 @@ export interface VariationProductProvider {
inventory?: StockResponse;
children: ReactNode;
locations?: Location[];
+ currency?: ResponseCurrency;
}
export interface VariationProductContextType {
@@ -56,6 +57,7 @@ export function VariationProductProvider({
children,
parentProduct,
locations,
+ currency,
}: VariationProductProvider): JSX.Element {
const productContext = useCreateShopperProductContext(
sourceProduct,
@@ -100,6 +102,7 @@ export function VariationProductProvider({
{children}
diff --git a/examples/core/src/components/search/Hit.tsx b/examples/core/src/components/search/Hit.tsx
index 58bac822..22193ebf 100644
--- a/examples/core/src/components/search/Hit.tsx
+++ b/examples/core/src/components/search/Hit.tsx
@@ -1,4 +1,4 @@
-import Link from "next/link";
+import { LocaleLink } from "../LocaleLink";
import Price from "../product/Price";
import StrikePrice from "../product/StrikePrice";
import { EP_CURRENCY_CODE } from "../../lib/resolve-ep-currency-code";
@@ -26,7 +26,7 @@ export default function HitComponent({
return (
<>
-
+
-
+
{hit.attributes?.name}
-
+
{hit.attributes?.description}
@@ -80,7 +80,7 @@ export default function HitComponent({
-
+
>
);
}
diff --git a/examples/core/src/components/search/MobileFilters.tsx b/examples/core/src/components/search/MobileFilters.tsx
index 21b51a84..358a4e23 100644
--- a/examples/core/src/components/search/MobileFilters.tsx
+++ b/examples/core/src/components/search/MobileFilters.tsx
@@ -3,7 +3,7 @@ import { Dialog, Transition } from "@headlessui/react";
import { Dispatch, Fragment, SetStateAction, type JSX } from "react";
import { XMarkIcon } from "@heroicons/react/24/solid";
import NodeMenu from "./NodeMenu";
-import { useStore } from "../../app/(store)/StoreProvider";
+import { useStore } from "../../app/[lang]/(store)/StoreProvider";
interface IMobileFilters {
lookup?: BreadcrumbLookup;
diff --git a/examples/core/src/components/search/NodeMenu.tsx b/examples/core/src/components/search/NodeMenu.tsx
index 9d0b0563..3a6b199e 100644
--- a/examples/core/src/components/search/NodeMenu.tsx
+++ b/examples/core/src/components/search/NodeMenu.tsx
@@ -1,6 +1,6 @@
import { clsx } from "clsx";
import { usePathname } from "next/navigation";
-import Link from "next/link";
+import { LocaleLink } from "../LocaleLink";
import type { JSX } from "react";
import { NavigationNode } from "../../lib/build-site-navigation";
@@ -34,7 +34,7 @@ function MenuItem({ item }: MenuItemProps): JSX.Element {
activeItem && clsx("ais-HierarchicalMenu-item--selected"),
)}
>
-
{item.name}
-
+
{activeItem && !!item.children?.length && (
diff --git a/examples/core/src/components/search/SearchResults.tsx b/examples/core/src/components/search/SearchResults.tsx
index 7a20d3ab..9f5fe587 100644
--- a/examples/core/src/components/search/SearchResults.tsx
+++ b/examples/core/src/components/search/SearchResults.tsx
@@ -8,8 +8,8 @@ import { BreadcrumbLookup } from "../../lib/types/breadcrumb-lookup";
import { buildBreadcrumbLookup } from "../../lib/build-breadcrumb-lookup";
import MobileFilters from "./MobileFilters";
import { ProductListData } from "@epcc-sdk/sdks-shopper";
-import { useStore } from "../../app/(store)/StoreProvider";
-import { useElasticPathClient } from "../../app/(store)/ClientProvider";
+import { useStore } from "../../app/[lang]/(store)/StoreProvider";
+import { useElasticPathClient } from "../../app/[lang]/(store)/ClientProvider";
import { ResourcePagination } from "../pagination/ResourcePagination";
interface ISearchResults {
diff --git a/examples/core/src/lib/create-cookie-from-generate-token-response.ts b/examples/core/src/lib/create-cookie-from-generate-token-response.ts
index dfe455b7..c576d21b 100644
--- a/examples/core/src/lib/create-cookie-from-generate-token-response.ts
+++ b/examples/core/src/lib/create-cookie-from-generate-token-response.ts
@@ -7,7 +7,7 @@ import {
import {
AccountMemberCredential,
AccountMemberCredentials,
-} from "../app/(auth)/account-member-credentials-schema";
+} from "../app/[lang]/(auth)/account-member-credentials-schema";
export type AccountMemberAuthResponse = NonNullable<
Awaited>["data"]
diff --git a/examples/core/src/lib/format-currency.tsx b/examples/core/src/lib/format-currency.tsx
index 694a2b93..004443b1 100644
--- a/examples/core/src/lib/format-currency.tsx
+++ b/examples/core/src/lib/format-currency.tsx
@@ -7,11 +7,40 @@ export function formatCurrency(
locals?: Parameters[0];
} = { locals: "en-US" },
) {
- const { decimal_places = 2, code } = currency;
+ const {
+ decimal_places = 2,
+ decimal_point = ".",
+ thousand_separator = ",",
+ format = "{price} {code}",
+ code,
+ } = currency;
const resolvedAmount = amount / Math.pow(10, decimal_places);
- return new Intl.NumberFormat(options.locals, {
+ const [integerPart, decimalPartRaw] = resolvedAmount
+ .toFixed(decimal_places)
+ .split(".");
+
+ const integerWithGrouping = integerPart?.replace(
+ /\B(?=(\d{3})+(?!\d))/g,
+ thousand_separator
+ );
+
+ const formattedNumber = `${integerWithGrouping}${decimal_point}${decimalPartRaw}`;
+
+ const isNegative = resolvedAmount < 0;
+
+ const absoluteFormattedNumber = formattedNumber.replace(/^-/, "");
+
+ let finalFormatted = format
+ .replace("{price}", absoluteFormattedNumber)
+ .replace("{code}", code ?? "");
+
+ if (isNegative) {
+ finalFormatted = `-${finalFormatted}`;
+ }
+
+ return finalFormatted || new Intl.NumberFormat(options.locals, {
style: "currency",
maximumFractionDigits: decimal_places,
minimumFractionDigits: decimal_places,
diff --git a/examples/core/src/lib/i18n.ts b/examples/core/src/lib/i18n.ts
new file mode 100644
index 00000000..a3930fe6
--- /dev/null
+++ b/examples/core/src/lib/i18n.ts
@@ -0,0 +1,28 @@
+import { ResponseCurrency } from "@epcc-sdk/sdks-shopper";
+
+export const SUPPORTED_LOCALES = ["en", "fr", "de", "es", "en-GB"];
+
+export const LOCALE_TO_CURRENCY: Record = {
+ en: "USD",
+ fr: "EUR",
+ de: "EUR",
+ "en-GB": "GBP",
+};
+
+export function getPreferredCurrency(lang: string | undefined, currencies: ResponseCurrency[], cartCurrencyCode?: string) {
+ if (!currencies?.length) return undefined;
+
+ const preferredCode = cartCurrencyCode
+ ? cartCurrencyCode
+ : lang
+ ? LOCALE_TO_CURRENCY[lang]
+ : undefined
+
+ let currency = currencies.find((c: any) => c.code === preferredCode && c.enabled)
+
+ if (!currency) {
+ currency = currencies.find((c: any) => c.default && c.enabled);
+ }
+
+ return currency;
+};
diff --git a/examples/core/src/lib/retrieve-account-member-credentials.ts b/examples/core/src/lib/retrieve-account-member-credentials.ts
index 33fdee1c..39bbbaa0 100644
--- a/examples/core/src/lib/retrieve-account-member-credentials.ts
+++ b/examples/core/src/lib/retrieve-account-member-credentials.ts
@@ -3,7 +3,7 @@ import {
AccountMemberCredential,
AccountMemberCredentials,
accountMemberCredentialsSchema,
-} from "../app/(auth)/account-member-credentials-schema";
+} from "../app/[lang]/(auth)/account-member-credentials-schema";
export function getSelectedAccount(
memberCredentials: AccountMemberCredentials,
diff --git a/examples/core/src/middleware.ts b/examples/core/src/middleware.ts
index a409e123..94753ddc 100644
--- a/examples/core/src/middleware.ts
+++ b/examples/core/src/middleware.ts
@@ -1,16 +1,33 @@
-import { NextFetchEvent, NextRequest } from "next/server";
+import { NextFetchEvent, NextRequest, NextResponse } from "next/server";
import { implicitAuthMiddleware } from "./lib/middleware/implicit-auth-middleware";
import { cartCookieMiddleware } from "./lib/middleware/cart-cookie-middleware";
import { composeMiddleware } from "./lib/middleware/run-middleware";
+import { SUPPORTED_LOCALES } from "./lib/i18n";
export const config = {
matcher: ["/", "/((?!_next|api|favicon|configuration-error).*)"],
};
export async function middleware(req: NextRequest, event: NextFetchEvent) {
+ const acceptLanguage = req.headers.get("accept-language");
+ const firstLang = acceptLanguage?.split(",")[0] ?? "";
+ const browserLocale = firstLang.split("-")[0] || "en";
+
+ const DEFAULT_LOCALE = browserLocale;
+
+ const url = req.nextUrl.clone();
+ const pathSegments = url.pathname.split("/").filter(Boolean);
+
+ const locale = pathSegments[0] ?? "";
+
+ if (!SUPPORTED_LOCALES.includes(locale)) {
+ url.pathname = `/${DEFAULT_LOCALE}${url.pathname}`;
+ return NextResponse.redirect(url);
+ }
+
const runner = composeMiddleware([
implicitAuthMiddleware,
cartCookieMiddleware,
]);
return runner(req, event);
-}
+}
\ No newline at end of file