, string> | null>(null);
const withRootProvider = (Component: ElementType) => {
const StyledComponent = (props: P) => {
- const [variantProps, otherProps] = recipe.splitVariantProps(props)
- const slotStyles = recipe(variantProps) as Record, string>
+ const [variantProps, otherProps] = recipe.splitVariantProps(props);
+ const slotStyles = recipe(variantProps) as Record, string>;
return (
- )
- }
- return StyledComponent
- }
+ );
+ };
+ return StyledComponent;
+ };
const withProvider = (
Component: ElementType,
slot: Slot,
- options?: Options,
+ options?: Options
): ForwardRefExoticComponent & RefAttributes> => {
const StyledComponent = styled(
Component,
{},
{
- shouldForwardProp: (prop, variantKeys) => shouldForwardProp(prop, variantKeys, options),
- },
- ) as StyledComponent
+ shouldForwardProp: (prop, variantKeys) =>
+ shouldForwardProp(prop, variantKeys, options),
+ }
+ ) as StyledComponent;
const StyledSlotProvider = forwardRef((props, ref) => {
- const [variantProps, otherProps] = recipe.splitVariantProps(props)
- const slotStyles = recipe(variantProps) as Record, string>
+ const [variantProps, otherProps] = recipe.splitVariantProps(props);
+ const slotStyles = recipe(variantProps) as Record, string>;
return (
@@ -62,34 +68,38 @@ export const createStyleContext = (recipe: R) => {
className={cx(slotStyles?.[slot], props.className)}
/>
- )
- })
+ );
+ });
// @ts-expect-error
- StyledSlotProvider.displayName = Component.displayName || Component.name
+ StyledSlotProvider.displayName = Component.displayName || Component.name;
- return StyledSlotProvider
- }
+ return StyledSlotProvider;
+ };
const withContext = (
Component: ElementType,
- slot: Slot,
+ slot: Slot
): ForwardRefExoticComponent & RefAttributes> => {
- const StyledComponent = styled(Component)
+ const StyledComponent = styled(Component);
const StyledSlotComponent = forwardRef((props, ref) => {
- const slotStyles = useContext(StyleContext)
+ const slotStyles = useContext(StyleContext);
return (
-
- )
- })
+
+ );
+ });
// @ts-expect-error
- StyledSlotComponent.displayName = Component.displayName || Component.name
+ StyledSlotComponent.displayName = Component.displayName || Component.name;
- return StyledSlotComponent
- }
+ return StyledSlotComponent;
+ };
return {
withRootProvider,
withProvider,
withContext,
- }
-}
+ };
+};
diff --git a/src/i18n/config.ts b/src/i18n/config.ts
new file mode 100644
index 0000000..8862010
--- /dev/null
+++ b/src/i18n/config.ts
@@ -0,0 +1,13 @@
+import { Locale } from "@/i18n/locales";
+import { getRequestConfig } from "next-intl/server";
+import { notFound } from "next/navigation";
+import { routing } from "@/i18n/routing";
+
+export default getRequestConfig(async ({ locale }) => {
+ // Validate that the incoming `locale` parameter is valid
+ if (!routing.locales.includes(locale as Locale)) notFound();
+
+ return {
+ messages: (await import(`../messages/${locale}.json`)).default,
+ };
+});
diff --git a/src/i18n/locales.ts b/src/i18n/locales.ts
new file mode 100644
index 0000000..bcd9efe
--- /dev/null
+++ b/src/i18n/locales.ts
@@ -0,0 +1,24 @@
+export const locales = [
+ {
+ label: {
+ de: "Deutsch",
+ en: "German",
+ fr: "Allemand",
+ it: "Tedesco",
+ },
+ code: "de",
+ },
+ {
+ label: {
+ de: "Englisch",
+ en: "English",
+ it: "Inglese",
+ fr: "Anglais",
+ },
+ code: "en",
+ },
+] as const;
+
+export type Locale = (typeof locales)[number]["code"];
+
+export const defaultLocale: Locale = "en";
diff --git a/src/i18n/navigation.ts b/src/i18n/navigation.ts
new file mode 100644
index 0000000..4dfd5f1
--- /dev/null
+++ b/src/i18n/navigation.ts
@@ -0,0 +1,8 @@
+// Lightweight wrappers around Next.js' navigation APIs
+
+import { createLocalizedPathnamesNavigation } from "next-intl/navigation";
+import { routing } from "@/i18n/routing";
+
+// that will consider the routing configuration
+export const { Link, redirect, usePathname, useRouter } =
+ createLocalizedPathnamesNavigation(routing);
diff --git a/src/i18n/routing.ts b/src/i18n/routing.ts
new file mode 100644
index 0000000..23d4879
--- /dev/null
+++ b/src/i18n/routing.ts
@@ -0,0 +1,20 @@
+import { defaultLocale, locales } from "./locales";
+
+import { defineRouting } from "next-intl/routing";
+
+export const routing = defineRouting({
+ // A list of all locales that are supported
+ locales: locales.map(({ code }) => code),
+
+ // Used when no locale matches
+ defaultLocale,
+
+ localePrefix: "as-needed",
+
+ pathnames: {
+ "/about": {
+ en: "/about",
+ de: "/ueber-uns",
+ },
+ },
+});
diff --git a/src/middleware.ts b/src/middleware.ts
new file mode 100644
index 0000000..e220572
--- /dev/null
+++ b/src/middleware.ts
@@ -0,0 +1,8 @@
+import createMiddleware from "next-intl/middleware";
+import { routing } from "@/i18n/routing";
+
+export default createMiddleware(routing);
+
+export const config = {
+ matcher: ["/((?!api|admin|static|.*\\..*|_next|favicon.ico|robots.txt).*)"],
+};
diff --git a/yarn.lock b/yarn.lock
index c0366c6..ce997c2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1168,6 +1168,45 @@
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.7.tgz#d0ece53ce99ab5a8e37ebdfe5e32452a2bfc073e"
integrity sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==
+"@formatjs/ecma402-abstract@2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz#39197ab90b1c78b7342b129a56a7acdb8f512e17"
+ integrity sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==
+ dependencies:
+ "@formatjs/intl-localematcher" "0.5.4"
+ tslib "^2.4.0"
+
+"@formatjs/fast-memoize@2.2.0", "@formatjs/fast-memoize@^2.2.0":
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz#33bd616d2e486c3e8ef4e68c99648c196887802b"
+ integrity sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==
+ dependencies:
+ tslib "^2.4.0"
+
+"@formatjs/icu-messageformat-parser@2.7.8":
+ version "2.7.8"
+ resolved "https://registry.yarnpkg.com/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.8.tgz#f6d7643001e9bb5930d812f1f9a9856f30fa0343"
+ integrity sha512-nBZJYmhpcSX0WeJ5SDYUkZ42AgR3xiyhNCsQweFx3cz/ULJjym8bHAzWKvG5e2+1XO98dBYC0fWeeAECAVSwLA==
+ dependencies:
+ "@formatjs/ecma402-abstract" "2.0.0"
+ "@formatjs/icu-skeleton-parser" "1.8.2"
+ tslib "^2.4.0"
+
+"@formatjs/icu-skeleton-parser@1.8.2":
+ version "1.8.2"
+ resolved "https://registry.yarnpkg.com/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.2.tgz#2252c949ae84ee66930e726130ea66731a123c9f"
+ integrity sha512-k4ERKgw7aKGWJZgTarIcNEmvyTVD9FYh0mTrrBMHZ1b8hUu6iOJ4SzsZlo3UNAvHYa+PnvntIwRPt1/vy4nA9Q==
+ dependencies:
+ "@formatjs/ecma402-abstract" "2.0.0"
+ tslib "^2.4.0"
+
+"@formatjs/intl-localematcher@0.5.4", "@formatjs/intl-localematcher@^0.5.4":
+ version "0.5.4"
+ resolved "https://registry.yarnpkg.com/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz#caa71f2e40d93e37d58be35cfffe57865f2b366f"
+ integrity sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==
+ dependencies:
+ tslib "^2.4.0"
+
"@humanwhocodes/config-array@^0.11.14":
version "0.11.14"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b"
@@ -5670,6 +5709,16 @@ internal-slot@^1.0.4, internal-slot@^1.0.7:
hasown "^2.0.0"
side-channel "^1.0.4"
+intl-messageformat@^10.5.14:
+ version "10.5.14"
+ resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.5.14.tgz#e5bb373f8a37b88fbe647d7b941f3ab2a37ed00a"
+ integrity sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w==
+ dependencies:
+ "@formatjs/ecma402-abstract" "2.0.0"
+ "@formatjs/fast-memoize" "2.2.0"
+ "@formatjs/icu-messageformat-parser" "2.7.8"
+ tslib "^2.4.0"
+
ip-address@^9.0.5:
version "9.0.5"
resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a"
@@ -6416,6 +6465,20 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+negotiator@^0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
+ integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
+
+next-intl@^3.18.0:
+ version "3.18.0"
+ resolved "https://registry.yarnpkg.com/next-intl/-/next-intl-3.18.0.tgz#d8aa33b82a72d0a58e9ccf0a3a667c96b9642465"
+ integrity sha512-TpSwnBEHA+hEQH/bJJnDL/BkDdkSxABJv8f0rpAV6yPEw7zq/+tH84SHWzKLZYewfBBgI3yTtuGZYIIPk792pw==
+ dependencies:
+ "@formatjs/intl-localematcher" "^0.5.4"
+ negotiator "^0.6.3"
+ use-intl "^3.18.0"
+
next-tick@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb"
@@ -7836,6 +7899,14 @@ use-context-selector@2.0.0:
resolved "https://registry.yarnpkg.com/use-context-selector/-/use-context-selector-2.0.0.tgz#3b5dafec7aa947c152d4f0aa7f250e99a205df3d"
integrity sha512-owfuSmUNd3eNp3J9CdDl0kMgfidV+MkDvHPpvthN5ThqM+ibMccNE0k+Iq7TWC6JPFvGZqanqiGCuQx6DyV24g==
+use-intl@^3.18.0:
+ version "3.18.0"
+ resolved "https://registry.yarnpkg.com/use-intl/-/use-intl-3.18.0.tgz#f1b448e7da33aac7a0a00acdeb54a872e33fee00"
+ integrity sha512-38A07Lyu4VapNOckenvL0PhQYetOPz5s7qoAs2wvM4yLCiye+pX8352Jhjr9mxA430zYhrLTK4pilX9VdXlX3g==
+ dependencies:
+ "@formatjs/fast-memoize" "^2.2.0"
+ intl-messageformat "^10.5.14"
+
use-isomorphic-layout-effect@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb"