diff --git a/next.config.mjs b/next.config.mjs
index e8b1ccf..23e3526 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -1,3 +1,7 @@
+import createNextIntlPlugin from "next-intl/plugin";
+
+const withNextIntl = createNextIntlPlugin("./src/lib/i18n.ts");
+
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
@@ -10,4 +14,4 @@ const nextConfig = {
},
};
-export default nextConfig;
+export default withNextIntl(nextConfig);
diff --git a/package.json b/package.json
index 37eba83..4a4d492 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
"clsx": "^2.1.1",
"lucide-react": "^0.428.0",
"next": "14.2.5",
+ "next-intl": "^3.17.6",
"next-themes": "^0.3.0",
"react": "^18",
"react-dom": "^18",
diff --git a/src/app/events/page.tsx b/src/app/[locale]/events/page.tsx
similarity index 63%
rename from src/app/events/page.tsx
rename to src/app/[locale]/events/page.tsx
index 24fe400..996387e 100644
--- a/src/app/events/page.tsx
+++ b/src/app/[locale]/events/page.tsx
@@ -1,8 +1,6 @@
-import { FC } from "react";
-
import EventsPage from "@/features/events";
-const Events: FC = () => {
+const Events = () => {
return ;
};
export default Events;
diff --git a/src/app/globals.css b/src/app/[locale]/globals.css
similarity index 100%
rename from src/app/globals.css
rename to src/app/[locale]/globals.css
diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx
new file mode 100644
index 0000000..d94d0e8
--- /dev/null
+++ b/src/app/[locale]/layout.tsx
@@ -0,0 +1,35 @@
+import type { Metadata } from "next";
+import { NextIntlClientProvider } from "next-intl";
+import { getMessages } from "next-intl/server";
+import { Sora } from "next/font/google";
+import "./globals.css";
+import Wrapper from "@/components/layout/wrapper";
+const sora = Sora({ subsets: ["latin"] });
+
+export const metadata: Metadata = {
+ title: "Welcome to Hammercode!",
+ description: "Hammercode is a community based in Palu, Indonesia",
+};
+
+export default async function LocaleRootLayout({
+ children,
+ params: { locale },
+}: Readonly<{
+ children: React.ReactNode;
+ params: { locale: string };
+}>) {
+ const messages = await getMessages();
+
+ return (
+
+
+
+
+
+
+ {children}
+
+
+
+ );
+}
diff --git a/src/app/[locale]/page.tsx b/src/app/[locale]/page.tsx
new file mode 100644
index 0000000..ecde379
--- /dev/null
+++ b/src/app/[locale]/page.tsx
@@ -0,0 +1,5 @@
+import Home from "@/features/home";
+
+export default function HomePage() {
+ return ;
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 0313011..568d357 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,28 +1,9 @@
-import type { Metadata } from "next";
-import { Sora } from "next/font/google";
-import "./globals.css";
-import Wrapper from "@/components/layout/wrapper";
+import { ReactNode } from "react";
-const sora = Sora({ subsets: ["latin"] });
-
-export const metadata: Metadata = {
- title: "Welcome to Hammercode!",
- description: "Hammercode is a community based in Palu, Indonesia",
+type Props = {
+ children: ReactNode;
};
-export default function RootLayout({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- return (
-
-
-
-
-
- {children}
-
-
- );
+export default function RootLayout({ children }: Props) {
+ return children;
}
diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx
index a23b2bc..23405db 100644
--- a/src/app/not-found.tsx
+++ b/src/app/not-found.tsx
@@ -1,4 +1,13 @@
-const NotFound = () => {
- return 404
;
-};
-export default NotFound;
+"use client";
+
+import Error from "next/error";
+
+export default function NotFound() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index ecde379..77d15e3 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,5 +1,5 @@
-import Home from "@/features/home";
+import { redirect } from "next/navigation";
-export default function HomePage() {
- return ;
+export default function RootPage() {
+ redirect("/en");
}
diff --git a/src/components/common/locale-toggle/index.tsx b/src/components/common/locale-toggle/index.tsx
new file mode 100644
index 0000000..acc497a
--- /dev/null
+++ b/src/components/common/locale-toggle/index.tsx
@@ -0,0 +1,54 @@
+import { useTransition } from "react";
+import { useLocale } from "next-intl";
+import { useParams } from "next/navigation";
+
+import { usePathname, useRouter } from "@/lib/navigation";
+import { Locale } from "@/lib/i18n";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { Button } from "@/components/ui/button";
+
+const LocaleToggle = () => {
+ const locale = useLocale();
+ const router = useRouter();
+ const [isPending, startTransition] = useTransition();
+ const pathname = usePathname();
+ const params = useParams();
+
+ const handleSwitch = (value: string) => {
+ const nextLocale = value as Locale;
+ startTransition(() => {
+ router.replace(
+ // @ts-expect-error -- TypeScript will validate that only known `params`
+ // are used in combination with a given `pathname`. Since the two will
+ // always match for the current route, we can skip runtime checks.
+ { pathname, params },
+ { locale: nextLocale }
+ );
+ });
+ };
+ return (
+
+
+
+
+
+ handleSwitch("id")} className="cursor-pointer">
+ 🇮🇩 ID
+
+ handleSwitch("en")} className="cursor-pointer">
+ 🇬🇧 EN
+
+
+
+ );
+};
+
+export default LocaleToggle;
diff --git a/src/components/common/modetoggle/index.tsx b/src/components/common/mode-toggle/index.tsx
similarity index 100%
rename from src/components/common/modetoggle/index.tsx
rename to src/components/common/mode-toggle/index.tsx
diff --git a/src/components/common/navbar/NavbarList.tsx b/src/components/common/navbar/NavbarList.tsx
index 3920fc1..09ae151 100644
--- a/src/components/common/navbar/NavbarList.tsx
+++ b/src/components/common/navbar/NavbarList.tsx
@@ -1,13 +1,15 @@
-import Link from "next/link";
+import { Link } from "@/lib/navigation";
+import { ComponentProps } from "react";
+import { pathnames } from "@/lib/config";
-import { NavbarListType } from "./types";
+export type NavbarListProps = ComponentProps>;
-const NavbarList = ({ title, link }: NavbarListType) => {
+function NavbarList({ href, ...rest }: NavbarListProps) {
return (
-
- {title}
+
+ {rest.title}
);
-};
+}
export default NavbarList;
diff --git a/src/components/common/navbar/constant.ts b/src/components/common/navbar/constant.ts
new file mode 100644
index 0000000..35c0f6f
--- /dev/null
+++ b/src/components/common/navbar/constant.ts
@@ -0,0 +1,17 @@
+import { NavbarListProps } from "./NavbarList";
+import { ValidPathnames } from "./type";
+
+export const LINK: NavbarListProps[] = [
+ {
+ id: "1",
+ href: "/about",
+ },
+ {
+ id: "2",
+ href: "/events",
+ },
+ {
+ id: "3",
+ href: "/contact",
+ },
+];
diff --git a/src/components/common/navbar/constants.tsx b/src/components/common/navbar/constants.tsx
deleted file mode 100644
index 27b9e20..0000000
--- a/src/components/common/navbar/constants.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { NavbarListType } from "./types";
-
-export const navbarLists: NavbarListType[] = [
- {
- title: "About",
- link: "/about",
- },
- {
- title: "Contact",
- link: "/contact",
- },
- {
- title: "Event",
- link: "/events",
- },
-];
diff --git a/src/components/common/navbar/index.tsx b/src/components/common/navbar/index.tsx
index cfe78ff..b8301df 100644
--- a/src/components/common/navbar/index.tsx
+++ b/src/components/common/navbar/index.tsx
@@ -1,10 +1,13 @@
import Link from "next/link";
import NavbarList from "./NavbarList";
-import { navbarLists } from "./constants";
-import { ModeToggle } from "../modetoggle";
+import { ModeToggle } from "../mode-toggle";
+import { useTranslations } from "next-intl";
+import LocaleToggle from "../locale-toggle";
+import { LINK } from "./constant";
const Navbar = () => {
+ const t = useTranslations("Layout");
return (