Skip to content

Conversation

@VaibhavNarayann
Copy link

@VaibhavNarayann VaibhavNarayann commented Jan 23, 2026

This PR adds an AI-powered blog localization tool built using lingo.dev.

Features:

  • Generates en.json from user content
  • Translates content using lingo.dev
  • Displays translated output in the UI
  • Includes project-level README with setup instructions

Label: community-submission

Summary by CodeRabbit

  • New Features
    • Added AI Blog Post Localizer community example showcasing content localization workflows with lingo.dev. Users can paste blog content, automatically translate it into multiple languages, and view translated versions in a unified interface.

✏️ Tip: You can customize this high-level summary in your review settings.

@sumitsaurabh927
Copy link
Contributor

hi @VaibhavNarayann your PR is missing changeset.

Please also sign your commits

@VaibhavNarayann
Copy link
Author

Hey @sumitsaurabh927 Thanks for the review!
I’ve added the required changeset and re-signed the commits using SSH signing.
Please let me know if anything else is needed.

@coderabbitai
Copy link

coderabbitai bot commented Jan 26, 2026

📝 Walkthrough

Walkthrough

This PR introduces a complete AI Blog Post Localizer example project demonstrating content translation using lingo.dev. It includes a React client with UI components and layout sections, a Node.js/Express backend API with two endpoints, i18n configuration files, and necessary build and development configurations for both frontend and backend.

Changes

Cohort / File(s) Summary
Changelog & Documentation
.changeset/hip-moose-stop.md, community/ai-blog-post-localizer/README.md
Adds changelog entry for patch releases and project README documenting features, tech stack, prerequisites, and environment setup
Client Package Configuration
community/ai-blog-post-localizer/client/package.json, client/eslint.config.js, client/postcss.config.js, client/vite.config.ts
Defines client dependencies (React, Vite, Tailwind, Axios), ESLint rules for TypeScript/React, PostCSS with Tailwind/Autoprefixer, and Vite React plugin configuration
Client TypeScript & Build Configuration
client/tsconfig.json, client/tsconfig.app.json, client/tsconfig.node.json
Establishes TypeScript project references, app-specific compiler options (ES2022 target, strict mode), and node tooling configuration
Client Entry & Global Styles
client/index.html, client/src/main.tsx, client/src/index.css
HTML entry point, React root mounting, and comprehensive Tailwind-based CSS foundation with theme tokens, animations, and utility classes
Client App Structure
client/src/App.tsx, client/src/App.css, client/src/pages/Index.tsx, client/src/pages/NotFound.tsx
Root component with routing setup (Index and NotFound pages), React Query provider, tooltip provider, and toaster/sonner UI components; includes app-specific animations and styling
Client Navigation Components
client/src/component/Navbar.tsx, client/src/component/NavLink.tsx, client/src/component/Footer.tsx
Fixed responsive navbar with mobile menu toggle, styled NavLink wrapper with active state handling, and multi-column footer with social links
Client Section Components
client/src/component/HeroSection.tsx, client/src/component/FeaturesSection.tsx, client/src/component/HowItWorksSection.tsx, client/src/component/CTASection.tsx
Landing page sections: hero with content translation flow (dialog for paste/translate, state management for loading/results), features grid with 6 items, step-by-step how-it-works, and CTA with trust badges
Client UI Component Library
client/src/component/ui/button.tsx, client/src/component/ui/input.tsx, client/src/component/ui/textarea.tsx, client/src/component/ui/dialog.tsx, client/src/component/ui/alert-dialog.tsx, client/src/component/ui/accordion.tsx, client/src/component/ui/tooltip.tsx, client/src/component/ui/toast.tsx, client/src/component/ui/toaster.tsx, client/src/component/ui/sonner.tsx
Composable UI primitives built on Radix UI with Tailwind styling: button variants (default, destructive, outline, ghost, hero), form inputs, dialog/modal, toast notifications, and tooltips
Client Hooks & Utilities
client/src/hooks/use-mobile.tsx, client/src/hooks/use-toast.tsx, client/src/lib/utils.ts
Mobile breakpoint detection hook, reducer-based toast state management (single toast limit, auto-dismiss), and Tailwind class merging utility (clsx + tailwind-merge)
Client Ignore Rules
client/.gitignore
Standard ignore patterns for node_modules, build artifacts, logs, and IDE/OS files
Client Documentation
client/README.md
Minimal React + TypeScript + Vite template documentation with plugin guidance and ESLint configuration examples
Client Config
client/tailwind.config.ts
Tailwind configuration with dark mode class support, container defaults, extended theme (colors mapped to CSS vars, sidebar tokens), custom border radius, and accordion animations
Server Package Configuration
server/package.json
Node.js/Express server with TypeScript, CORS, and dotenv dependencies; build and dev scripts
Server TypeScript Configuration
server/tsconfig.json
Strict TypeScript config with Node ESNext target, source maps, declaration generation, and bundler-style module resolution
Server Implementation
server/src/index.ts, server/src/runLingo.ts
Express API with POST /content (writes blog content to en.json), GET /translate-content (runs lingo.dev CLI, reads es.json), and error handling; runLingo module wraps lingo.dev CLI execution as Promise
i18n Configuration & Resources
server/i18n.json, server/i18n/en.json, server/i18n/es.json
lingo.dev i18n schema defining source (en) and target (es) locales with JSON bucket rules; English and Spanish translation resource files

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Client as React Client
    participant Server as Express Server
    participant FileSystem as File System
    participant LingoService as lingo.dev CLI

    User->>Client: Paste blog content & click Translate
    Client->>Server: POST /content (with blog content)
    Server->>FileSystem: Write content to i18n/en.json
    FileSystem-->>Server: File written
    Server-->>Client: Success response
    
    Client->>Server: GET /translate-content
    Server->>LingoService: Execute: npx lingo.dev run
    LingoService->>LingoService: Translate en.json → es.json
    LingoService-->>Server: Execution complete
    Server->>FileSystem: Read i18n/es.json
    FileSystem-->>Server: Return translated content
    Server-->>Client: Return translated JSON
    
    Client->>Client: Update state with translated content
    Client->>User: Display translated content panel
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Poem

🐰 A rabbit hops through code so bright,
With components arranged just right,
From hero views to footer's call,
And translations flowing through it all,
Lingo.dev makes languages bloom—
A localized example room! 🌍

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete, missing most required template sections including detailed Changes list, Testing with specific test cases, Visuals checklist items, and the full Checklist requirements. Expand the description to include: detailed bullet points under Changes section, specific business logic tests with checkboxes, UI/UX verification checklist items (screenshots/video), and complete the Checklist section with proper status indicators.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'AI Blog Localization Tool' is a clear, concise description of the main feature being added and accurately reflects the overall purpose of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🤖 Fix all issues with AI agents
In `@community/ai-blog-post-localizer/client/package.json`:
- Around line 18-21: The package.json lists an incorrect dependency "radix-ui"
while the code imports scoped packages; update package.json by removing the
"radix-ui" entry or replace it with the exact scoped packages used (e.g.,
`@radix-ui/react-slot`, `@radix-ui/react-tooltip`, `@radix-ui/react-dialog`), then run
your package manager to install the correct packages and update lockfile; search
the codebase for imports of `@radix-ui/`* to ensure you added every required
scoped package and remove any unused entries.

In `@community/ai-blog-post-localizer/client/README.md`:
- Around line 1-16: Update the README.md to replace the generic Vite template
text with project-specific setup and usage: add a "Prerequisites" section
listing required tools, an "Environment Variables" section enumerating the
client and server env var names and purpose (e.g., API URL, keys, PORTs), a "Run
Locally" section with exact commands to install and run both the localizer
client and server (npm/yarn install, npm run dev/start), and a "Translation Flow
/ Usage" section that describes step-by-step how to use the
translation/localization workflow (how to start the UI, trigger translations,
where outputs are stored, and any manual steps). Also include troubleshooting
tips and links to any relevant scripts or endpoints used by the client/server so
contributors can run the localizer end-to-end.

In `@community/ai-blog-post-localizer/client/src/component/FeaturesSection.tsx`:
- Around line 57-68: The staggered animationDelay on the feature card wrapper in
FeaturesSection (the div with key={feature.title} and style={{ animationDelay:
`${index * 0.1}s` }}) has no effect because no animation class is present; add
the animation utility (e.g., include "animate-fade-in-up") to that div's
className so the CSS animation defined in index.css runs and the per-item
animationDelay takes effect.

In `@community/ai-blog-post-localizer/client/src/component/HeroSection.tsx`:
- Around line 141-155: The two Button instances in HeroSection.tsx call
handleTranslate and only check disabled={!blogUrl.trim()}, so users can still
trigger duplicate requests while loading; update both Button props to also
disable when loading (e.g., disabled={loading || !blogUrl.trim()}) and/or guard
handleTranslate to return early if loading to ensure clicks cannot send repeated
requests while a translation is in progress.
- Around line 12-35: Replace hard-coded API base URLs in the handleTranslate
function with a Vite environment variable: build the base URL from
import.meta.env.VITE_API_BASE_URL (falling back to a sensible default if needed)
and use it when calling axios.post and axios.get instead of
"http://localhost:4000"; update the two requests in handleTranslate (the
axios.post to "/content" and axios.get to "/translate-content") to prepend the
configured base URL so the component (HeroSection, handleTranslate) works across
environments.

In `@community/ai-blog-post-localizer/client/src/component/Navbar.tsx`:
- Around line 44-55: The mobile menu toggle button in the Navbar lacks an
accessible name and expanded state; update the button (where setIsOpen and
isOpen are used) to include an explicit accessible label (e.g.,
aria-label="Toggle mobile menu" or aria-labelledby) and add
aria-expanded={isOpen}; also assign a stable id to the mobile menu container
(the div rendered when isOpen is true) and reference it from the button via
aria-controls so screen readers can identify and track the menu state.

In `@community/ai-blog-post-localizer/client/src/hooks/use-toast.tsx`:
- Around line 166-177: In useToast, the React.useEffect currently depends on
[state], causing the effect to re-subscribe on every state change; change the
dependency array to [] so the effect runs only once on mount and cleanup runs on
unmount, keeping the stable setState subscription (refer to useToast, setState,
listeners, memoryState) — update the React.useEffect signature to use an empty
dependency array so listeners.push(setState) is called only once and the cleanup
removes that same setState.

In `@community/ai-blog-post-localizer/client/src/index.css`:
- Around line 1-6: Move the `@import` rule for the Inter font to the top of the
stylesheet so it appears before any `@tailwind` directives; specifically, place
the "@import
url('https://fonts.googleapis.com/css2?family=Inter:...&display=swap');" line
above the "@tailwind base;", "@tailwind components;", and "@tailwind utilities;"
directives to comply with CSS `@import` ordering and ensure the font is loaded
correctly.

In `@community/ai-blog-post-localizer/README.md`:
- Around line 1-48: Update the README to add Installation, Usage/Running,
Project Structure, API Endpoints and Troubleshooting sections: include a Project
Structure block showing top-level directories (client/ and server/),
Installation steps for both client and server (how to npm install in each),
Running instructions with commands to start dev servers for backend (e.g., npm
run dev in server) and frontend (e.g., npm run dev in client), a .env
explanation listing LINGO_API_KEY and PORT, and explicit API documentation for
the /content and /translate-content endpoints (HTTP method, purpose, expected
payload/response) so users can set up and exercise the translation workflow.
- Around line 46-48: The README.md contains an unclosed Markdown code block for
the env snippet (the block starting with ```env and containing LINGO_API_KEY and
PORT), which breaks rendering; fix it by adding the closing triple backticks
(```) after the PORT=4000 line so the env code block is properly terminated.

In `@community/ai-blog-post-localizer/server/src/index.ts`:
- Around line 62-65: The catch block that currently sends errors with res.json({
error: String(err) }) returns HTTP 200; change it to send a non-200 status
(e.g., use res.status(err?.status || 500).json({ error: String(err) })) so
clients receive an actual error status; update the catch handler in
server/src/index.ts (the catch(err) / res.json call) to set the response status
before sending the JSON error payload.
- Around line 24-29: The request-body validation using typeof content !==
'object' is too permissive because it allows null and arrays; update the check
around the content variable in the request handler to reject null and arrays
(e.g., ensure content !== null && !Array.isArray(content) && typeof content ===
'object') and return the 400 JSON when invalid, and fix the typo in the error
message from "Invaild Content" to "Invalid Content".
- Line 13: The PORT constant is initialized without a default and can be
undefined; change the initialization of PORT to provide a fallback (e.g., const
PORT = process.env.PORT ?? "3000" or parseInt(process.env.PORT || "3000")) and
ensure the value used with server.listen is numeric if required, then fix the
console.log message that currently contains a stray "{" by removing it so the
log prints like `Server listening on port ${PORT}` (update references in
index.ts around PORT and the server.listen/console.log call).

In `@community/ai-blog-post-localizer/server/tsconfig.json`:
- Around line 12-16: The tsconfig currently sets "types": [] which excludes
Node.js type defs and will break the server (usage of process/Buffer/modules);
update the tsconfig (where "types": [] is defined) to include Node types (e.g.,
set "types" to include "node" or remove the empty array) and ensure `@types/node`
is added as a dev dependency by running npm install -D `@types/node` so the
Express/Node server compiles correctly.
🧹 Nitpick comments (16)
community/ai-blog-post-localizer/client/src/component/Footer.tsx (1)

19-29: Add accessible labels to icon-only links.

Screen readers cannot convey the purpose of these social media links since they only contain icons without text or aria-label attributes.

♿ Proposed fix for accessibility
 <div className="flex gap-4">
-  <a href="#" className="text-muted-foreground hover:text-primary transition-colors">
+  <a href="#" className="text-muted-foreground hover:text-primary transition-colors" aria-label="Twitter">
     <Twitter className="w-5 h-5" />
   </a>
-  <a href="#" className="text-muted-foreground hover:text-primary transition-colors">
+  <a href="#" className="text-muted-foreground hover:text-primary transition-colors" aria-label="LinkedIn">
     <Linkedin className="w-5 h-5" />
   </a>
-  <a href="#" className="text-muted-foreground hover:text-primary transition-colors">
+  <a href="#" className="text-muted-foreground hover:text-primary transition-colors" aria-label="GitHub">
     <Github className="w-5 h-5" />
   </a>
 </div>
community/ai-blog-post-localizer/client/index.html (1)

7-7: Use a descriptive document title.

“client” is generic; consider naming it after the tool for better UX/bookmarks.

♻️ Suggested tweak
-    <title>client</title>
+    <title>AI Blog Localization Tool</title>
community/ai-blog-post-localizer/client/src/component/ui/button.tsx (1)

4-4: Minor formatting inconsistency in import statement.

There's a double space before the import path.

🔧 Suggested fix
-import {cn} from  "../../lib/utils"
+import { cn } from "../../lib/utils";
community/ai-blog-post-localizer/client/src/component/CTASection.tsx (1)

29-37: CTA buttons lack click handlers or navigation.

The "Start Free Trial" and "Schedule Demo" buttons are currently non-functional. For a demo/example project this is acceptable, but consider adding placeholder onClick handlers or wrapping with links if these should navigate somewhere.

community/ai-blog-post-localizer/client/src/component/ui/alert-dialog.tsx (1)

4-4: Simplify the relative import path.

The current import path ../../component/ui/button is unnecessarily long. Since this file is in the same component/ui/ directory, use a sibling import instead.

🔧 Suggested fix
-import { buttonVariants } from "../../component/ui/button";
+import { buttonVariants } from "./button";
community/ai-blog-post-localizer/client/src/component/ui/toast.tsx (1)

72-72: Non-standard toast-close attribute.

The toast-close="" attribute is non-standard HTML. If this is used for CSS selectors or testing, consider using a data-* attribute (e.g., data-toast-close) for better semantics and HTML validity.

🔧 Suggested fix
-    toast-close=""
+    data-toast-close=""
community/ai-blog-post-localizer/client/src/pages/NotFound.tsx (1)

1-2: Use Link for SPA navigation.

<a href="/"> triggers a full reload. Using Link keeps client-side routing intact. Please verify this aligns with your react-router setup.

♻️ Suggested change
-import { useLocation } from "react-router-dom";
+import { Link, useLocation } from "react-router-dom";
@@
-        <a href="/" className="text-primary underline hover:text-primary/90">
-          Return to Home
-        </a>
+        <Link to="/" className="text-primary underline hover:text-primary/90">
+          Return to Home
+        </Link>

Also applies to: 16-18

community/ai-blog-post-localizer/client/src/App.tsx (2)

3-3: Remove commented-out code.

This commented-out import is dead code with an incorrect path. It should be removed to keep the codebase clean.

🧹 Proposed fix
 import { Toaster } from "./component/ui/toaster";
 import { Toaster as Sonner } from "./component/ui/sonner";
-// import { TooltipProvider } from "../components/ui/tooltip";

 import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

16-17: Consider consolidating toast providers.

Two separate toast systems (Toaster and Sonner) are rendered simultaneously. While this may be intentional, it can lead to inconsistent UX if toasts from both systems appear with different styling. Consider using only one toast provider for consistency.

community/ai-blog-post-localizer/server/src/runLingo.ts (2)

3-9: Add explicit return type and improve error handling.

The function lacks type annotations and discards useful error information:

  1. Missing explicit return type Promise<string>
  2. Rejecting with only stderr loses the error code and original error object
  3. No timeout specified for the external process
♻️ Proposed fix
-export function runLingo() { 
-    return new Promise((resolve, reject)=> {
-        exec("npx lingo.dev@latest run", (error, stdout, stderr)=>{
-            if(error) return reject(stderr); 
-            resolve(stdout); 
+export function runLingo(): Promise<string> {
+    return new Promise((resolve, reject) => {
+        exec("npx lingo.dev@latest run", { timeout: 60000 }, (error, stdout, stderr) => {
+            if (error) {
+                return reject(new Error(`Lingo failed: ${stderr || error.message}`));
+            }
+            resolve(stdout);
         })
     })
-
 }

5-5: Pin lingo.dev to a specific version instead of @latest.

Using @latest with npx will fetch the newest version on every execution, potentially introducing breaking changes in production. Since lingo.dev is not listed in package.json dependencies, consider pinning to a specific stable version (e.g., npx lingo.dev@0.117.x run or similar) to ensure reproducible builds and avoid unexpected breaking changes.

community/ai-blog-post-localizer/server/package.json (1)

15-22: Move type definitions and TypeScript to devDependencies.

@types/cors, @types/express, and typescript are build-time dependencies and should be in devDependencies rather than dependencies. This reduces the production bundle size.

♻️ Proposed fix
   "dependencies": {
-    "@types/cors": "^2.8.19",
-    "@types/express": "^5.0.6",
     "cors": "^2.8.6",
     "dotenv": "^17.2.3",
-    "express": "^5.2.1",
-    "typescript": "^5.9.3"
-  }
+    "express": "^5.2.1"
+  },
+  "devDependencies": {
+    "@types/cors": "^2.8.19",
+    "@types/express": "^5.0.6",
+    "typescript": "^5.9.3"
+  }
 }
community/ai-blog-post-localizer/server/src/index.ts (2)

31-35: Avoid sync file writes in the request path.
Blocking I/O can stall concurrent requests; the handler is already async, so await a promise-based write.

♻️ Suggested change
-    fs.writeFileSync(
-        I18N_PATH_Source,
-        JSON.stringify(content, null, 2),
-        "utf-8"
-    )
+    await fs.promises.writeFile(
+        I18N_PATH_Source,
+        JSON.stringify(content, null, 2),
+        "utf-8"
+    );

52-55: Guard long-running lingo CLI and avoid sync reads.
runLingo() shells out per request; consider a timeout/serialization in runLingo to prevent hung or overlapping executions, and switch to async reads.

♻️ Suggested change
-    const data = fs.readFileSync(I18N_PATH_Target, "utf-8"); 
+    const data = await fs.promises.readFile(I18N_PATH_Target, "utf-8"); 
community/ai-blog-post-localizer/client/src/hooks/use-toast.tsx (2)

5-6: Consider reducing the toast removal delay.

TOAST_REMOVE_DELAY is set to ~16.67 minutes, which is unusually long for toast notifications. Typical values are 3-5 seconds. If manual dismissal is intended, consider renaming or documenting the rationale.


85-109: Side effects in reducer reduce testability.

The DISMISS_TOAST case calls addToRemoveQueue, which schedules timeouts and dispatches actions. This makes the reducer impure and harder to test in isolation. Consider extracting this logic to the dismiss function or a middleware-like pattern.

Comment on lines +18 to +21
"lucide-react": "^0.562.0",
"next-themes": "^0.4.6",
"radix-ui": "^1.4.3",
"react": "^19.2.0",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find Radix imports to confirm the correct package name
rg -n --glob '!**/node_modules/**' '@radix-ui/|radix-ui'

Repository: lingodotdev/lingo.dev

Length of output: 50377


Remove or replace the radix-ui dependency.

The codebase imports from scoped @radix-ui/* packages (e.g., @radix-ui/react-slot, @radix-ui/react-tooltip, @radix-ui/react-dialog), but package.json declares "radix-ui": "^1.4.3", which is a different package. Either remove this unused dependency or replace it with the specific @radix-ui/* packages being imported.

🤖 Prompt for AI Agents
In `@community/ai-blog-post-localizer/client/package.json` around lines 18 - 21,
The package.json lists an incorrect dependency "radix-ui" while the code imports
scoped packages; update package.json by removing the "radix-ui" entry or replace
it with the exact scoped packages used (e.g., `@radix-ui/react-slot`,
`@radix-ui/react-tooltip`, `@radix-ui/react-dialog`), then run your package manager
to install the correct packages and update lockfile; search the codebase for
imports of `@radix-ui/`* to ensure you added every required scoped package and
remove any unused entries.

Comment on lines +1 to +16
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## React Compiler

The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add project-specific setup/usage instructions.

This README is still the generic Vite template. Given the PR objective, please add guidance for running the localizer (client + server), required env vars, and how to use the translation flow.

🤖 Prompt for AI Agents
In `@community/ai-blog-post-localizer/client/README.md` around lines 1 - 16,
Update the README.md to replace the generic Vite template text with
project-specific setup and usage: add a "Prerequisites" section listing required
tools, an "Environment Variables" section enumerating the client and server env
var names and purpose (e.g., API URL, keys, PORTs), a "Run Locally" section with
exact commands to install and run both the localizer client and server (npm/yarn
install, npm run dev/start), and a "Translation Flow / Usage" section that
describes step-by-step how to use the translation/localization workflow (how to
start the UI, trigger translations, where outputs are stored, and any manual
steps). Also include troubleshooting tips and links to any relevant scripts or
endpoints used by the client/server so contributors can run the localizer
end-to-end.

Comment on lines +57 to +68
<div
key={feature.title}
className="group p-6 rounded-2xl bg-gradient-card border border-border hover:border-primary/30 transition-all duration-300 hover:shadow-glow"
style={{ animationDelay: `${index * 0.1}s` }}
>
<div className="w-12 h-12 rounded-xl bg-gradient-primary flex items-center justify-center mb-4 group-hover:scale-110 transition-transform duration-300">
<feature.icon className="w-6 h-6 text-primary-foreground" />
</div>
<h3 className="text-xl font-semibold mb-2 text-foreground">{feature.title}</h3>
<p className="text-muted-foreground">{feature.description}</p>
</div>
))}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Animation delay has no effect without an animation class.

The animationDelay style is set but no animation class is applied to the element. The staggered delay will have no visible effect.

Suggested fix: Add an animation class
             <div
               key={feature.title}
-              className="group p-6 rounded-2xl bg-gradient-card border border-border hover:border-primary/30 transition-all duration-300 hover:shadow-glow"
+              className="group p-6 rounded-2xl bg-gradient-card border border-border hover:border-primary/30 transition-all duration-300 hover:shadow-glow animate-fade-in-up"
               style={{ animationDelay: `${index * 0.1}s` }}
             >

This uses the animate-fade-in-up utility defined in index.css.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div
key={feature.title}
className="group p-6 rounded-2xl bg-gradient-card border border-border hover:border-primary/30 transition-all duration-300 hover:shadow-glow"
style={{ animationDelay: `${index * 0.1}s` }}
>
<div className="w-12 h-12 rounded-xl bg-gradient-primary flex items-center justify-center mb-4 group-hover:scale-110 transition-transform duration-300">
<feature.icon className="w-6 h-6 text-primary-foreground" />
</div>
<h3 className="text-xl font-semibold mb-2 text-foreground">{feature.title}</h3>
<p className="text-muted-foreground">{feature.description}</p>
</div>
))}
<div
key={feature.title}
className="group p-6 rounded-2xl bg-gradient-card border border-border hover:border-primary/30 transition-all duration-300 hover:shadow-glow animate-fade-in-up"
style={{ animationDelay: `${index * 0.1}s` }}
>
<div className="w-12 h-12 rounded-xl bg-gradient-primary flex items-center justify-center mb-4 group-hover:scale-110 transition-transform duration-300">
<feature.icon className="w-6 h-6 text-primary-foreground" />
</div>
<h3 className="text-xl font-semibold mb-2 text-foreground">{feature.title}</h3>
<p className="text-muted-foreground">{feature.description}</p>
</div>
))}
🤖 Prompt for AI Agents
In `@community/ai-blog-post-localizer/client/src/component/FeaturesSection.tsx`
around lines 57 - 68, The staggered animationDelay on the feature card wrapper
in FeaturesSection (the div with key={feature.title} and style={{
animationDelay: `${index * 0.1}s` }}) has no effect because no animation class
is present; add the animation utility (e.g., include "animate-fade-in-up") to
that div's className so the CSS animation defined in index.css runs and the
per-item animationDelay takes effect.

Comment on lines +12 to +35
import axios from "axios";

import {Input} from "../component/ui/input";

import { Textarea } from "../component/ui/textarea";

const HeroSection = () => {
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [blogUrl, setBlogUrl] = useState("");
const [loading, setLoading] = useState(false);
const [translatedContent, setTranslatedContent] = useState<any>(null)

const handleTranslate = async () => {
try {
setLoading(true)

await axios.post("http://localhost:4000/content", {
content_1: blogUrl,
});


const get = await axios.get("http://localhost:4000/translate-content");

setTranslatedContent(get.data.content);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd community/ai-blog-post-localizer && find . -type f -name "package.json" -o -name "vite.config.*" -o -name "tsconfig.json" | head -20

Repository: lingodotdev/lingo.dev

Length of output: 178


🏁 Script executed:

cd community/ai-blog-post-localizer/client && ls -la && head -20 package.json

Repository: lingodotdev/lingo.dev

Length of output: 1569


🏁 Script executed:

find community/ai-blog-post-localizer -type f -name "HeroSection.tsx" -exec head -50 {} +

Repository: lingodotdev/lingo.dev

Length of output: 1352


🏁 Script executed:

grep -r "localhost:4000" community/ai-blog-post-localizer/client --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" -n

Repository: lingodotdev/lingo.dev

Length of output: 343


🏁 Script executed:

find community/ai-blog-post-localizer -type f \( -name ".env*" -o -name "vite.config.*" \)

Repository: lingodotdev/lingo.dev

Length of output: 119


🏁 Script executed:

cat community/ai-blog-post-localizer/client/vite.config.ts

Repository: lingodotdev/lingo.dev

Length of output: 197


🏁 Script executed:

find community/ai-blog-post-localizer -type f -name ".env*"

Repository: lingodotdev/lingo.dev

Length of output: 47


🏁 Script executed:

grep -r "http://" community/ai-blog-post-localizer/client/src --include="*.tsx" --include="*.ts" -n

Repository: lingodotdev/lingo.dev

Length of output: 343


🏁 Script executed:

grep -r "import.meta.env" community/ai-blog-post-localizer/client/src --include="*.tsx" --include="*.ts" -n

Repository: lingodotdev/lingo.dev

Length of output: 47


Remove hard-coded API base URLs and use environment variables.

The hard-coded http://localhost:4000 URLs will fail in production and non-local environments. Since this is a Vite project, use import.meta.env.VITE_API_BASE_URL to make the API endpoint configurable.

🔧 Suggested change
+const API_BASE_URL = (import.meta.env.VITE_API_BASE_URL ?? "").replace(/\/$/, "");
@@
-     await axios.post("http://localhost:4000/content", {
+     await axios.post(`${API_BASE_URL}/content`, {
        content_1: blogUrl,
      });
@@
-    const get = await axios.get("http://localhost:4000/translate-content");
+    const get = await axios.get(`${API_BASE_URL}/translate-content`);
🤖 Prompt for AI Agents
In `@community/ai-blog-post-localizer/client/src/component/HeroSection.tsx` around
lines 12 - 35, Replace hard-coded API base URLs in the handleTranslate function
with a Vite environment variable: build the base URL from
import.meta.env.VITE_API_BASE_URL (falling back to a sensible default if needed)
and use it when calling axios.post and axios.get instead of
"http://localhost:4000"; update the two requests in handleTranslate (the
axios.post to "/content" and axios.get to "/translate-content") to prepend the
configured base URL so the component (HeroSection, handleTranslate) works across
environments.

Comment on lines +141 to +155
<Button
variant="hero"
className="w-full"
onClick={handleTranslate}
disabled={!blogUrl.trim()}
>
<Globe className="w-4 h-4 mr-2" />
{loading ? "Translating..." : "Translate"}
</Button>
<Button
variant="hero"
className="flex mx-auto"
onClick={handleTranslate}
disabled={!blogUrl.trim()}
>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Prevent duplicate translation requests while loading.

Both buttons remain clickable during loading, which can send repeated requests.

✅ Suggested change
-              disabled={!blogUrl.trim()}
+              disabled={loading || !blogUrl.trim()}
@@
-              disabled={!blogUrl.trim()}
+              disabled={loading || !blogUrl.trim()}
🤖 Prompt for AI Agents
In `@community/ai-blog-post-localizer/client/src/component/HeroSection.tsx` around
lines 141 - 155, The two Button instances in HeroSection.tsx call
handleTranslate and only check disabled={!blogUrl.trim()}, so users can still
trigger duplicate requests while loading; update both Button props to also
disable when loading (e.g., disabled={loading || !blogUrl.trim()}) and/or guard
handleTranslate to return early if loading to ensure clicks cannot send repeated
requests while a translation is in progress.

Comment on lines +46 to +48
```env
LINGO_API_KEY=your_lingo_api_key_here
PORT=4000
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Close the code block.

The env code block is not properly closed with closing backticks, which will cause rendering issues.

📝 Proposed fix
 ```env
 LINGO_API_KEY=your_lingo_api_key_here
 PORT=4000
+```
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```env
LINGO_API_KEY=your_lingo_api_key_here
PORT=4000
🤖 Prompt for AI Agents
In `@community/ai-blog-post-localizer/README.md` around lines 46 - 48, The
README.md contains an unclosed Markdown code block for the env snippet (the
block starting with ```env and containing LINGO_API_KEY and PORT), which breaks
rendering; fix it by adding the closing triple backticks (```) after the
PORT=4000 line so the env code block is properly terminated.

dotenv.config();
app.use(cors());
app.use(express.json());
const PORT = process.env.PORT;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "index.ts" -path "*/ai-blog-post-localizer/server/src/*"

Repository: lingodotdev/lingo.dev

Length of output: 119


🏁 Script executed:

cat -n ./community/ai-blog-post-localizer/server/src/index.ts

Repository: lingodotdev/lingo.dev

Length of output: 1947


Fix the PORT initialization and console.log message.
PORT should have a default value to prevent undefined, and the console.log message has a malformed string (the opening { should be removed).

Suggested fix
-const PORT = process.env.PORT; 
+const PORT = process.env.PORT || 3000; 
...
-app.listen(PORT, ()=>{
-    console.log(`{Server Started at Port: ${PORT}`)
-})
+app.listen(PORT, () => {
+    console.log(`Server started at port ${PORT}`);
+});
🤖 Prompt for AI Agents
In `@community/ai-blog-post-localizer/server/src/index.ts` at line 13, The PORT
constant is initialized without a default and can be undefined; change the
initialization of PORT to provide a fallback (e.g., const PORT =
process.env.PORT ?? "3000" or parseInt(process.env.PORT || "3000")) and ensure
the value used with server.listen is numeric if required, then fix the
console.log message that currently contains a stray "{" by removing it so the
log prints like `Server listening on port ${PORT}` (update references in
index.ts around PORT and the server.listen/console.log call).

Comment on lines +24 to +29
const content = req.body;
if(typeof content !== 'object') {
return res.status(400).json({
error: "Invaild Content"
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find and read the target file
fd "index.ts" community/ai-blog-post-localizer/server/src/ -x cat -n {}

Repository: lingodotdev/lingo.dev

Length of output: 1947


Tighten request-body validation (null/arrays slip through).

typeof checks allow null and arrays to pass validation—both would be written to the translation file instead of being rejected. Additionally, the error message contains a typo.

🔧 Suggested validation fix
-    const content = req.body; 
-    if(typeof content !== 'object') {
-        return res.status(400).json({
-            error: "Invaild Content"
-        }); 
-    }
+    const content = req.body; 
+    if (!content || Array.isArray(content) || typeof content !== "object") {
+        return res.status(400).json({
-            error: "Invalid content"
+            error: "Invalid Content"
+        });
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const content = req.body;
if(typeof content !== 'object') {
return res.status(400).json({
error: "Invaild Content"
});
}
const content = req.body;
if (!content || Array.isArray(content) || typeof content !== "object") {
return res.status(400).json({
error: "Invalid Content"
});
}
🤖 Prompt for AI Agents
In `@community/ai-blog-post-localizer/server/src/index.ts` around lines 24 - 29,
The request-body validation using typeof content !== 'object' is too permissive
because it allows null and arrays; update the check around the content variable
in the request handler to reject null and arrays (e.g., ensure content !== null
&& !Array.isArray(content) && typeof content === 'object') and return the 400
JSON when invalid, and fix the typo in the error message from "Invaild Content"
to "Invalid Content".

Comment on lines +62 to +65
} catch(err) {
res.json({
error: String(err)
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "index.ts" | grep -E "ai-blog-post-localizer.*server.*src"

Repository: lingodotdev/lingo.dev

Length of output: 119


🏁 Script executed:

wc -l community/ai-blog-post-localizer/server/src/index.ts

Repository: lingodotdev/lingo.dev

Length of output: 120


🏁 Script executed:

cat -n community/ai-blog-post-localizer/server/src/index.ts | head -80

Repository: lingodotdev/lingo.dev

Length of output: 1947


Return non-200 status on translate failures. Clients shouldn't treat errors as successful responses. Currently, the error handler returns HTTP 200, which masks failures.

🔧 Suggested change
-    } catch(err) {
-        res.json({
-            error: String(err)
-        })
-    }
+    } catch (err) {
+        res.status(500).json({
+            error: String(err)
+        });
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch(err) {
res.json({
error: String(err)
})
} catch (err) {
res.status(500).json({
error: String(err)
});
}
🤖 Prompt for AI Agents
In `@community/ai-blog-post-localizer/server/src/index.ts` around lines 62 - 65,
The catch block that currently sends errors with res.json({ error: String(err)
}) returns HTTP 200; change it to send a non-200 status (e.g., use
res.status(err?.status || 500).json({ error: String(err) })) so clients receive
an actual error status; update the catch handler in server/src/index.ts (the
catch(err) / res.json call) to set the response status before sending the JSON
error payload.

Comment on lines +12 to +16
"types": [],
// For nodejs:
// "lib": ["esnext"],
// "types": ["node"],
// and npm install -D @types/node
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Enable Node.js types for the server project.

The types: [] array explicitly excludes all type definitions, including Node.js types. For an Express/Node.js server, this will cause TypeScript errors when using Node globals like process, Buffer, or module-level variables.

Suggested fix
-    "types": [],
-    // For nodejs:
-    // "lib": ["esnext"],
-    // "types": ["node"],
-    // and npm install -D `@types/node`
+    "lib": ["esnext"],
+    "types": ["node"],

Ensure @types/node is installed as a dev dependency:

npm install -D `@types/node`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"types": [],
// For nodejs:
// "lib": ["esnext"],
// "types": ["node"],
// and npm install -D @types/node
"lib": ["esnext"],
"types": ["node"],
🤖 Prompt for AI Agents
In `@community/ai-blog-post-localizer/server/tsconfig.json` around lines 12 - 16,
The tsconfig currently sets "types": [] which excludes Node.js type defs and
will break the server (usage of process/Buffer/modules); update the tsconfig
(where "types": [] is defined) to include Node types (e.g., set "types" to
include "node" or remove the empty array) and ensure `@types/node` is added as a
dev dependency by running npm install -D `@types/node` so the Express/Node server
compiles correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants