-
Notifications
You must be signed in to change notification settings - Fork 828
AI Blog Localization Tool #1859
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
AI Blog Localization Tool #1859
Conversation
|
hi @VaibhavNarayann your PR is missing changeset. Please also sign your commits |
4d28cff to
cdf4517
Compare
|
Hey @sumitsaurabh927 Thanks for the review! |
📝 WalkthroughWalkthroughThis 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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~35 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this 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-labelattributes.♿ 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
onClickhandlers 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/buttonis unnecessarily long. Since this file is in the samecomponent/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-standardtoast-closeattribute.The
toast-close=""attribute is non-standard HTML. If this is used for CSS selectors or testing, consider using adata-*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: UseLinkfor SPA navigation.
<a href="/">triggers a full reload. UsingLinkkeeps 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 (
ToasterandSonner) 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:
- Missing explicit return type
Promise<string>- Rejecting with only
stderrloses the error code and original error object- 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
@latestwith npx will fetch the newest version on every execution, potentially introducing breaking changes in production. Since lingo.dev is not listed inpackage.jsondependencies, consider pinning to a specific stable version (e.g.,npx lingo.dev@0.117.x runor 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, andtypescriptare build-time dependencies and should be indevDependenciesrather thandependencies. 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 alreadyasync, soawaita 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 inrunLingoto 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_DELAYis 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_TOASTcase callsaddToRemoveQueue, which schedules timeouts and dispatches actions. This makes the reducer impure and harder to test in isolation. Consider extracting this logic to thedismissfunction or a middleware-like pattern.
| "lucide-react": "^0.562.0", | ||
| "next-themes": "^0.4.6", | ||
| "radix-ui": "^1.4.3", | ||
| "react": "^19.2.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.
| # 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: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| <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> | ||
| ))} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| <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.
| 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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 -20Repository: lingodotdev/lingo.dev
Length of output: 178
🏁 Script executed:
cd community/ai-blog-post-localizer/client && ls -la && head -20 package.jsonRepository: 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" -nRepository: 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.tsRepository: 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" -nRepository: 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" -nRepository: 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.
| <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()} | ||
| > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| ```env | ||
| LINGO_API_KEY=your_lingo_api_key_here | ||
| PORT=4000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| ```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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.tsRepository: 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).
| const content = req.body; | ||
| if(typeof content !== 'object') { | ||
| return res.status(400).json({ | ||
| error: "Invaild Content" | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.
| 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".
| } catch(err) { | ||
| res.json({ | ||
| error: String(err) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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.tsRepository: lingodotdev/lingo.dev
Length of output: 120
🏁 Script executed:
cat -n community/ai-blog-post-localizer/server/src/index.ts | head -80Repository: 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.
| } 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.
| "types": [], | ||
| // For nodejs: | ||
| // "lib": ["esnext"], | ||
| // "types": ["node"], | ||
| // and npm install -D @types/node |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| "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.
This PR adds an AI-powered blog localization tool built using lingo.dev.
Features:
Label: community-submission
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.