<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Kashif Rezwi</title>
    <description>The latest articles on Forem by Kashif Rezwi (@kashifrezwi).</description>
    <link>https://forem.com/kashifrezwi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3787250%2F9c5a4968-f093-43db-a064-9dde23fc6c80.jpeg</url>
      <title>Forem: Kashif Rezwi</title>
      <link>https://forem.com/kashifrezwi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kashifrezwi"/>
    <language>en</language>
    <item>
      <title>I Built an AI Agent That Makes Any Landing Page (Next.js App) Multilingual in Minutes</title>
      <dc:creator>Kashif Rezwi</dc:creator>
      <pubDate>Mon, 23 Feb 2026 18:11:18 +0000</pubDate>
      <link>https://forem.com/kashifrezwi/i-built-an-ai-agent-that-makes-any-nextjs-app-multilingual-in-3-minutes-4bdm</link>
      <guid>https://forem.com/kashifrezwi/i-built-an-ai-agent-that-makes-any-nextjs-app-multilingual-in-3-minutes-4bdm</guid>
      <description>&lt;h2&gt;
  
  
  The Problem Nobody Wants to Solve
&lt;/h2&gt;

&lt;p&gt;Going multilingual is one of the most commonly requested — and most commonly abandoned — features in software development.&lt;/p&gt;

&lt;p&gt;Companies know they need it. Users demand it. Global markets require it. Revenue depends on it. But the implementation consistently defeats engineering teams.&lt;/p&gt;

&lt;p&gt;Why? Because translation is only 10% of the work. The other 90% is orchestration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;String externalization&lt;/strong&gt; — wrapping every hardcoded string in &lt;code&gt;t()&lt;/code&gt; functions across hundreds of files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration&lt;/strong&gt; — setting up i18n libraries, locale directories, provider wrappers, config files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translation management&lt;/strong&gt; — maintaining JSON locale files that drift out of sync with every feature&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt; — creating branches, opening PRs, setting up preview environments to verify the result&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coordination&lt;/strong&gt; — developers, translators, reviewers, and PMs rarely speaking the same language about the problem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Retrofitting i18n into an existing codebase costs 3–10x more than building it in from the start. So teams punt. And the feature dies on the backlog.&lt;/p&gt;

&lt;p&gt;AI tools like &lt;a href="https://lingo.dev" rel="noopener noreferrer"&gt;Lingo.dev&lt;/a&gt; have dramatically lowered the cost of the translation step itself. But they still require a developer to sit down, read the documentation, configure the tooling, run the CLI, manage the output, and set up a preview. That's still hours of focused engineering time per project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The real problem isn't translation. Translation is solved. The problem is the orchestration gap&lt;/strong&gt; — the hours between "we want multilingual support" and "here's a working branch with a live preview."&lt;/p&gt;

&lt;p&gt;That's the gap LingoAgent fills.&lt;/p&gt;




&lt;h2&gt;
  
  
  What LingoAgent Does
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;One input. One click. One working multilingual branch with a live preview.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You provide a GitHub repository URL and select your target languages. An autonomous AI agent takes over completely:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clones the repo into an isolated cloud sandbox&lt;/li&gt;
&lt;li&gt;Detects the framework (Next.js App Router)&lt;/li&gt;
&lt;li&gt;Analyzes the codebase for existing i18n setups&lt;/li&gt;
&lt;li&gt;Configures the i18n scaffolding (providers, switcher, components)&lt;/li&gt;
&lt;li&gt;Extracts every hardcoded JSX string via Babel AST and translates them with Lingo.dev SDK&lt;/li&gt;
&lt;li&gt;Commits everything to a new branch and opens a GitHub PR&lt;/li&gt;
&lt;li&gt;Triggers a live Vercel preview deployment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The entire process takes few minutes instead of days. The developer's only job is to review the PR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🌐 Live:&lt;/strong&gt; &lt;a href="https://lingo-agent.vercel.app" rel="noopener noreferrer"&gt;lingo-agent.vercel.app&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;📦 Repo:&lt;/strong&gt; &lt;a href="https://github.com/Kashif-Rezwi/lingo-agent" rel="noopener noreferrer"&gt;github.com/Kashif-Rezwi/lingo-agent&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  The Approach: Orchestration, Not Translation
&lt;/h2&gt;

&lt;p&gt;This distinction drove every design decision.&lt;/p&gt;

&lt;p&gt;Lingo.dev has already solved the hard problem of accurate, context-aware AI translation. LingoAgent doesn't compete with that — it wraps Lingo.dev in an intelligent agent pipeline that removes every remaining manual step.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Lingo.dev provides (5 tools)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compiler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Build-time AST translation for React — zero code changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CLI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Translates project files across 26 formats via &lt;code&gt;i18n.json&lt;/code&gt; config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CI/CD Action&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GitHub Action that automates CLI on every push&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SDK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runtime translation in 7+ languages for dynamic content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP Server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Exposes framework-specific setup knowledge to AI assistants&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  What LingoAgent adds on top
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What Lingo.dev Still Needs a Human For&lt;/th&gt;
&lt;th&gt;What LingoAgent Does Instead&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reading and understanding the docs&lt;/td&gt;
&lt;td&gt;Agent queries the MCP server for exact instructions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloning the repo locally&lt;/td&gt;
&lt;td&gt;Agent clones into an isolated E2B sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detecting framework &amp;amp; choosing setup path&lt;/td&gt;
&lt;td&gt;Agent reads &lt;code&gt;package.json&lt;/code&gt; and config files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Modifying &lt;code&gt;next.config.ts&lt;/code&gt; and &lt;code&gt;layout.tsx&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Agent applies verified changes from MCP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Writing &lt;code&gt;i18n.json&lt;/code&gt; configuration&lt;/td&gt;
&lt;td&gt;Agent generates it from user's selected locales&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Running &lt;code&gt;npm install&lt;/code&gt; and translation engine&lt;/td&gt;
&lt;td&gt;Agent executes inside the sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Creating branch and opening PR&lt;/td&gt;
&lt;td&gt;Agent calls GitHub API via Octokit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setting up preview deployment&lt;/td&gt;
&lt;td&gt;Agent triggers Vercel and polls until ready&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;LingoAgent uses the Lingo.dev SDK&lt;/strong&gt; for string translation and the &lt;strong&gt;MCP Server&lt;/strong&gt; for correct i18n scaffolding, building an autonomous pipeline around them.&lt;/p&gt;


&lt;h2&gt;
  
  
  Architecture: Three Layers
&lt;/h2&gt;

&lt;p&gt;The system has three distinct layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────────────────┐
│                   Browser (User)                     │
│                                                      │
│  Next.js 14 App Router (Client)                      │
│  ┌──────────┬────────────┬────────────────────────┐  │
│  │  /login  │ /dashboard │ /jobs/[jobId]          │  │
│  │  GitHub  │ New Job /  │ SSE log stream         │  │
│  │  OAuth   │ History /  │ + PR / Preview links   │  │
│  │          │ Settings   │                        │  │
│  └──────────┴────────────┴────────────────────────┘  │
└──────────────────────┬───────────────────────────────┘
                       │ REST + SSE
                       ▼
┌──────────────────────────────────────────────────────┐
│           NestJS API Server (Backend)                │
│                                                      │
│  AuthGuard → AgentController → AgentService          │
│  JobsService (Prisma + Neon PostgreSQL)              │
│                                                      │
│  ┌────────────────────────────────────────────────┐  │
│  │          Agent Pipeline (per job)              │  │
│  │  Groq LLM — tool call planner                  │  │
│  │  7 sequential tools in E2B sandbox             │  │
│  └────────────────────────────────────────────────┘  │
│                                                      │
│  External Services:                                  │
│  ├── GitHub (Octokit) — clone / branch / PR          │
│  ├── E2B — isolated sandbox execution                │
│  ├── Lingo.dev SDK + MCP — translation               │
│  └── Vercel API — preview deployment                 │
└──────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why these choices?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decision&lt;/th&gt;
&lt;th&gt;Rationale&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Monorepo&lt;/strong&gt; (&lt;code&gt;/client&lt;/code&gt; + &lt;code&gt;/server&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Clear separation; each deploys independently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SSE over WebSockets&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One-way log streaming is all we need; SSE is simpler and HTTP-native&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;E2B sandboxes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Complete process isolation — git, npm, node all run in throwaway VMs. No untrusted code on our servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sequential tool forcing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prevents the LLM from skipping steps or calling tools out of order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RxJS &lt;code&gt;ReplaySubject&lt;/code&gt; per job&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Late-joining SSE connections replay all past events from job start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Groq (Llama 3.3 70B)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fast inference, free tier available, excellent tool-calling accuracy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The 7-Step Agent Pipeline
&lt;/h2&gt;

&lt;p&gt;This is the core of LingoAgent. Each job runs a &lt;strong&gt;strictly sequential 7-step pipeline&lt;/strong&gt;. The LLM is only ever shown one tool schema at a time (&lt;code&gt;toolChoice: 'required'&lt;/code&gt;), forcing it to call that exact tool. The server executes the tool manually — the LLM only plans, never executes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: &lt;code&gt;clone_repo&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Spins up a fresh E2B cloud sandbox and clones the repository into &lt;code&gt;/workspace&lt;/code&gt;. Returns a &lt;code&gt;sandboxId&lt;/code&gt; that every subsequent tool uses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: &lt;code&gt;detect_framework&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Reads &lt;code&gt;package.json&lt;/code&gt;, &lt;code&gt;next.config.ts&lt;/code&gt;, and directory structure. Identifies Next.js version, App Router vs Pages Router, and the layout file path. &lt;strong&gt;If it's not Next.js App Router, the pipeline halts immediately&lt;/strong&gt; with a clear error — no wasted time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: &lt;code&gt;analyze_repo&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Scans for existing i18n libraries (&lt;code&gt;next-intl&lt;/code&gt;, &lt;code&gt;i18next&lt;/code&gt;, &lt;code&gt;react-i18next&lt;/code&gt;). If found, the agent stops and warns the user about potential conflicts. Counts JSX files to estimate the translation workload.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: &lt;code&gt;setup_lingo&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Queries the &lt;strong&gt;Lingo.dev MCP server&lt;/strong&gt; for exact setup instructions for the detected framework. Then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writes &lt;code&gt;i18n.json&lt;/code&gt; configuration&lt;/li&gt;
&lt;li&gt;Creates the &lt;code&gt;TextTranslator&lt;/code&gt; component, &lt;code&gt;LingoProvider&lt;/code&gt;, and &lt;code&gt;LanguageSwitcher&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Patches &lt;code&gt;layout.tsx&lt;/code&gt; to wrap the app with the provider&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This step is where the MCP server shines — instead of the LLM hallucinating configuration, it gets verified, up-to-date instructions directly from Lingo.dev.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: &lt;code&gt;install_and_translate&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The heaviest step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Runs &lt;code&gt;npm install&lt;/code&gt; inside the sandbox&lt;/li&gt;
&lt;li&gt;Uses &lt;strong&gt;Babel AST parsing&lt;/strong&gt; to extract every hardcoded JSX string — text nodes, &lt;code&gt;placeholder&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;alt&lt;/code&gt;, &lt;code&gt;aria-label&lt;/code&gt; attributes&lt;/li&gt;
&lt;li&gt;Translates extracted strings in chunks via the &lt;strong&gt;Lingo.dev SDK&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Writes locale JSON files to &lt;code&gt;public/locales/{lang}.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the Lingo.dev API key is invalid or quota is exceeded, the pipeline aborts immediately with actionable guidance pointing the user to the Settings tab.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: &lt;code&gt;commit_and_push&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Uses &lt;strong&gt;Octokit&lt;/strong&gt; to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new branch (&lt;code&gt;lingo/add-multilingual-support&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Commit all modified and new files&lt;/li&gt;
&lt;li&gt;Open a pull request with a structured description&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The PR body includes what was translated, which files were modified, and known limitations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: &lt;code&gt;trigger_preview&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Calls the &lt;strong&gt;Vercel API&lt;/strong&gt; to trigger a preview deployment for the new branch. Polls deployment status every few seconds until it's ready, then returns the live preview URL.&lt;/p&gt;

&lt;p&gt;The SSE stream emits &lt;code&gt;{ type: 'complete', data: { prUrl, previewUrl } }&lt;/code&gt; — and the frontend displays both links.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the LLM Orchestration Actually Works
&lt;/h2&gt;

&lt;p&gt;This is the part I found most interesting to build. The pattern is &lt;strong&gt;LLM as planner, not executor&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified version of the core loop&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;generateText&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;groq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;llama-3.3-70b-versatile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM_PROMPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;conversationHistory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;currentTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;currentTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;toolChoice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Force the LLM to call THIS tool&lt;/span&gt;
  &lt;span class="na"&gt;maxSteps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key design decisions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One tool at a time.&lt;/strong&gt; The LLM never sees all 7 tools simultaneously. It sees exactly one tool schema and is forced to produce arguments for it. This eliminates tool selection errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Server-side execution.&lt;/strong&gt; The LLM returns tool call arguments. The server validates them, executes the tool, and feeds the result back into the conversation history. The LLM never touches the actual APIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Up to 3 retries.&lt;/strong&gt; If the LLM produces invalid arguments, the server appends a correction prompt and retries. After 3 failures, the pipeline aborts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context window as state.&lt;/strong&gt; Each tool's return value feeds the next through the LLM's conversation history. The &lt;code&gt;sandboxId&lt;/code&gt; from step 1 flows through steps 2–7. The &lt;code&gt;framework&lt;/code&gt; from step 2 informs step 4. The &lt;code&gt;branchName&lt;/code&gt; from step 6 feeds step 7.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This architecture avoids the two biggest pitfalls of LLM agent systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hallucination&lt;/strong&gt; — the LLM can't hallucinate a tool call because it's only shown one option&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SDK timeouts&lt;/strong&gt; — the server controls execution timing, not the LLM&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-Time Observability
&lt;/h2&gt;

&lt;p&gt;Users can tolerate a minute process. They cannot tolerate a minute black box.&lt;/p&gt;

&lt;p&gt;Every tool emits structured log events through an SSE stream:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Each tool emits logs via an emit function&lt;/span&gt;
&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;📦 Cloning repository...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clone_repo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;percent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// ... work happens ...&lt;/span&gt;
&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;log&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✅ Repository cloned successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the frontend, the &lt;code&gt;/jobs/[jobId]&lt;/code&gt; page opens an &lt;code&gt;EventSource&lt;/code&gt; connection and renders logs in real time. Because the backend uses an RxJS &lt;code&gt;ReplaySubject&lt;/code&gt;, even if the user navigates away and comes back, they see the full log history from the start.&lt;/p&gt;




&lt;h2&gt;
  
  
  Auth Flow: GitHub Token as Bearer
&lt;/h2&gt;

&lt;p&gt;LingoAgent uses GitHub OAuth exclusively — no passwords, no separate accounts.&lt;/p&gt;

&lt;p&gt;The clever part: the same GitHub access token that authenticates the user on the frontend is forwarded as a Bearer token to the NestJS backend. The backend:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Validates the token exists&lt;/li&gt;
&lt;li&gt;Uses it directly to call GitHub APIs (clone, branch, commit, PR) &lt;strong&gt;on behalf of the user&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No separate JWT system. No session store on the backend. The user's own GitHub permissions govern what repositories the agent can access.&lt;/p&gt;




&lt;h2&gt;
  
  
  Custom API Keys: Bypassing Free Tier Limits
&lt;/h2&gt;

&lt;p&gt;One practical challenge: shared API keys hit rate limits fast during a hackathon demo.&lt;/p&gt;

&lt;p&gt;LingoAgent lets users supply their own &lt;strong&gt;Lingo.dev&lt;/strong&gt; and &lt;strong&gt;Groq&lt;/strong&gt; API keys via &lt;code&gt;Dashboard → Settings&lt;/code&gt;. These keys are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stored in &lt;code&gt;localStorage&lt;/code&gt; (never sent to any third party)&lt;/li&gt;
&lt;li&gt;Sent with each job request as optional headers&lt;/li&gt;
&lt;li&gt;Used by the backend to override server-side defaults&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means the demo works even when the shared keys are exhausted — users just paste in their own free-tier keys.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tech&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Next.js 14 (App Router)&lt;/td&gt;
&lt;td&gt;React framework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NextAuth.js&lt;/td&gt;
&lt;td&gt;GitHub OAuth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tailwind CSS&lt;/td&gt;
&lt;td&gt;Styling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;Type safety&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tech&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NestJS 11&lt;/td&gt;
&lt;td&gt;API server, DI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vercel AI SDK&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;generateText&lt;/code&gt; + tool calling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Groq (Llama 3.3 70B)&lt;/td&gt;
&lt;td&gt;LLM inference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E2B&lt;/td&gt;
&lt;td&gt;Isolated sandbox VMs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lingo.dev SDK&lt;/td&gt;
&lt;td&gt;String translation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lingo.dev MCP&lt;/td&gt;
&lt;td&gt;Setup instructions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Octokit&lt;/td&gt;
&lt;td&gt;GitHub REST API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prisma + Neon PostgreSQL&lt;/td&gt;
&lt;td&gt;Job storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RxJS&lt;/td&gt;
&lt;td&gt;SSE stream management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Three Principles That Guided Every Decision
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Reliability over breadth
&lt;/h3&gt;

&lt;p&gt;A demo that works perfectly for &lt;strong&gt;Next.js App Router&lt;/strong&gt; is worth more than a demo that claims to support five frameworks but breaks on all of them.&lt;/p&gt;

&lt;p&gt;Lingo.dev provides its deepest support for Next.js App Router. Its MCP server, compiler, and SDK are all tuned for this stack. Supporting Vite, Remix, or Pages Router would each require separate detection logic, different patching strategies, and independent testing — multiplying the surface area several times.&lt;/p&gt;

&lt;p&gt;In a hackathon, doing one thing flawlessly beats doing five things poorly.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Observable beats fast
&lt;/h3&gt;

&lt;p&gt;The live log stream isn't just a feature — it's core to the product experience. Watching the agent clone, detect, configure, translate, commit, and deploy in real time builds trust and makes the few minute wait feel productive, not idle.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. LLM as planner, not executor
&lt;/h3&gt;

&lt;p&gt;The Groq LLM decides &lt;strong&gt;what&lt;/strong&gt; to do (which arguments to pass). The NestJS server decides &lt;strong&gt;how&lt;/strong&gt; to do it (actual API calls, file operations, error handling). This separation prevents hallucination, avoids SDK timeout issues, and makes the pipeline deterministic and debuggable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Known Limitations (Honest Assessment)
&lt;/h2&gt;

&lt;p&gt;These are deliberate scope constraints, not bugs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Next.js App Router only&lt;/strong&gt; — Pages Router, Vite, Remix not supported&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hardcoded strings in JS logic are not translated&lt;/strong&gt; — Babel AST targets JSX text nodes and common attributes (&lt;code&gt;placeholder&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;alt&lt;/code&gt;, &lt;code&gt;aria-label&lt;/code&gt;). Strings in variables or API responses may be missed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Large repos may time out&lt;/strong&gt; — E2B sandboxes have a configurable timeout (default 10 min)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No monorepo support&lt;/strong&gt; — single-app repositories only&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Existing i18n setups may conflict&lt;/strong&gt; — repos already using &lt;code&gt;next-intl&lt;/code&gt; or &lt;code&gt;i18next&lt;/code&gt; may produce conflicts&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prisma 7 is a breaking change.&lt;/strong&gt; The &lt;code&gt;url&lt;/code&gt; field in &lt;code&gt;schema.prisma&lt;/code&gt; is no longer supported — datasource configuration moved to &lt;code&gt;prisma.config.ts&lt;/code&gt;. This caused real deployment pain that taught me to always check for major version breaking changes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LLM tool-calling is fragile unless you constrain it.&lt;/strong&gt; Showing an LLM 7 tools and hoping it calls them in order is a recipe for chaos. Showing it one tool at a time with &lt;code&gt;toolChoice: 'required'&lt;/code&gt; produces deterministic, reliable behavior.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSE is dramatically simpler than WebSockets&lt;/strong&gt; for one-way streaming. No connection management, no heartbeats, no reconnection protocol — just &lt;code&gt;text/event-stream&lt;/code&gt; and you're done.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;E2B sandboxes are a superpower&lt;/strong&gt; for agent systems. Running untrusted code? npm install with unknown dependencies? Git clone with arbitrary URLs? Put it all in a throwaway VM and don't think twice.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The MCP protocol is underrated.&lt;/strong&gt; Instead of the LLM hallucinating framework configuration, it gets verified instructions from Lingo.dev's own MCP server. This single integration eliminated an entire class of bugs.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Checkout the Video
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Demo Video:&lt;/strong&gt; &lt;a href="https://drive.google.com/drive/folders/1GW-W05pXK-dTD6qWeqy38LuvGFD2R1sI" rel="noopener noreferrer"&gt;Google Drive link&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Demo repo:&lt;/strong&gt; &lt;a href="https://github.com/Kashif-Rezwi/lingo-agent-demo-app" rel="noopener noreferrer"&gt;Kashif-Rezwi/lingo-agent-demo-app&lt;/a&gt; — a clean Next.js 14 App Router landing page built for testing.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sign into &lt;a href="https://lingo-agent.vercel.app" rel="noopener noreferrer"&gt;LingoAgent&lt;/a&gt; with GitHub&lt;/li&gt;
&lt;li&gt;Paste &lt;code&gt;https://github.com/Kashif-Rezwi/lingo-agent-demo-app&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select Japanese, French, and Arabic&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Start&lt;/strong&gt; and watch the agent work&lt;/li&gt;
&lt;li&gt;Review the GitHub PR and live Vercel preview&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://github.com/Kashif-Rezwi" rel="noopener noreferrer"&gt;Kashif Rezwi&lt;/a&gt; for the &lt;a href="https://lingo.dev" rel="noopener noreferrer"&gt;Lingo.dev Hackathon 2026&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>nextjs</category>
      <category>i18n</category>
      <category>hackathon</category>
    </item>
  </channel>
</rss>
