diff --git a/README.md b/README.md
index 5e3271df..16b06154 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,105 @@ bun run dev
 
 Note: I added [remark-mermaid](https://github.com/temando/remark-mermaid) into utils and slightly modified it so that it is always simple mode and always returns a `pre` + `<div class="mermaid"></div>`. This allows mermaid code blocks to be included.
 
+# Adding a New Language
+
+UI strings live in `src/i18n/locales/` and are served via a pure Preact context — no external i18n library required. Adding a new locale takes 3 steps:
+
+## 1. Create the locale file
+
+Copy `src/i18n/locales/en.ts` to a new file, e.g. `src/i18n/locales/es.ts`. Translate all string values. The key structure must stay identical.
+
+```ts
+// src/i18n/locales/es.ts
+const es = {
+  nav: { home: "Inicio", blog: "Blog", portfolio: "Portafolio" },
+  hero: { prefix: "Construyo ", highlight: "apps increíbles", ... },
+  // ... all other keys translated
+} as const;
+
+export default es;
+```
+
+## 2. Register it in the context
+
+In `src/i18n/context.tsx`, import the new locale and add it to the `locales` map:
+
+```ts
+import en from "~/i18n/locales/en";
+import es from "~/i18n/locales/es"; // add
+
+const locales: Record<string, Translation> = { en, es }; // add es
+```
+
+## 3. Add it to Astro and create the page
+
+In `astro.config.mjs`, add the locale code to the `locales` array:
+
+```js
+i18n: {
+  locales: ["en", "es"], // add "es"
+  defaultLocale: "en",
+  routing: { prefixDefaultLocale: false },
+},
+```
+
+Then create `src/pages/es/index.astro` (mirroring `src/pages/index.astro`). Astro will set `Astro.currentLocale` to `"es"` for that page, which flows through the layout's `locale` prop into `I18nProvider` — all components then resolve strings from the new locale automatically.
+
+> **Note on islands**: All home-page sections are grouped inside `<HomeSections locale={...} client:visible />`, which wraps them in a single `I18nProvider`. Each Astro `client:*` island is an isolated Preact tree, so any new island components that use `useTranslation()` must also receive `locale` and render inside an `I18nProvider`.
+
+## Non-standard / fun locales
+
+For locales that aren't real BCP 47 language codes (e.g. a "backward" locale that reverses all strings for testing), use Astro's locale **object syntax** to decouple the URL path from the `lang` attribute:
+
+```js
+// astro.config.mjs
+i18n: {
+  locales: [
+    "en",
+    { path: "backward", codes: ["en-x-backward"] },
+  ],
+  defaultLocale: "en",
+  routing: { prefixDefaultLocale: false },
+},
+```
+
+- **`path`** — the URL segment (`/backward/`)
+- **`codes`** — what goes in `<html lang="...">`. Using `en-x-backward` is a valid BCP 47 private-use extension: screen readers treat it as English while still accurately describing the variant.
+
+Because `Astro.currentLocale` resolves to the first entry in `codes` (`"en-x-backward"`), you must pass `locale` explicitly in the page rather than relying on `Astro.currentLocale`:
+
+```astro
+<!-- src/pages/backward/index.astro -->
+<DefaultPageLayout description={description}>
+  <HomeSections locale="backward" ... client:visible />
+</DefaultPageLayout>
+```
+
+Then register `"backward"` in `src/i18n/context.tsx` alongside its locale file:
+
+```ts
+import backward from "~/i18n/locales/backward";
+const locales: Record<string, Translation> = { en, backward };
+```
+
+## TODO: Scale with `getStaticPaths`
+
+The current approach (one folder per locale, e.g. `src/pages/es/index.astro`) works but doesn't scale — each new locale requires duplicating every page file.
+
+The better long-term solution is to use a dynamic `[locale]` route with `getStaticPaths` so a single file generates all locale variants:
+
+```astro
+---
+// src/pages/[locale]/index.astro
+export function getStaticPaths() {
+  return [{ params: { locale: "es" } }, { params: { locale: "fr" } }];
+}
+const { locale } = Astro.params;
+---
+```
+
+This should be done when adding a second language for real, so the routing stays maintainable.
+
 # Outputs
 
 - `dist` - this is the output of the build
diff --git a/astro.config.mjs b/astro.config.mjs
index 4dee7017..c33c7bc4 100644
--- a/astro.config.mjs
+++ b/astro.config.mjs
@@ -16,4 +16,11 @@ export default defineConfig({
     remarkPlugins: [remarkGfm, remarkSmartypants, remarkMermaid],
   },
   integrations: [mdx(), unoCss(), preact()],
+  i18n: {
+    locales: ["en"],
+    defaultLocale: "en",
+    routing: {
+      prefixDefaultLocale: false,
+    },
+  },
 });
diff --git a/package.json b/package.json
index 2d1bc68f..0d715845 100644
--- a/package.json
+++ b/package.json
@@ -1,20 +1,6 @@
 {
   "name": "hrgui",
   "version": "2026.03.22",
-  "description": "Portfolio website",
-  "private": true,
-  "scripts": {
-    "dev": "astro dev --port 3000",
-    "start": "astro dev",
-    "build": "astro build",
-    "preview": "astro preview",
-    "format": "prettier --write \"src/**/*\"",
-    "test": "vitest",
-    "test-ui": "vitest --ui",
-    "lint:es": "eslint src",
-    "lint": "yarn lint:es && tsc",
-    "prepare": "husky"
-  },
   "devDependencies": {
     "@astrojs/mdx": "^5.0.2",
     "@astrojs/preact": "5.0.2",
@@ -66,9 +52,22 @@
     "vite-tsconfig-paths": "^6.1.1",
     "vitest": "^4.1.0"
   },
+  "description": "Portfolio website",
   "overrides": {
     "preact": "^10.25.1",
     "@preact/preset-vite": "^2.9.3"
   },
-  "dependencies": {}
+  "private": true,
+  "scripts": {
+    "dev": "astro dev --port 3000",
+    "start": "astro dev",
+    "build": "astro build",
+    "preview": "astro preview",
+    "format": "prettier --write \"src/**/*\"",
+    "test": "vitest",
+    "test-ui": "vitest --ui",
+    "lint:es": "eslint src",
+    "lint": "yarn lint:es && tsc",
+    "prepare": "husky"
+  }
 }
diff --git a/src/components/app/AppLayout.tsx b/src/components/app/AppLayout.tsx
index 3d6b3706..9c00f1de 100644
--- a/src/components/app/AppLayout.tsx
+++ b/src/components/app/AppLayout.tsx
@@ -1,5 +1,6 @@
 import type { ComponentChildren } from "preact";
 
+import { I18nProvider } from "~/i18n/context";
 import Footer from "./Footer";
 import Header from "./Header";
 
@@ -7,15 +8,18 @@ interface Props {
   children?: ComponentChildren;
   currentUrl?: string;
   currentPathName?: string;
+  locale?: string;
 }
 
-const AppLayout = ({ children, currentPathName }: Props) => {
+const AppLayout = ({ children, currentPathName, locale = "en" }: Props) => {
   return (
-    <div className="bg-background font-body text-on-background">
-      <Header currentPathName={currentPathName} />
-      <div className="min-h-screen">{children}</div>
-      <Footer />
-    </div>
+    <I18nProvider locale={locale}>
+      <div className="bg-background font-body text-on-background">
+        <Header currentPathName={currentPathName} />
+        <div className="min-h-screen">{children}</div>
+        <Footer />
+      </div>
+    </I18nProvider>
   );
 };
 
diff --git a/src/components/app/AppSocialMedia.tsx b/src/components/app/AppSocialMedia.tsx
index 1515906f..1e1922ad 100644
--- a/src/components/app/AppSocialMedia.tsx
+++ b/src/components/app/AppSocialMedia.tsx
@@ -1,4 +1,5 @@
 import classNames from "classnames";
+import { useTranslation } from "~/i18n/context";
 
 import Github from "~/components/icons/Github";
 import LinkedIn from "~/components/icons/LinkedIn";
@@ -9,11 +10,12 @@ type Props = {
 };
 
 const AppSocialMedia = ({ className }: Props) => {
+  const { t } = useTranslation();
   return (
     <div className={classNames("flex gap-2", className)}>
       <a
-        title="View My GitHub Profile"
-        aria-label="View My GitHub Profile"
+        title={t("social.github")}
+        aria-label={t("social.github")}
         href={GITHUB_URL}
         target="_blank"
         rel="noopener noreferrer"
@@ -22,8 +24,8 @@ const AppSocialMedia = ({ className }: Props) => {
         <Github aria-hidden="true" />
       </a>
       <a
-        title="View My LinkedIn Profile"
-        aria-label="View My LinkedIn Profile"
+        title={t("social.linkedin")}
+        aria-label={t("social.linkedin")}
         href={LINKEDIN_URL}
         target="_blank"
         rel="noopener noreferrer"
diff --git a/src/components/app/Footer.tsx b/src/components/app/Footer.tsx
index 032f8798..40e8addf 100644
--- a/src/components/app/Footer.tsx
+++ b/src/components/app/Footer.tsx
@@ -1,3 +1,5 @@
+import { useTranslation } from "~/i18n/context";
+
 import Link from "~/components/layout/Link";
 import LinkButton from "~/components/layout/LinkButton";
 
@@ -11,6 +13,8 @@ const footerBackToTopClassName =
   "font-mono text-sm uppercase tracking-[0.16em] text-primary transition-all duration-150 ease-out hover:text-primary-container hover:underline hover:decoration-2 hover:underline-offset-4 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/70 focus-visible:ring-offset-2 focus-visible:ring-offset-surface-container-high active:opacity-80";
 
 const Footer = () => {
+  const { t } = useTranslation();
+
   function handleBackToTop() {
     window.scrollTo({ top: 0, behavior: "smooth" });
   }
@@ -25,22 +29,19 @@ const Footer = () => {
           <div>
             <Logo />
             <p className="prose prose-sm dark:text-gray-200 mt-4 mb-4">
-              Harman Goei (hrgui) is a developer that loves to make cool and
-              awesome web applications. His strength is in HTML, CSS,
-              JavaScript, but he is willing to code anywhere in the stack to
-              make the web be awesome.
+              {t("footer.bio")}
             </p>
           </div>
           <div className="mb-4 mt-4">
             <nav className="flex flex-col gap-1">
               <Link href="/" className={footerLinkClassName}>
-                Home
+                {t("nav.home")}
               </Link>
               <Link href="/posts" className={footerLinkClassName}>
-                Blog
+                {t("nav.blog")}
               </Link>
               <Link href="/portfolio" className={footerLinkClassName}>
-                Portfolio
+                {t("nav.portfolio")}
               </Link>
             </nav>
           </div>
@@ -49,7 +50,7 @@ const Footer = () => {
       <div className="p-6 bg-gray-200 dark:bg-neutral-800 border-t-2 border-gray-300 dark:border-neutral-700 dark:text-gray-200">
         <div className="container mx-auto flex justify-between">
           <div className="font-mono text-sm uppercase tracking-[0.12em] text-on-surface-muted">
-            &copy; {new Date().getFullYear()} Harman Goei
+            {t("footer.copyright", { year: new Date().getFullYear() })}
           </div>
           <AppSocialMedia />
           <div>
@@ -57,7 +58,7 @@ const Footer = () => {
               onClick={handleBackToTop}
               className={footerBackToTopClassName}
             >
-              back to top?
+              {t("footer.backToTop")}
             </LinkButton>
           </div>
         </div>
diff --git a/src/components/app/Header.tsx b/src/components/app/Header.tsx
index 71bee1de..6947c134 100644
--- a/src/components/app/Header.tsx
+++ b/src/components/app/Header.tsx
@@ -1,5 +1,6 @@
 import classNames from "classnames";
 import { useState } from "preact/hooks";
+import { useTranslation } from "~/i18n/context";
 
 import useScrollTrigger from "~/hooks/useScrollTrigger";
 
@@ -16,6 +17,7 @@ type Props = {
 };
 
 const Header = ({ currentPathName }: Props) => {
+  const { t } = useTranslation();
   const [isOpen, setisOpen] = useState(false);
   const handleSetIsOpen = () => setisOpen(!isOpen);
   const trigger = useScrollTrigger({
@@ -26,17 +28,17 @@ const Header = ({ currentPathName }: Props) => {
   // NOTE: this needs a key because of use in the Drawer
   const links = [
     <NavLink currentPathName={currentPathName} key="home" href="/" exact>
-      Home
+      {t("nav.home")}
     </NavLink>,
     <NavLink currentPathName={currentPathName} key="blog" href="/posts">
-      Blog
+      {t("nav.blog")}
     </NavLink>,
     <NavLink
       currentPathName={currentPathName}
       key="portfolio"
       href="/portfolio"
     >
-      Portfolio
+      {t("nav.portfolio")}
     </NavLink>,
   ];
 
diff --git a/src/components/app/blog/BlogSubHeader.tsx b/src/components/app/blog/BlogSubHeader.tsx
index 8d0e9994..f0c43901 100644
--- a/src/components/app/blog/BlogSubHeader.tsx
+++ b/src/components/app/blog/BlogSubHeader.tsx
@@ -1,3 +1,5 @@
+import { useTranslation } from "~/i18n/context";
+
 import { toDisplayDate } from "./utils";
 
 type Props = {
@@ -8,6 +10,7 @@ type Props = {
 };
 
 const BlogSubHeader = ({ hidden, date, title, excerpt }: Props) => {
+  const { t } = useTranslation();
   return (
     <section className="circuit-board-bg relative overflow-hidden px-6 pb-8 pt-28">
       <div className="relative z-10 container mx-auto max-w-[1536px]">
@@ -15,10 +18,12 @@ const BlogSubHeader = ({ hidden, date, title, excerpt }: Props) => {
           <div className="mb-8 flex items-center gap-4">
             <div className="flex items-center gap-2 whitespace-nowrap font-mono text-sm text-primary bg-primary/10 px-2 py-1 rounded tracking-widest">
               <span className={"uppercase"}>{toDisplayDate(date)} //</span>
-              <span className="text-primary">ENTRY_RECORD</span>
+              <span className="text-primary">
+                {t("blog.subHeader.entryRecord")}
+              </span>
               {hidden && process.env.NODE_ENV === "development" && (
                 <span className="rounded bg-tertiary/18 px-2 py-1 text-xs font-semibold uppercase tracking-[0.16em] text-tertiary">
-                  Hidden Draft
+                  {t("blog.subHeader.hiddenDraft")}
                 </span>
               )}
             </div>
@@ -29,14 +34,13 @@ const BlogSubHeader = ({ hidden, date, title, excerpt }: Props) => {
         <div className="mb-8">
           {!date && hidden && process.env.NODE_ENV === "development" && (
             <div className="mb-5 inline-flex rounded bg-tertiary/18 px-2 py-1 font-mono text-xs font-semibold uppercase tracking-[0.16em] text-tertiary">
-              Hidden Draft
+              {t("blog.subHeader.hiddenDraft")}
             </div>
           )}
 
           {hidden && process.env.NODE_ENV === "development" && (
             <div className="mb-6 max-w-3xl rounded-2xl border border-tertiary/40 bg-tertiary/10 px-4 py-3 text-sm text-on-surface">
-              You are looking at a hidden post. Remove `hidden: true` or set it
-              to `false` to publish this post.
+              {t("blog.subHeader.hiddenWarning")}
             </div>
           )}
 
diff --git a/src/components/app/blog/Posts.tsx b/src/components/app/blog/Posts.tsx
index 6ecc83fd..314b4b87 100644
--- a/src/components/app/blog/Posts.tsx
+++ b/src/components/app/blog/Posts.tsx
@@ -1,4 +1,5 @@
 import classNames from "classnames";
+import { useTranslation } from "~/i18n/context";
 
 import { type Frontmatter } from "~/types/frontmatter";
 
@@ -15,6 +16,7 @@ const toNodeId = (index: number) =>
   `SYSLOG_${String(index + 1).padStart(2, "0")}`;
 
 const Posts = ({ posts }: Props) => {
+  const { t } = useTranslation();
   const visiblePosts =
     posts?.filter(
       (post) => !(post.hidden && process.env.NODE_ENV !== "development")
@@ -38,7 +40,7 @@ const Posts = ({ posts }: Props) => {
                 <div className="inline-flex items-center gap-3">
                   {isNew(featuredPost.date) && (
                     <span className="rounded bg-secondary/18 px-2 py-1 font-mono text-xs font-semibold uppercase tracking-[0.16em] text-secondary">
-                      New
+                      {t("blog.posts.newBadge")}
                     </span>
                   )}
                   <p className="font-mono text-sm text-on-surface-muted">
@@ -65,7 +67,7 @@ const Posts = ({ posts }: Props) => {
               </p>
               <div className="mt-auto flex items-center justify-end pt-12">
                 <span className="font-mono text-sm uppercase tracking-[0.16em] text-primary transition-transform group-hover:translate-x-0.5 group-focus-visible:translate-x-0.5">
-                  Execute_Read -&gt;
+                  {t("blog.posts.executeRead")}
                 </span>
               </div>
             </a>
@@ -92,7 +94,7 @@ const Posts = ({ posts }: Props) => {
                   {remainingPosts[0].excerpt}
                 </p>
                 <p className="mt-auto pt-8 text-right font-mono text-sm uppercase tracking-[0.16em] text-primary transition-transform group-hover:translate-x-0.5 group-focus-visible:translate-x-0.5">
-                  Read_More -&gt;
+                  {t("blog.posts.readMore")}
                 </p>
               </a>
             )}
@@ -134,7 +136,7 @@ const Posts = ({ posts }: Props) => {
                     {post.excerpt}
                   </p>
                   <p className="mt-auto pt-8 text-right font-mono text-sm uppercase tracking-[0.16em] text-primary transition-transform group-hover:translate-x-0.5 group-focus-visible:translate-x-0.5">
-                    Read_More -&gt;
+                    {t("blog.posts.readMore")}
                   </p>
                 </a>
               );
@@ -144,7 +146,7 @@ const Posts = ({ posts }: Props) => {
 
         {visiblePosts.length === 0 && (
           <div className="rounded-2xl border border-outline-variant bg-surface-container-low px-7 py-8 text-on-surface-muted">
-            No posts available.
+            {t("blog.posts.noPosts")}
           </div>
         )}
       </div>
diff --git a/src/components/app/home/HomeSections.tsx b/src/components/app/home/HomeSections.tsx
new file mode 100644
index 00000000..59baecda
--- /dev/null
+++ b/src/components/app/home/HomeSections.tsx
@@ -0,0 +1,33 @@
+import { I18nProvider } from "~/i18n/context";
+import { type PortfolioFrontmatter } from "~/types/frontmatter";
+
+import { Hero } from "./sections/Hero";
+import { ThreeSellPoints } from "./sections/ThreeSellPoints";
+import { TechnicalSkills } from "./sections/TechnicalSkills";
+import { Education } from "./sections/Education";
+import { PortfolioShowcase } from "~/components/app/portfolio/PortfolioShowcase";
+
+interface Props {
+  locale?: string;
+  items: PortfolioFrontmatter[];
+  containerClassName?: string;
+}
+
+export function HomeSections({
+  locale = "en",
+  items,
+  containerClassName,
+}: Props) {
+  return (
+    <I18nProvider locale={locale}>
+      <Hero />
+      <ThreeSellPoints />
+      <TechnicalSkills />
+      <Education />
+      <PortfolioShowcase
+        containerClassName={containerClassName}
+        items={items}
+      />
+    </I18nProvider>
+  );
+}
diff --git a/src/components/app/home/sections/Education.test.tsx b/src/components/app/home/sections/Education.test.tsx
index 92c22546..d25b3cb4 100644
--- a/src/components/app/home/sections/Education.test.tsx
+++ b/src/components/app/home/sections/Education.test.tsx
@@ -18,12 +18,12 @@ test("should be able to render passed in education", () => {
           key: "pcc",
           imgSrc: "/images/pcc.png",
           url: "https://pasadena.edu/",
-          title: "Pasadena City College",
-          description: "",
           timeframe: { start: 2008, end: 2010 },
         },
       ]}
     />
   );
-  expect(screen.getByText(/Pasadena City College/)).toBeInTheDocument();
+  const links = screen.getAllByRole("link");
+  expect(links[0]).toHaveAttribute("href", "https://pasadena.edu/");
+  expect(screen.getByText(/2008/)).toBeInTheDocument();
 });
diff --git a/src/components/app/home/sections/Education.tsx b/src/components/app/home/sections/Education.tsx
index 7234777c..92f8d868 100644
--- a/src/components/app/home/sections/Education.tsx
+++ b/src/components/app/home/sections/Education.tsx
@@ -1,3 +1,5 @@
+import { useTranslation } from "~/i18n/context";
+
 import { education as defaultEducation } from "~/constants";
 
 export function Education({
@@ -5,6 +7,7 @@ export function Education({
 }: {
   education?: typeof defaultEducation;
 }) {
+  const { t } = useTranslation();
   return (
     <section
       className="bg-surface px-6 pb-12 pt-8"
@@ -12,20 +15,16 @@ export function Education({
     >
       <div className="container mx-auto">
         <div className="rounded-3xl border border-outline-variant bg-surface-container-low p-6 sm:p-8">
-          <p className="label-mono mb-2 text-primary">module_02 // education</p>
+          <p className="label-mono mb-2 text-primary">
+            {t("home.education.moduleLabel")}
+          </p>
           <h1 className="font-headline text-4xl font-semibold text-on-surface">
-            Education
+            {t("home.education.heading")}
           </h1>
           <div className="mt-6 space-y-6">
             {education?.map(
-              ({
-                key,
-                imgSrc,
-                url,
-                title,
-                description,
-                timeframe: { start, end },
-              }) => {
+              ({ key, imgSrc, url, timeframe: { start, end } }) => {
+                const title = t(`home.education.${key}.title`);
                 return (
                   <article
                     key={key}
@@ -39,7 +38,7 @@ export function Education({
                       >
                         <img
                           loading="lazy"
-                          alt={`Open ${title} in a new tab`}
+                          alt={t("home.education.imgAlt", { title })}
                           src={imgSrc}
                           className="h-20 w-20 rounded-lg border border-outline-variant object-contain p-2"
                         />
@@ -50,7 +49,7 @@ export function Education({
                         {title}
                       </h3>
                       <p className="text-on-surface-muted tracking">
-                        {description}
+                        {t(`home.education.${key}.description`)}
                       </p>
                       <p className="mt-2 font-mono text-sm uppercase tracking-[0.16em] text-primary">
                         {start} - {end}
diff --git a/src/components/app/home/sections/Hero.tsx b/src/components/app/home/sections/Hero.tsx
index 31a8234b..3f5396c5 100644
--- a/src/components/app/home/sections/Hero.tsx
+++ b/src/components/app/home/sections/Hero.tsx
@@ -1,23 +1,26 @@
 import { useEffect, useRef, useState } from "preact/hooks";
+import { useTranslation } from "~/i18n/context";
 
 import AppSocialMedia from "~/components/app/AppSocialMedia";
 
 const HERO_COMMAND_PROMPT = "$";
-const HERO_COMMAND_BODY = " about";
-const HERO_COMMAND_TEXT = `${HERO_COMMAND_PROMPT}${HERO_COMMAND_BODY}`;
-const HERO_PREFIX = "I build ";
-const HERO_HIGHLIGHT = "cool and awesome";
-const HERO_SUFFIX = "web and mobile apps.";
-const HERO_TEXT = `${HERO_PREFIX}${HERO_HIGHLIGHT}${HERO_SUFFIX}`;
 const TYPING_INTERVAL_MS = 42;
 const HERO_COMMAND_DELAY_MS = 500;
 const HERO_COMMAND_DELAY_TICKS = Math.ceil(
   HERO_COMMAND_DELAY_MS / TYPING_INTERVAL_MS
 );
-const HERO_TOTAL_TYPED_CHARS =
-  HERO_COMMAND_TEXT.length + HERO_COMMAND_DELAY_TICKS + HERO_TEXT.length;
 
 export function Hero() {
+  const { t } = useTranslation();
+  const HERO_COMMAND_BODY = t("hero.commandBody");
+  const HERO_COMMAND_TEXT = `${HERO_COMMAND_PROMPT}${HERO_COMMAND_BODY}`;
+  const HERO_PREFIX = t("hero.prefix");
+  const HERO_HIGHLIGHT = t("hero.highlight");
+  const HERO_SUFFIX = t("hero.suffix");
+  const HERO_TEXT = `${HERO_PREFIX}${HERO_HIGHLIGHT}${HERO_SUFFIX}`;
+  const HERO_TOTAL_TYPED_CHARS =
+    HERO_COMMAND_TEXT.length + HERO_COMMAND_DELAY_TICKS + HERO_TEXT.length;
+
   const intervalRef = useRef<number | null>(null);
   const [typedChars, setTypedChars] = useState(0);
 
@@ -193,7 +196,7 @@ export function Hero() {
         </h1>
         {isTypingComplete && (
           <p className="mt-5 rounded border border-outline-variant bg-surface-container-lowest px-2 py-2 font-mono text-sm tracking-[0.08em] text-on-surface-muted">
-            <span className="text-secondary">// quick_links</span>
+            <span className="text-secondary">{t("hero.quickLinks")}</span>
           </p>
         )}
         {isTypingComplete && (
diff --git a/src/components/app/home/sections/TechnicalSkills.tsx b/src/components/app/home/sections/TechnicalSkills.tsx
index 0dcdfd03..596b78a4 100644
--- a/src/components/app/home/sections/TechnicalSkills.tsx
+++ b/src/components/app/home/sections/TechnicalSkills.tsx
@@ -1,4 +1,5 @@
 import type { JSX } from "preact";
+import { useTranslation } from "~/i18n/context";
 
 import { technicalSkills as defaultTechnicalSkills } from "~/constants";
 
@@ -14,22 +15,22 @@ const sectionTitleMap: Record<string, string> = {
 const summaryCards = [
   {
     key: "javascript",
-    title: "JavaScript",
-    subtitle: "LOGIC_ENGINE_V8",
+    titleKey: "home.technicalSkills.javascript.title",
+    subtitleKey: "home.technicalSkills.javascript.subtitle",
     className: "text-primary",
     accentBorderClassName: "border-primary",
   },
   {
     key: "html-css",
-    title: "HTML/CSS",
-    subtitle: "STRUCTURAL_UI_CORE",
+    titleKey: "home.technicalSkills.htmlCss.title",
+    subtitleKey: "home.technicalSkills.htmlCss.subtitle",
     className: "text-secondary",
     accentBorderClassName: "border-secondary",
   },
   {
     key: "other",
-    title: "JACK OF ALL TRADES",
-    subtitle: "OTHER_TECH_SKILLS_I_HAVE",
+    titleKey: "home.technicalSkills.other.title",
+    subtitleKey: "home.technicalSkills.other.subtitle",
     className: "text-tertiary",
     accentBorderClassName: "border-tertiary",
   },
@@ -94,6 +95,7 @@ export function TechnicalSkills({
 }: {
   technicalSkills?: typeof defaultTechnicalSkills;
 }) {
+  const { t } = useTranslation();
   const summaryCardMap = Object.fromEntries(
     summaryCards.map((card) => [card.key, card])
   );
@@ -150,7 +152,9 @@ export function TechnicalSkills({
       <div className="container mx-auto">
         <div className="rounded-[2rem] border border-primary/30 dark:border-primary/18 bg-surface-container-lowest px-6 py-8 shadow-floating sm:px-10 sm:py-12">
           <div className="mb-10 flex items-center justify-between">
-            <p className="label-mono text-primary">module_01 // tech_stack</p>
+            <p className="label-mono text-primary">
+              {t("home.technicalSkills.moduleLabel")}
+            </p>
             <svg
               aria-hidden="true"
               viewBox="0 0 24 24"
@@ -184,7 +188,7 @@ export function TechnicalSkills({
           </div>
 
           <h1 className="font-headline text-4xl font-semibold text-on-surface sm:text-6xl">
-            Core Proficiencies
+            {t("home.technicalSkills.heading")}
           </h1>
 
           <div className="mt-10 space-y-6">
@@ -192,11 +196,15 @@ export function TechnicalSkills({
               const title = getSectionTitle(section, index);
               const card = (section.key && summaryCardMap[section.key]) ||
                 summaryCards[index] || {
-                  title,
-                  subtitle: "TECH_MODULE",
+                  titleKey: undefined,
+                  subtitleKey: undefined,
                   className: "text-on-surface",
                   accentBorderClassName: "border-outline",
                 };
+              const cardTitle = card.titleKey ? t(card.titleKey) : title;
+              const cardSubtitle = card.subtitleKey
+                ? t(card.subtitleKey)
+                : "TECH_MODULE";
 
               return (
                 <div
@@ -208,10 +216,10 @@ export function TechnicalSkills({
                       className={`relative overflow-hidden p-6 border-solid border-t-0 border-r-0 border-l-0 border-b-2 md:border-b-0 md:border-l-2 ${card.className} ${card.accentBorderClassName}`}
                     >
                       <h2 className="font-headline text-4xl leading-tight sm:text-3xl">
-                        {card.title}
+                        {cardTitle}
                       </h2>
                       <p className="mt-3 font-mono text-sm uppercase tracking-[0.12em] text-on-surface-muted">
-                        {card.subtitle}
+                        {cardSubtitle}
                       </p>
                     </article>
 
diff --git a/src/components/app/home/sections/ThreeSellPoints.tsx b/src/components/app/home/sections/ThreeSellPoints.tsx
index 9773e95c..2dea4b41 100644
--- a/src/components/app/home/sections/ThreeSellPoints.tsx
+++ b/src/components/app/home/sections/ThreeSellPoints.tsx
@@ -1,18 +1,20 @@
 import type { JSX } from "preact";
-
-import { threeSellPoints as defaultThreeSellPoints } from "~/constants";
+import { useTranslation } from "~/i18n/context";
 
 type ItemProps = JSX.HTMLAttributes<HTMLDivElement> & {
   title: string;
 };
 
 function Item({ title, children, ...props }: ItemProps) {
+  const { t } = useTranslation();
   return (
     <div
       className="surface-module-raised mb-6 rounded-3xl border border-outline-variant p-6"
       {...props}
     >
-      <p className="label-mono mb-3 text-primary">core</p>
+      <p className="label-mono mb-3 text-primary">
+        {t("home.sellPoints.moduleLabel")}
+      </p>
       <h2 className="mb-3 font-headline font-semibold text-2xl leading-tight text-on-surface">
         {title}
       </h2>
@@ -23,24 +25,23 @@ function Item({ title, children, ...props }: ItemProps) {
   );
 }
 
-export function ThreeSellPoints({
-  threeSellPoints = defaultThreeSellPoints,
-}: {
-  threeSellPoints?: typeof defaultThreeSellPoints;
-}) {
+export function ThreeSellPoints() {
+  const { t } = useTranslation();
   return (
     <section
       className="relative -mt-6 bg-surface px-6 pb-16 pt-16 sm:-mt-8"
       data-testid="section-three-sell-points"
     >
       <div className="container mx-auto sm:grid sm:grid-cols-3 sm:gap-8">
-        {threeSellPoints.map(({ title, description }) => {
-          return (
-            <Item key={title} title={title}>
-              {description}
-            </Item>
-          );
-        })}
+        <Item title={t("home.sellPoints.passion.title")}>
+          {t("home.sellPoints.passion.description")}
+        </Item>
+        <Item title={t("home.sellPoints.versatile.title")}>
+          {t("home.sellPoints.versatile.description")}
+        </Item>
+        <Item title={t("home.sellPoints.offWork.title")}>
+          {t("home.sellPoints.offWork.description")}
+        </Item>
       </div>
     </section>
   );
diff --git a/src/components/app/portfolio/PortfolioShowcase.tsx b/src/components/app/portfolio/PortfolioShowcase.tsx
index e14bf93d..4b93dce3 100644
--- a/src/components/app/portfolio/PortfolioShowcase.tsx
+++ b/src/components/app/portfolio/PortfolioShowcase.tsx
@@ -1,3 +1,5 @@
+import { useTranslation } from "~/i18n/context";
+
 import { type PortfolioFrontmatter } from "~/types/frontmatter";
 import { getTechBadgeStyle } from "~/components/portfolio/technologyColors";
 import Github from "~/components/icons/Github";
@@ -43,6 +45,7 @@ export function PortfolioShowcase({
   hasTitle = true,
   containerClassName = "container mx-auto max-w-[1536px]",
 }: Props) {
+  const { t } = useTranslation();
   const featuredItem = items?.find((item) => item.featured);
   const regularItems = items?.filter((item) => !item.featured);
   const sectionClassName = hasTitle
@@ -54,15 +57,13 @@ export function PortfolioShowcase({
         {hasTitle && (
           <div className="mb-8 rounded-3xl border border-outline-variant bg-surface-container-lowest p-6 sm:p-8">
             <p className="label-mono mb-2 text-primary">
-              module_03 // portfolio
+              {t("portfolio.showcase.moduleLabel")}
             </p>
             <h1 className="font-headline text-4xl font-semibold text-on-surface">
-              Portfolio
+              {t("portfolio.showcase.heading")}
             </h1>
             <p className="mt-3 text-on-surface-muted">
-              Built in the digital underground: apps, UI experiments, and
-              creative code modules I designed and shipped, from React tools to
-              interactive front-end prototypes.
+              {t("portfolio.showcase.description")}
             </p>
 
             <div className="mt-8">
@@ -73,7 +74,9 @@ export function PortfolioShowcase({
                     <a
                       href={`/portfolio/${featuredItem.slug}`}
                       className={cardPageLinkClassName}
-                      aria-label={`View ${featuredItem.title}`}
+                      aria-label={t("portfolio.showcase.viewItem", {
+                        title: featuredItem.title,
+                      })}
                     />
                     {/* Image Container */}
                     <div className="relative block h-80 w-full overflow-hidden bg-surface-container-lowest md:h-auto md:w-2/5 md:flex-shrink-0">
@@ -128,7 +131,9 @@ export function PortfolioShowcase({
 
                       {/* Footer with Links */}
                       <div className="flex items-center gap-3">
-                        <span className={viewActionClassName}>View</span>
+                        <span className={viewActionClassName}>
+                          {t("portfolio.showcase.viewAction")}
+                        </span>
                         {featuredItem.demoUrl && (
                           <span
                             className={demoIconClassName}
@@ -143,8 +148,10 @@ export function PortfolioShowcase({
                             target="_blank"
                             rel="noopener noreferrer"
                             className={githubActionClassName}
-                            aria-label="View on Github"
-                            title="View on GitHub"
+                            aria-label={t(
+                              "portfolio.showcase.viewOnGithubShort"
+                            )}
+                            title={t("portfolio.showcase.viewOnGithub")}
                           >
                             <Github width={24} height={24} />
                           </a>
@@ -169,7 +176,9 @@ export function PortfolioShowcase({
                         <a
                           href={`/portfolio/${item.slug}`}
                           className={cardPageLinkClassName}
-                          aria-label={`View ${item.title}`}
+                          aria-label={t("portfolio.showcase.viewItem", {
+                            title: item.title,
+                          })}
                         />
                         {/* Image Container */}
                         <div className="relative block h-52 w-full overflow-hidden rounded-t-2xl bg-surface-container-lowest">
@@ -224,7 +233,7 @@ export function PortfolioShowcase({
                           <span
                             className={`${viewActionClassName} flex-1 px-3 text-center text-sm`}
                           >
-                            View
+                            {t("portfolio.showcase.viewAction")}
                           </span>
                           {item.demoUrl && (
                             <span
@@ -240,8 +249,8 @@ export function PortfolioShowcase({
                               target="_blank"
                               rel="noopener noreferrer"
                               className={githubActionClassName}
-                              title="View on GitHub"
-                              aria-label="View on GitHub"
+                              title={t("portfolio.showcase.viewOnGithub")}
+                              aria-label={t("portfolio.showcase.viewOnGithub")}
                             >
                               <Github width={20} height={20} />
                             </a>
@@ -264,7 +273,9 @@ export function PortfolioShowcase({
                 <a
                   href={`/portfolio/${featuredItem.slug}`}
                   className={cardPageLinkClassName}
-                  aria-label={`View ${featuredItem.title}`}
+                  aria-label={t("portfolio.showcase.viewItem", {
+                    title: featuredItem.title,
+                  })}
                 />
                 {/* Image Container */}
                 <div className="relative block h-80 w-full overflow-hidden bg-surface-container-lowest md:h-auto md:w-2/5 md:flex-shrink-0">
@@ -319,7 +330,9 @@ export function PortfolioShowcase({
 
                   {/* Footer with Links */}
                   <div className="flex items-center gap-3">
-                    <span className={viewActionClassName}>View</span>
+                    <span className={viewActionClassName}>
+                      {t("portfolio.showcase.viewAction")}
+                    </span>
                     {featuredItem.demoUrl && (
                       <span className={demoIconClassName} aria-hidden="true">
                         <Next className="w-6 h-6" />
@@ -331,7 +344,7 @@ export function PortfolioShowcase({
                         target="_blank"
                         rel="noopener noreferrer"
                         className={githubActionClassName}
-                        title="View on GitHub"
+                        title={t("portfolio.showcase.viewOnGithub")}
                       >
                         <Github width={24} height={24} />
                       </a>
@@ -352,7 +365,9 @@ export function PortfolioShowcase({
                     <a
                       href={`/portfolio/${item.slug}`}
                       className={cardPageLinkClassName}
-                      aria-label={`View ${item.title}`}
+                      aria-label={t("portfolio.showcase.viewItem", {
+                        title: item.title,
+                      })}
                     />
                     {/* Image Container */}
                     <div className="relative block h-52 w-full overflow-hidden rounded-t-2xl bg-surface-container-lowest">
@@ -407,7 +422,7 @@ export function PortfolioShowcase({
                       <span
                         className={`${viewActionClassName} flex-1 px-3 text-center text-sm`}
                       >
-                        View
+                        {t("portfolio.showcase.viewAction")}
                       </span>
                       {item.demoUrl && (
                         <span className={demoIconClassName} aria-hidden="true">
@@ -420,7 +435,7 @@ export function PortfolioShowcase({
                           target="_blank"
                           rel="noopener noreferrer"
                           className={githubActionClassName}
-                          title="View on GitHub"
+                          title={t("portfolio.showcase.viewOnGithub")}
                         >
                           <Github width={20} height={20} />
                         </a>
diff --git a/src/components/portfolio/PortfolioEntry.tsx b/src/components/portfolio/PortfolioEntry.tsx
index 714aff57..b63b3c48 100644
--- a/src/components/portfolio/PortfolioEntry.tsx
+++ b/src/components/portfolio/PortfolioEntry.tsx
@@ -1,4 +1,5 @@
 import type { ComponentChildren } from "preact";
+import { useTranslation } from "~/i18n/context";
 
 import TechnologiesUsed from "~/components/portfolio/TechnologiesUsed";
 
@@ -42,13 +43,14 @@ const PortfolioEntry = ({
   technologiesUsed,
   children,
 }: Props) => {
+  const { t } = useTranslation();
   return (
     <>
       <section className="graph-bg px-6 pb-10 pt-24 sm:pt-28">
         <div className="container mx-auto max-w-[1536px] lg:flex lg:items-end lg:justify-between lg:gap-10">
           <div>
             <p className="label-mono mb-3 text-tertiary">
-              project_entry // live_record
+              {t("portfolio.entry.projectLabel")}
             </p>
             <h1 className="bg-gradient-to-r from-on-background via-tertiary to-tertiary-container bg-clip-text font-headline text-3xl font-semibold tracking-tight text-transparent sm:text-5xl md:text-6xl">
               {title}
@@ -58,7 +60,7 @@ const PortfolioEntry = ({
           {(demoUrl || githubUrl || (urls && urls.length > 0)) && (
             <div className="mt-6 rounded-2xl border border-outline-variant bg-surface-container-high p-4 lg:mt-0 lg:min-w-[320px]">
               <p className="label-mono mb-3 text-on-surface-muted">
-                external_links
+                {t("portfolio.entry.externalLinksLabel")}
               </p>
               <div className="space-y-2">
                 {demoUrl && (
@@ -68,7 +70,7 @@ const PortfolioEntry = ({
                     target="__blank"
                     rel="noreferrer"
                   >
-                    Open Demo
+                    {t("portfolio.entry.openDemo")}
                   </Link>
                 )}
                 {githubUrl && (
@@ -78,7 +80,7 @@ const PortfolioEntry = ({
                     target="_blank"
                     rel="noreferrer"
                   >
-                    View GitHub Code
+                    {t("portfolio.entry.viewGithubCode")}
                   </Link>
                 )}
                 {urls &&
@@ -90,7 +92,7 @@ const PortfolioEntry = ({
                       rel="noreferrer"
                       className={`${externalLinkClassName} overflow-hidden overflow-ellipsis`}
                     >
-                      Visit {url}
+                      {t("portfolio.entry.visitUrl", { url })}
                     </Link>
                   ))}
               </div>
@@ -118,7 +120,7 @@ const PortfolioEntry = ({
                     className={`${accentGlowBaseClassName} ${accentGlowGeometry.impact} bg-gradient-to-r from-transparent via-primary/30 to-transparent dark:via-primary/22`}
                   />
                   <p className="label-mono mb-4 text-primary">
-                    impact_log // execution
+                    {t("portfolio.entry.impactLabel")}
                   </p>
                   <WhatIDid whatIDid={whatIDid} />
                 </div>
@@ -144,7 +146,7 @@ const PortfolioEntry = ({
                 className={`${accentGlowBaseClassName} ${accentGlowGeometry.notes} bg-gradient-to-r from-transparent via-secondary/30 to-transparent dark:via-secondary/22`}
               />
               <p className="label-mono mb-4 text-secondary">
-                project_notes // context
+                {t("portfolio.entry.notesLabel")}
               </p>
               <div className="prose prose-sm max-w-none prose-headings:mt-0 prose-headings:mb-4 dark:prose-invert md:prose-lg lg:prose-xl">
                 <main>{children}</main>
diff --git a/src/components/portfolio/PortfolioMedia.test.tsx b/src/components/portfolio/PortfolioMedia.test.tsx
index d68ef8e8..f4630e00 100644
--- a/src/components/portfolio/PortfolioMedia.test.tsx
+++ b/src/components/portfolio/PortfolioMedia.test.tsx
@@ -46,12 +46,6 @@ it("renders image links inside the slider", () => {
   expect(links[1]).toHaveAttribute("href", images[1].src);
 });
 
-it("shows the filename caption for the current image when a slider is present", () => {
-  render(<PortfolioMedia title="My Project" images={images} />);
-  // Default index is 0, so filename of images[0].src should appear
-  expect(screen.getByText("a.jpg")).toBeInTheDocument();
-});
-
 it("renders iframe when iframe prop is provided", () => {
   render(
     <PortfolioMedia
diff --git a/src/components/portfolio/TechnologiesUsed.tsx b/src/components/portfolio/TechnologiesUsed.tsx
index 688db9a9..b4378324 100644
--- a/src/components/portfolio/TechnologiesUsed.tsx
+++ b/src/components/portfolio/TechnologiesUsed.tsx
@@ -1,6 +1,7 @@
 import { type PortfolioTechnology } from "~/types/frontmatter";
 import type { JSX } from "preact";
 import { useState } from "preact/hooks";
+import { useTranslation } from "~/i18n/context";
 import {
   getTechColor,
   hexToRgba,
@@ -51,6 +52,7 @@ function describeDonutSlicePath(startPercent: number, endPercent: number) {
 }
 
 const TechnologiesUsed = ({ className, ...props }: Props) => {
+  const { t } = useTranslation();
   const [activeIndex, setActiveIndex] = useState<number | null>(null);
 
   const technologies = (props.data || [])
@@ -102,7 +104,7 @@ const TechnologiesUsed = ({ className, ...props }: Props) => {
   return (
     <div className={className}>
       <h3 className="mb-6 font-headline text-2xl font-semibold text-on-surface md:text-4xl">
-        Technologies Used
+        {t("portfolio.technologiesUsed.heading")}
       </h3>
 
       <div className="grid gap-6 md:grid-cols-[220px_1fr] md:items-center">
diff --git a/src/components/portfolio/WhatIDid.tsx b/src/components/portfolio/WhatIDid.tsx
index d4b28bc6..ba9b50a3 100644
--- a/src/components/portfolio/WhatIDid.tsx
+++ b/src/components/portfolio/WhatIDid.tsx
@@ -1,12 +1,15 @@
+import { useTranslation } from "~/i18n/context";
+
 import { type PortfolioFrontmatter } from "~/types/frontmatter";
 
 type Props = Pick<PortfolioFrontmatter, "whatIDid">;
 
 const WhatIDid = ({ whatIDid }: Props) => {
+  const { t } = useTranslation();
   return (
     <div>
       <h3 className="mb-6 font-headline text-2xl font-semibold text-on-surface md:text-4xl">
-        What I Did
+        {t("portfolio.whatIDid.heading")}
       </h3>
       <ul className="space-y-3">
         {whatIDid.map((bullet, i) => (
diff --git a/src/constants.tsx b/src/constants.tsx
index ef3b467b..bd878c13 100644
--- a/src/constants.tsx
+++ b/src/constants.tsx
@@ -4,39 +4,6 @@ export const LINKEDIN_URL = "https://www.linkedin.com/in/hrgui";
 
 export const siteTitle = "hrgui";
 
-type SellPoint = {
-  title: string;
-  description: string;
-};
-
-export const threeSellPoints: SellPoint[] = [
-  {
-    title: "Making the web awesome is my passion.",
-    description: `I am a Software Engineer who loves building interactive web
-    applications. I believe great apps should work smoothly and look clean,
-    so people can focus on getting things done. Seeing users succeed with
-    something I helped build is the most rewarding part of my work. I am
-    always refining the experience to make each product better than before.`,
-  },
-  {
-    title: "I will work with anything... to make the web work.",
-    description: `Throughout my career, I have taken on unfamiliar and
-    challenging problems. I have contributed to backend systems,
-    automation platforms, and mobile applications across very different
-    codebases. Some were clean, some were heavily patched, but I focus on
-    improving the developer experience so teams can build and ship with
-    confidence.`,
-  },
-  {
-    title: "While my code is compiling...",
-    description: `Outside of coding, I play guitar and enjoy Japanese
-    animation (anime). Both constantly fuel my creativity and inspire how I think
-    about building products. I often learn anime theme songs on guitar,
-    and that mix of music and storytelling keeps my ideas fresh. When I can
-    bring those creative influences into software, I do my best work.`,
-  },
-];
-
 type TechnicalSkillSection = {
   key?: string;
   title: string | JSX.Element;
@@ -121,8 +88,6 @@ type Education = {
   key: string;
   url: string;
   imgSrc: string;
-  title: string;
-  description: string;
   timeframe: { start: number | string; end: number | string };
 };
 
@@ -131,9 +96,6 @@ export const education: Education[] = [
     key: "usc",
     url: "https://www.usc.edu",
     imgSrc: "/images/usc.webp",
-    title: "University of Southern California",
-    description: `Bachelor of Science (B.S.), Computer Science and Engineering,
-    Magna Cum Laude`,
     timeframe: { start: 2010, end: 2013 },
   },
 ];
diff --git a/src/i18n/context.tsx b/src/i18n/context.tsx
new file mode 100644
index 00000000..e78d6913
--- /dev/null
+++ b/src/i18n/context.tsx
@@ -0,0 +1,51 @@
+import { createContext, type ComponentChildren } from "preact";
+import { useContext } from "preact/hooks";
+
+import type { Translation } from "~/i18n/locales/en";
+import en from "~/i18n/locales/en";
+
+type TFunction = (key: string, opts?: Record<string, unknown>) => string;
+
+const locales: Record<string, Translation> = { en };
+
+function resolvePath(obj: unknown, path: string): string {
+  const parts = path.split(".");
+  let current: unknown = obj;
+  for (const part of parts) {
+    if (current == null || typeof current !== "object") return path;
+    current = (current as Record<string, unknown>)[part];
+  }
+  return typeof current === "string" ? current : path;
+}
+
+function createT(locale: string): TFunction {
+  const translations = locales[locale] ?? locales.en;
+  return (key, opts) => {
+    let str = resolvePath(translations, key);
+    if (opts) {
+      Object.entries(opts).forEach(([k, v]) => {
+        str = str.replace(`{{${k}}}`, String(v));
+      });
+    }
+    return str;
+  };
+}
+
+const I18nContext = createContext<TFunction>(createT("en"));
+
+interface ProviderProps {
+  locale?: string;
+  children: ComponentChildren;
+}
+
+export function I18nProvider({ locale = "en", children }: ProviderProps) {
+  return (
+    <I18nContext.Provider value={createT(locale)}>
+      {children}
+    </I18nContext.Provider>
+  );
+}
+
+export function useTranslation() {
+  return { t: useContext(I18nContext) };
+}
diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts
new file mode 100644
index 00000000..956ff9bd
--- /dev/null
+++ b/src/i18n/locales/en.ts
@@ -0,0 +1,117 @@
+const en = {
+  nav: {
+    home: "Home",
+    blog: "Blog",
+    portfolio: "Portfolio",
+  },
+  footer: {
+    bio: "Harman Goei (hrgui) is a developer that loves to make cool and awesome web applications. His strength is in HTML, CSS, JavaScript, but he is willing to code anywhere in the stack to make the web be awesome.",
+    backToTop: "back to top?",
+    copyright: "© {{year}} Harman Goei",
+  },
+  social: {
+    github: "View My GitHub Profile",
+    linkedin: "View My LinkedIn Profile",
+  },
+  hero: {
+    commandBody: " about",
+    prefix: "I build ",
+    highlight: "cool and awesome",
+    suffix: "web and mobile apps.",
+    quickLinks: "// quick_links",
+  },
+  home: {
+    sellPoints: {
+      moduleLabel: "core",
+      passion: {
+        title: "Making the web awesome is my passion.",
+        description:
+          "I am a Software Engineer who loves building interactive web applications. I believe great apps should work smoothly and look clean, so people can focus on getting things done. Seeing users succeed with something I helped build is the most rewarding part of my work. I am always refining the experience to make each product better than before.",
+      },
+      versatile: {
+        title: "I will work with anything... to make the web work.",
+        description:
+          "Throughout my career, I have taken on unfamiliar and challenging problems. I have contributed to backend systems, automation platforms, and mobile applications across very different codebases. Some were clean, some were heavily patched, but I focus on improving the developer experience so teams can build and ship with confidence.",
+      },
+      offWork: {
+        title: "While my code is compiling...",
+        description:
+          "Outside of coding, I play guitar and enjoy Japanese animation (anime). Both constantly fuel my creativity and inspire how I think about building products. I often learn anime theme songs on guitar, and that mix of music and storytelling keeps my ideas fresh. When I can bring those creative influences into software, I do my best work.",
+      },
+    },
+    technicalSkills: {
+      moduleLabel: "module_01 // tech_stack",
+      heading: "Core Proficiencies",
+      javascript: {
+        title: "JavaScript",
+        subtitle: "LOGIC_ENGINE_V8",
+      },
+      htmlCss: {
+        title: "HTML/CSS",
+        subtitle: "STRUCTURAL_UI_CORE",
+      },
+      other: {
+        title: "JACK OF ALL TRADES",
+        subtitle: "OTHER_TECH_SKILLS_I_HAVE",
+      },
+    },
+    education: {
+      moduleLabel: "module_02 // education",
+      heading: "Education",
+      imgAlt: "Open {{title}} in a new tab",
+      usc: {
+        title: "University of Southern California",
+        description:
+          "Bachelor of Science (B.S.), Computer Science and Engineering, Magna Cum Laude",
+      },
+    },
+  },
+  portfolio: {
+    showcase: {
+      moduleLabel: "module_03 // portfolio",
+      heading: "Portfolio",
+      description:
+        "Built in the digital underground: apps, UI experiments, and creative code modules I designed and shipped, from React tools to interactive front-end prototypes.",
+      viewAction: "View",
+      viewOnGithub: "View on GitHub",
+      viewOnGithubShort: "View on Github",
+      viewItem: "View {{title}}",
+    },
+    entry: {
+      projectLabel: "project_entry // live_record",
+      externalLinksLabel: "external_links",
+      openDemo: "Open Demo",
+      viewGithubCode: "View GitHub Code",
+      visitUrl: "Visit {{url}}",
+      impactLabel: "impact_log // execution",
+      notesLabel: "project_notes // context",
+    },
+    technologiesUsed: {
+      heading: "Technologies Used",
+      empty: "No technology data available.",
+    },
+    whatIDid: {
+      heading: "What I Did",
+    },
+  },
+  blog: {
+    posts: {
+      newBadge: "New",
+      executeRead: "Execute_Read ->",
+      readMore: "Read_More ->",
+      noPosts: "No posts available.",
+    },
+    subHeader: {
+      entryRecord: "ENTRY_RECORD",
+      hiddenDraft: "Hidden Draft",
+      hiddenWarning:
+        "You are looking at a hidden post. Remove `hidden: true` or set it to `false` to publish this post.",
+    },
+  },
+} as const;
+
+type DeepString<T> = {
+  readonly [K in keyof T]: T[K] extends string ? string : DeepString<T[K]>;
+};
+export type Translation = DeepString<typeof en>;
+export default en;
diff --git a/src/layouts/blog.astro b/src/layouts/blog.astro
index cfd7319b..dcd98693 100644
--- a/src/layouts/blog.astro
+++ b/src/layouts/blog.astro
@@ -17,9 +17,9 @@ const { title, excerpt, date, hidden } = Astro.props;
 ---
 
 <!DOCTYPE html>
-<html lang="en">
+<html lang={Astro.currentLocale ?? "en"}>
   <Head title={title} pageType={"blog"} description={excerpt} />
-  <AppLayout currentPathName={Astro.url.pathname} client:visible>
+  <AppLayout currentPathName={Astro.url.pathname} locale={Astro.currentLocale ?? "en"} client:visible>
     <BlogSubHeader
       date={date}
       title={title}
diff --git a/src/layouts/default.astro b/src/layouts/default.astro
index ab50ed27..c4752c83 100644
--- a/src/layouts/default.astro
+++ b/src/layouts/default.astro
@@ -13,9 +13,9 @@ export type Props = {
 ---
 
 <!DOCTYPE html>
-<html lang="en" class="dark:bg-neutral-800">
+<html lang={Astro.currentLocale ?? "en"} class="dark:bg-neutral-800">
   <Head title={title} description={description} />
-  <AppLayout currentPathName={Astro.url.pathname} client:visible>
+  <AppLayout currentPathName={Astro.url.pathname} locale={Astro.currentLocale ?? "en"} client:visible>
     <slot />
   </AppLayout>
 </html>
diff --git a/src/layouts/portfolio.astro b/src/layouts/portfolio.astro
index e3cb4d2e..16d90ad3 100644
--- a/src/layouts/portfolio.astro
+++ b/src/layouts/portfolio.astro
@@ -16,9 +16,9 @@ export type Props = {
 ---
 
 <!DOCTYPE html>
-<html lang="en">
+<html lang={Astro.currentLocale ?? "en"}>
   <Head title={title} description={description} />
-  <AppLayout currentPathName={Astro.url.pathname} client:visible>
+  <AppLayout currentPathName={Astro.url.pathname} locale={Astro.currentLocale ?? "en"} client:visible>
     <PortfolioEntry {...content} client:visible>
       <slot />
     </PortfolioEntry>
diff --git a/src/pages/index.astro b/src/pages/index.astro
index d7a00309..1d94c5ef 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -2,11 +2,7 @@
 import { getCollection } from "astro:content";
 import DefaultPageLayout from "~/layouts/default.astro";
 import { type PortfolioFrontmatter } from "~/types/frontmatter";
-import { Education } from "~/components/app/home/sections/Education";
-import { Hero } from "~/components/app/home/sections/Hero";
-import { TechnicalSkills } from "~/components/app/home/sections/TechnicalSkills";
-import { ThreeSellPoints } from "~/components/app/home/sections/ThreeSellPoints";
-import { PortfolioShowcase } from "~/components/app/portfolio/PortfolioShowcase";
+import { HomeSections } from "~/components/app/home/HomeSections";
 
 const description = `Hi! I'm Harman. I am a developer who loves to make cool and awesome web applications that rock the world.`;
 
@@ -14,14 +10,12 @@ const portfolioItems = await getCollection("portfolio");
 ---
 
 <DefaultPageLayout description={description}>
-  <Hero client:visible />
-  <ThreeSellPoints />
-  <TechnicalSkills />
-  <Education />
-  <PortfolioShowcase
+  <HomeSections
+    locale={Astro.currentLocale ?? "en"}
     containerClassName="container mx-auto"
     items={portfolioItems.map(
       (item) => ({ ...item.data, slug: item.id } as PortfolioFrontmatter)
     )}
+    client:visible
   />
 </DefaultPageLayout>
diff --git a/src/test/setup.tsx b/src/test/setup.tsx
index a6e8eb82..291c37b6 100644
--- a/src/test/setup.tsx
+++ b/src/test/setup.tsx
@@ -1,7 +1,45 @@
 import "@testing-library/jest-dom";
+import { vi } from "vitest";
 
 globalThis.IS_REACT_ACT_ENVIRONMENT = true;
 
+import en from "~/i18n/locales/en";
+
+function resolveTranslationKey(
+   
+  obj: Record<string, any>,
+  key: string
+): string {
+  const parts = key.split(".");
+   
+  let current: any = obj;
+  for (const part of parts) {
+    if (typeof current !== "object" || current === null) return key;
+    current = current[part];
+  }
+  return typeof current === "string" ? current : key;
+}
+
+vi.mock("~/i18n/context", () => ({
+  useTranslation: () => ({
+    t: (key: string, opts?: Record<string, unknown>) => {
+      let str = resolveTranslationKey(
+        en as unknown as Record<string, unknown>,
+        key
+      );
+      if (opts) {
+        str = Object.entries(opts).reduce(
+          (s, [k, v]) => s.replace(`{{${k}}}`, String(v)),
+          str
+        );
+      }
+      return str;
+    },
+  }),
+   
+  I18nProvider: ({ children }: { children: any }) => children,
+}));
+
 Element.prototype.scrollTo = () => {};
 
 Object.defineProperty(window, "matchMedia", {
