 messages/en.json                              |  46 +++++++++++
 messages/ja.json                              |  46 +++++++++++
 messages/translation-todo.json                | 106 ++++++++++++++++++++++++++
 next.config.mjs                               |   5 +-
 package.json                                  |   5 +-
 src/app/blog/[slug]/opengraph-image.tsx       |   4 +-
 src/app/blog/[slug]/page.tsx                  |   4 +-
 src/app/blog/page.tsx                         |   4 +-
 src/app/layout.tsx                            |   7 +-
 src/app/not-found.tsx                         |   7 +-
 src/app/page.tsx                              |  10 ++-
 src/components/mdx/code-block.tsx             |   4 +-
 src/components/navbar.tsx                     |   4 +-
 src/components/section/contact-section.tsx    |   6 +-
 src/components/section/hackathons-section.tsx |   6 +-
 src/components/section/projects-section.tsx   |  10 +--
 src/i18n/request.ts                           |  11 +++
 17 files changed, 260 insertions(+), 25 deletions(-)

diff --git a/messages/en.json b/messages/en.json
new file mode 100644
index 0000000..b327c25
--- /dev/null
+++ b/messages/en.json
@@ -0,0 +1,46 @@
+{
+  "code_block": {
+    "copy_code": "Copy code"
+  },
+  "contact_section": {
+    "contact": "Contact",
+    "get_in_touch": "Get in Touch"
+  },
+  "hackathons_section": {
+    "hackathons": "Hackathons",
+    "i_like_building_things": "I like building things"
+  },
+  "icons": {
+    "framer_motion": "Framer Motion",
+    "github": "GitHub",
+    "linkedin": "LinkedIn",
+    "next_js": "Next.js",
+    "react": "React",
+    "tailwind_css": "Tailwind CSS",
+    "x": "X",
+    "youtube": "youtube"
+  },
+  "navbar": {
+    "theme": "Theme"
+  },
+  "not_found": {
+    "page_not_found": "Page Not Found",
+    "the_page_you_apos_re_looking_for_doesn_a": "The page you&apos;re looking for doesn&apos;t exist or may have been moved."
+  },
+  "opengraph_image": {
+    "post_not_found": "Post Not Found"
+  },
+  "page": {
+    "about": "About",
+    "back_to_blog": "Back to Blog",
+    "education": "Education",
+    "my_thoughts_on_software_development_life": "My thoughts on software development, life, and more.",
+    "skills": "Skills",
+    "work_experience": "Work Experience"
+  },
+  "projects_section": {
+    "check_out_my_latest_work": "Check out my latest work",
+    "i_apos_ve_worked_on_a_variety_of_project": "I&apos;ve worked on a variety of projects, from simple websites to complex web applications. Here are a few of my favorites.",
+    "my_projects": "My Projects"
+  }
+}
diff --git a/messages/ja.json b/messages/ja.json
new file mode 100644
index 0000000..b327c25
--- /dev/null
+++ b/messages/ja.json
@@ -0,0 +1,46 @@
+{
+  "code_block": {
+    "copy_code": "Copy code"
+  },
+  "contact_section": {
+    "contact": "Contact",
+    "get_in_touch": "Get in Touch"
+  },
+  "hackathons_section": {
+    "hackathons": "Hackathons",
+    "i_like_building_things": "I like building things"
+  },
+  "icons": {
+    "framer_motion": "Framer Motion",
+    "github": "GitHub",
+    "linkedin": "LinkedIn",
+    "next_js": "Next.js",
+    "react": "React",
+    "tailwind_css": "Tailwind CSS",
+    "x": "X",
+    "youtube": "youtube"
+  },
+  "navbar": {
+    "theme": "Theme"
+  },
+  "not_found": {
+    "page_not_found": "Page Not Found",
+    "the_page_you_apos_re_looking_for_doesn_a": "The page you&apos;re looking for doesn&apos;t exist or may have been moved."
+  },
+  "opengraph_image": {
+    "post_not_found": "Post Not Found"
+  },
+  "page": {
+    "about": "About",
+    "back_to_blog": "Back to Blog",
+    "education": "Education",
+    "my_thoughts_on_software_development_life": "My thoughts on software development, life, and more.",
+    "skills": "Skills",
+    "work_experience": "Work Experience"
+  },
+  "projects_section": {
+    "check_out_my_latest_work": "Check out my latest work",
+    "i_apos_ve_worked_on_a_variety_of_project": "I&apos;ve worked on a variety of projects, from simple websites to complex web applications. Here are a few of my favorites.",
+    "my_projects": "My Projects"
+  }
+}
diff --git a/messages/translation-todo.json b/messages/translation-todo.json
new file mode 100644
index 0000000..638c509
--- /dev/null
+++ b/messages/translation-todo.json
@@ -0,0 +1,106 @@
+[
+  {
+    "key": "code_block.copy_code",
+    "en": "Copy code"
+  },
+  {
+    "key": "contact_section.contact",
+    "en": "Contact"
+  },
+  {
+    "key": "contact_section.get_in_touch",
+    "en": "Get in Touch"
+  },
+  {
+    "key": "hackathons_section.hackathons",
+    "en": "Hackathons"
+  },
+  {
+    "key": "hackathons_section.i_like_building_things",
+    "en": "I like building things"
+  },
+  {
+    "key": "icons.framer_motion",
+    "en": "Framer Motion"
+  },
+  {
+    "key": "icons.github",
+    "en": "GitHub"
+  },
+  {
+    "key": "icons.linkedin",
+    "en": "LinkedIn"
+  },
+  {
+    "key": "icons.next_js",
+    "en": "Next.js"
+  },
+  {
+    "key": "icons.react",
+    "en": "React"
+  },
+  {
+    "key": "icons.tailwind_css",
+    "en": "Tailwind CSS"
+  },
+  {
+    "key": "icons.x",
+    "en": "X"
+  },
+  {
+    "key": "icons.youtube",
+    "en": "youtube"
+  },
+  {
+    "key": "navbar.theme",
+    "en": "Theme"
+  },
+  {
+    "key": "not_found.page_not_found",
+    "en": "Page Not Found"
+  },
+  {
+    "key": "not_found.the_page_you_apos_re_looking_for_doesn_a",
+    "en": "The page you&apos;re looking for doesn&apos;t exist or may have been moved."
+  },
+  {
+    "key": "opengraph_image.post_not_found",
+    "en": "Post Not Found"
+  },
+  {
+    "key": "page.about",
+    "en": "About"
+  },
+  {
+    "key": "page.back_to_blog",
+    "en": "Back to Blog"
+  },
+  {
+    "key": "page.education",
+    "en": "Education"
+  },
+  {
+    "key": "page.my_thoughts_on_software_development_life",
+    "en": "My thoughts on software development, life, and more."
+  },
+  {
+    "key": "page.skills",
+    "en": "Skills"
+  },
+  {
+    "key": "page.work_experience",
+    "en": "Work Experience"
+  },
+  {
+    "key": "projects_section.check_out_my_latest_work",
+    "en": "Check out my latest work"
+  },
+  {
+    "key": "projects_section.i_apos_ve_worked_on_a_variety_of_project",
+    "en": "I&apos;ve worked on a variety of projects, from simple websites to complex web applications. Here are a few of my favorites."
+  },
+  {
+    "key": "projects_section.my_projects",
+    "en": "My Projects"
+  }
+]
diff --git a/next.config.mjs b/next.config.mjs
index 0581de9..0504517 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -1,3 +1,6 @@
+import createNextIntlPlugin from 'next-intl/plugin';
+const withNextIntl = createNextIntlPlugin();
+
 import { withContentCollections } from "@content-collections/next";
 
 /** @type {import('next').NextConfig} */
@@ -31,4 +34,4 @@ const nextConfig = {
 };
 
 // withContentCollections must be the outermost plugin
-export default withContentCollections(nextConfig);
+export default withNextIntl(withContentCollections(nextConfig));
diff --git a/package.json b/package.json
index 04f1848..3cc8f89 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,8 @@
     "tailwind-merge": "^3.4.0",
     "tailwindcss": "^4.1.18",
     "tailwindcss-animate": "^1.0.7",
-    "tw-animate-css": "^1.4.0"
+    "tw-animate-css": "^1.4.0",
+    "next-intl": "^4.0.0"
   },
   "devDependencies": {
     "@content-collections/core": "^0.13.1",
@@ -52,4 +53,4 @@
     "typescript": "^5.9.3",
     "zod": "^4.3.5"
   }
-}
\ No newline at end of file
+}
diff --git a/src/app/blog/[slug]/opengraph-image.tsx b/src/app/blog/[slug]/opengraph-image.tsx
index 0e587b7..861efc7 100644
--- a/src/app/blog/[slug]/opengraph-image.tsx
+++ b/src/app/blog/[slug]/opengraph-image.tsx
@@ -1,3 +1,4 @@
+import { getTranslations } from 'next-intl/server';
  
 
 import { ImageResponse } from "next/og";
@@ -126,6 +127,7 @@ export default async function Image({
 }: {
     params: Promise<{ slug: string }>;
 }) {
+  const t = await getTranslations();
     try {
         const fontData = await getFontData();
         const { slug } = await params;
@@ -146,7 +148,7 @@ export default async function Image({
                                     </div>
                                 )}
                                 <div style={styles.mainContainer}>
-                                    <div style={styles.title}>Post Not Found</div>
+                                    <div style={styles.title}>{t('opengraph_image.post_not_found')}</div>
                                 </div>
                             </div>
                         </div>
diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[slug]/page.tsx
index 7ff8a92..3f65aec 100644
--- a/src/app/blog/[slug]/page.tsx
+++ b/src/app/blog/[slug]/page.tsx
@@ -1,3 +1,4 @@
+import { getTranslations } from 'next-intl/server';
 import { allPosts } from "content-collections";
 import { formatDate } from "@/lib/utils";
 import { DATA } from "@/data/resume";
@@ -79,6 +80,7 @@ export default async function Blog({
     slug: string;
   }>;
 }) {
+  const t = await getTranslations();
   const { slug } = await params;
   const sortedPosts = getSortedPosts();
   const currentIndex = sortedPosts.findIndex(
@@ -123,7 +125,7 @@ export default async function Blog({
         }}
       />
       <div className="flex justify-start gap-4 items-center">
-        <Link href="/blog" className="text-sm text-muted-foreground hover:text-foreground transition-colors border border-border rounded-lg px-2 py-1 inline-flex items-center gap-1 mb-6 group" aria-label="Back to Blog">
+        <Link href="/blog" className="text-sm text-muted-foreground hover:text-foreground transition-colors border border-border rounded-lg px-2 py-1 inline-flex items-center gap-1 mb-6 group" aria-label={t('page.back_to_blog')}>
           <ChevronLeft className="size-3 group-hover:-translate-x-px transition-transform" />
           Back to Blog
         </Link>
diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx
index ce95b31..5787b91 100644
--- a/src/app/blog/page.tsx
+++ b/src/app/blog/page.tsx
@@ -1,3 +1,4 @@
+import { getTranslations } from 'next-intl/server';
 import BlurFade from "@/components/magicui/blur-fade";
 import { allPosts } from "content-collections";
 import Link from "next/link";
@@ -27,6 +28,7 @@ export default async function BlogPage({
 }: {
   searchParams: Promise<{ page?: string }>;
 }) {
+  const t = await getTranslations();
   const { page: pageParam } = await searchParams;
 
   const posts = allPosts;
@@ -49,7 +51,7 @@ export default async function BlogPage({
       <BlurFade delay={BLUR_FADE_DELAY}>
         <h1 className="text-2xl font-semibold tracking-tight mb-2">Blog <span className="ml-1 bg-card border border-border rounded-md px-2 py-1 text-muted-foreground text-sm">{sortedPosts.length} posts</span></h1>
         <p className="text-sm text-muted-foreground mb-8">
-          My thoughts on software development, life, and more.
+          {t('page.my_thoughts_on_software_development_life')}
         </p>
       </BlurFade>
 
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 7fe1c44..83f5aee 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,3 +1,5 @@
+import { NextIntlClientProvider } from 'next-intl';
+import { getMessages } from 'next-intl/server';
 import Navbar from "@/components/navbar";
 import { ThemeProvider } from "@/components/theme-provider";
 import { TooltipProvider } from "@/components/ui/tooltip";
@@ -56,11 +58,12 @@ export const metadata: Metadata = {
   },
 };
 
-export default function RootLayout({
+export default async function RootLayout({
   children,
 }: Readonly<{
   children: React.ReactNode;
 }>) {
+  const messages = await getMessages();
   return (
     <html lang="en" suppressHydrationWarning>
       <body
@@ -70,6 +73,7 @@ export default function RootLayout({
           geistMono.variable
         )}
       >
+        <NextIntlClientProvider messages={messages}>
         <ThemeProvider attribute="class" defaultTheme="light">
           <TooltipProvider delayDuration={0}>
             <div className="absolute inset-0 top-0 left-0 right-0 h-[100px] overflow-hidden z-0">
@@ -89,6 +93,7 @@ export default function RootLayout({
             <Navbar />
           </TooltipProvider>
         </ThemeProvider>
+      </NextIntlClientProvider>
       </body>
     </html>
   );
diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx
index e0b25d6..4f19fc8 100644
--- a/src/app/not-found.tsx
+++ b/src/app/not-found.tsx
@@ -1,9 +1,11 @@
+import { useTranslations } from 'next-intl';
 import Link from "next/link";
 import { Home } from "lucide-react";
 
 import { Button } from "@/components/ui/button";
 
 export default function NotFound() {
+  const t = useTranslations();
     return (
         <div className="min-h-[calc(100vh-12rem)] flex flex-col">
             <div className="flex-1 flex items-center justify-center p-8">
@@ -12,11 +14,10 @@ export default function NotFound() {
                         404
                     </h1>
                     <h2 className="text-4xl tracking-tight font-semibold text-foreground mb-2">
-                        Page Not Found
+                        {t('not_found.page_not_found')}
                     </h2>
                     <p className="text-muted-foreground mb-8 text-balance tracking-tight font-medium">
-                        The page you&apos;re looking for doesn&apos;t exist or may have been
-                        moved.
+                        {t('not_found.the_page_you_apos_re_looking_for_doesn_a')}
                     </p>
                     <div className="flex flex-col sm:flex-row gap-3">
                         <Link href="/">
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 7e8ec4d..8c92262 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -1,3 +1,4 @@
+import { useTranslations } from 'next-intl';
 /* eslint-disable @next/next/no-img-element */
 import BlurFade from "@/components/magicui/blur-fade";
 import BlurFadeText from "@/components/magicui/blur-fade-text";
@@ -14,6 +15,7 @@ import { ArrowUpRight } from "lucide-react";
 const BLUR_FADE_DELAY = 0.04;
 
 export default function Page() {
+  const t = useTranslations();
   return (
     <main className="min-h-dvh flex flex-col gap-14 relative">
       <section id="hero">
@@ -44,7 +46,7 @@ export default function Page() {
       <section id="about">
         <div className="flex min-h-0 flex-col gap-y-4">
           <BlurFade delay={BLUR_FADE_DELAY * 3}>
-            <h2 className="text-xl font-bold">About</h2>
+            <h2 className="text-xl font-bold">{t('page.about')}</h2>
           </BlurFade>
           <BlurFade delay={BLUR_FADE_DELAY * 4}>
             <div className="prose max-w-full text-pretty font-sans leading-relaxed text-muted-foreground dark:prose-invert">
@@ -58,7 +60,7 @@ export default function Page() {
       <section id="work">
         <div className="flex min-h-0 flex-col gap-y-6">
           <BlurFade delay={BLUR_FADE_DELAY * 5}>
-            <h2 className="text-xl font-bold">Work Experience</h2>
+            <h2 className="text-xl font-bold">{t('page.work_experience')}</h2>
           </BlurFade>
           <BlurFade delay={BLUR_FADE_DELAY * 6}>
             <WorkSection />
@@ -68,7 +70,7 @@ export default function Page() {
       <section id="education">
         <div className="flex min-h-0 flex-col gap-y-6">
           <BlurFade delay={BLUR_FADE_DELAY * 7}>
-            <h2 className="text-xl font-bold">Education</h2>
+            <h2 className="text-xl font-bold">{t('page.education')}</h2>
           </BlurFade>
           <div className="flex flex-col gap-8">
             {DATA.education.map((education, index) => (
@@ -116,7 +118,7 @@ export default function Page() {
       <section id="skills">
         <div className="flex min-h-0 flex-col gap-y-4">
           <BlurFade delay={BLUR_FADE_DELAY * 9}>
-            <h2 className="text-xl font-bold">Skills</h2>
+            <h2 className="text-xl font-bold">{t('page.skills')}</h2>
           </BlurFade>
           <div className="flex flex-wrap gap-2">
             {DATA.skills.map((skill, id) => (
diff --git a/src/components/mdx/code-block.tsx b/src/components/mdx/code-block.tsx
index 654150d..ebdec41 100644
--- a/src/components/mdx/code-block.tsx
+++ b/src/components/mdx/code-block.tsx
@@ -1,4 +1,5 @@
 "use client";
+import { useTranslations } from 'next-intl';
 
 import { useState, useRef, useEffect, type ComponentProps } from "react";
 import { Copy, Check } from "lucide-react";
@@ -15,6 +16,7 @@ function extractLanguage(className?: string): string {
 }
 
 export function CodeBlock({ children, ...props }: CodeBlockProps) {
+  const t = useTranslations();
   const [copied, setCopied] = useState(false);
   const [{ html, className, title }, setRenderState] = useState<{
     html: string;
@@ -85,7 +87,7 @@ export function CodeBlock({ children, ...props }: CodeBlockProps) {
           variant="outline"
           size="icon"
           className={cn("absolute size-8 text-primary cursor-pointer right-3 opacity-100 lg:opacity-0 lg:group-hover:opacity-100 transition-opacity rounded-md border border-border shadow-none", title ? "top-13" : "top-3", props.className)}
-          aria-label="Copy code"
+          aria-label={t('code_block.copy_code')}
         >
           {copied ? <Check className="size-4" /> : <Copy className="size-4" />}
         </Button>
diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx
index cb0034f..9064308 100644
--- a/src/components/navbar.tsx
+++ b/src/components/navbar.tsx
@@ -1,3 +1,4 @@
+import { useTranslations } from 'next-intl';
 import { Dock, DockIcon } from "@/components/magicui/dock";
 import { ModeToggle } from "@/components/mode-toggle";
 import { Separator } from "@/components/ui/separator";
@@ -10,6 +11,7 @@ import {
 import { DATA } from "@/data/resume";
 
 export default function Navbar() {
+  const t = useTranslations();
   return (
     <div className="pointer-events-none fixed inset-x-0 bottom-4 z-30">
       <Dock className="z-50 pointer-events-auto relative h-14 p-2 w-fit mx-auto flex gap-2 border bg-card/90 backdrop-blur-3xl shadow-[0_0_10px_3px] shadow-primary/5">
@@ -87,7 +89,7 @@ export default function Navbar() {
             sideOffset={8}
             className="rounded-xl bg-primary text-primary-foreground px-4 py-2 text-sm shadow-[0_10px_40px_-10px_rgba(0,0,0,0.3)] dark:shadow-[0_10px_40px_-10px_rgba(0,0,0,0.5)]"
           >
-            <p>Theme</p>
+            <p>{t('navbar.theme')}</p>
             <TooltipArrow className="fill-primary" />
           </TooltipContent>
         </Tooltip>
diff --git a/src/components/section/contact-section.tsx b/src/components/section/contact-section.tsx
index 7574e21..71d8cbf 100644
--- a/src/components/section/contact-section.tsx
+++ b/src/components/section/contact-section.tsx
@@ -1,12 +1,14 @@
+import { useTranslations } from 'next-intl';
 import Link from "next/link";
 import { FlickeringGrid } from "@/components/magicui/flickering-grid";
 import { DATA } from "@/data/resume";
 
 export default function ContactSection() {
+  const t = useTranslations();
   return (
     <div className="border rounded-xl p-10 relative">
       <div className="absolute -top-4 border bg-primary z-10 rounded-xl px-4 py-1 left-1/2 -translate-x-1/2">
-        <span className="text-background text-sm font-medium">Contact</span>
+        <span className="text-background text-sm font-medium">{t('contact_section.contact')}</span>
       </div>
       <div className="absolute inset-0 top-0 left-0 right-0 h-1/2 rounded-xl overflow-hidden">
         <FlickeringGrid
@@ -21,7 +23,7 @@ export default function ContactSection() {
       </div>
       <div className="relative flex flex-col items-center gap-4 text-center">
         <h2 className="text-3xl font-bold tracking-tighter sm:text-5xl">
-          Get in Touch
+          {t('contact_section.get_in_touch')}
         </h2>
         <p className="mx-auto max-w-lg text-muted-foreground text-balance">
           Want to chat? Just shoot me a dm{" "}
diff --git a/src/components/section/hackathons-section.tsx b/src/components/section/hackathons-section.tsx
index 93b2d8a..5821c2c 100644
--- a/src/components/section/hackathons-section.tsx
+++ b/src/components/section/hackathons-section.tsx
@@ -1,3 +1,4 @@
+import { useTranslations } from 'next-intl';
 /* eslint-disable @next/next/no-img-element */
 import { Badge } from "@/components/ui/badge";
 import Link from "next/link";
@@ -5,6 +6,7 @@ import { DATA } from "@/data/resume";
 import { Timeline, TimelineItem, TimelineConnectItem } from "@/components/timeline";
 
 export default function HackathonsSection() {
+  const t = useTranslations();
   return (
     <section id="hackathons" className="overflow-hidden">
       <div className="flex min-h-0 flex-col gap-y-8 w-full">
@@ -12,12 +14,12 @@ export default function HackathonsSection() {
           <div className="flex items-center w-full">
             <div className="flex-1 h-px bg-linear-to-r from-transparent from-5% via-border via-95% to-transparent" />
             <div className="border bg-primary z-10 rounded-xl px-4 py-1">
-              <span className="text-background text-sm font-medium">Hackathons</span>
+              <span className="text-background text-sm font-medium">{t('hackathons_section.hackathons')}</span>
             </div>
             <div className="flex-1 h-px bg-linear-to-l from-transparent from-5% via-border via-95% to-transparent" />
           </div>
           <div className="flex flex-col gap-y-3 items-center justify-center">
-            <h2 className="text-3xl font-bold tracking-tighter sm:text-4xl">I like building things</h2>
+            <h2 className="text-3xl font-bold tracking-tighter sm:text-4xl">{t('hackathons_section.i_like_building_things')}</h2>
             <p className="text-muted-foreground md:text-lg/relaxed lg:text-base/relaxed xl:text-lg/relaxed text-balance text-center">
               During my time in university, I attended {DATA.hackathons.length}+
               hackathons. People from around the country would come together and
diff --git a/src/components/section/projects-section.tsx b/src/components/section/projects-section.tsx
index b51c9d0..3909a55 100644
--- a/src/components/section/projects-section.tsx
+++ b/src/components/section/projects-section.tsx
@@ -1,3 +1,4 @@
+import { useTranslations } from 'next-intl';
 import BlurFade from "@/components/magicui/blur-fade";
 import { ProjectCard } from "@/components/project-card";
 import { DATA } from "@/data/resume";
@@ -5,6 +6,7 @@ import { DATA } from "@/data/resume";
 const BLUR_FADE_DELAY = 0.04;
 
 export default function ProjectsSection() {
+  const t = useTranslations();
     return (
         <section id="projects">
             <div className="flex min-h-0 flex-col gap-y-8">
@@ -15,7 +17,7 @@ export default function ProjectsSection() {
 
                         />
                         <div className="border bg-primary z-10 rounded-xl px-4 py-1">
-                            <span className="text-background text-sm font-medium">My Projects</span>
+                            <span className="text-background text-sm font-medium">{t('projects_section.my_projects')}</span>
                         </div>
                         <div
                             className="flex-1 h-px bg-linear-to-l from-transparent from-5% via-border via-95% to-transparent"
@@ -23,11 +25,9 @@ export default function ProjectsSection() {
                         />
                     </div>
                     <div className="flex flex-col gap-y-3 items-center justify-center">
-                        <h2 className="text-3xl font-bold tracking-tighter sm:text-4xl">Check out my latest work</h2>
+                        <h2 className="text-3xl font-bold tracking-tighter sm:text-4xl">{t('projects_section.check_out_my_latest_work')}</h2>
                         <p className="text-muted-foreground md:text-lg/relaxed lg:text-base/relaxed xl:text-lg/relaxed text-balance text-center">
-                            I&apos;ve worked on a variety of projects, from simple
-                            websites to complex web applications. Here are a few of my
-                            favorites.
+                            {t('projects_section.i_apos_ve_worked_on_a_variety_of_project')}
                         </p>
                     </div>
                 </div>
diff --git a/src/i18n/request.ts b/src/i18n/request.ts
new file mode 100644
index 0000000..eb85a4b
--- /dev/null
+++ b/src/i18n/request.ts
@@ -0,0 +1,11 @@
+import { getRequestConfig } from 'next-intl/server';
+
+// Single-locale setup (no locale routing): the agent localizes the code and seeds the
+// messages; negotiating / switching the active locale is left to the team.
+export default getRequestConfig(async () => {
+  const locale = 'en';
+  return {
+    locale,
+    messages: (await import(`../../messages/${locale}.json`)).default,
+  };
+});
