-
Notifications
You must be signed in to change notification settings - Fork 825
Add community demo: error localization engine (Closes #1761) #1880
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?
Conversation
📝 WalkthroughWalkthroughThis PR introduces a complete error localization demo application in the Changes
Sequence DiagramsequenceDiagram
participant Client as User/Browser
participant Frontend as Frontend App
participant Server as Express Server
participant Localizer as Localizer Middleware
participant AuthRoute as Auth Route
participant LingoClient as Lingo Client
participant LingoAPI as Lingo.dev API
Client->>Frontend: Select language & submit signup form
Frontend->>Server: POST /api/auth/signup?lang=es (email, password)
Server->>Localizer: Middleware processes request
Localizer->>Localizer: Extract language from query/header
Localizer->>Server: Attach req.targetLang = 'es'
Server->>AuthRoute: Route handler validates input
AuthRoute->>AuthRoute: Validate email & password format
alt Validation fails
AuthRoute->>LingoClient: localizeError(errorMsg, 'es')
LingoClient->>LingoAPI: POST localize (message, targetLocale='es')
LingoAPI-->>LingoClient: Translated error
LingoClient-->>AuthRoute: Localized error response
AuthRoute-->>Server: 400/409 + localized error
else Validation passes
AuthRoute->>LingoClient: localizeError(successMsg, 'es')
LingoClient->>LingoAPI: POST localize (message, targetLocale='es')
LingoAPI-->>LingoClient: Translated success message
LingoClient-->>AuthRoute: Localized success response
AuthRoute-->>Server: 200 + localized success message
end
Server-->>Frontend: JSON response (localized content)
Frontend-->>Client: Display localized error/success message
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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: 4
🤖 Fix all issues with AI agents
In `@community/error-localization-engine/middleware/localizer.js`:
- Around line 7-20: Normalize the derived lang before assigning req.targetLang:
if req.query.lang is an array or object, coerce it to a string by taking the
first element/value; when parsing req.headers['accept-language'], split on ','
then drop any quality suffix by splitting on ';' and trimming the result;
lowercase or otherwise normalize the token (e.g., toLowerCase()) and optionally
validate it against a simple BCP-47-ish pattern before assigning to
req.targetLang; finally fall back to 'en' if the normalized value is empty or
invalid. Ensure you update the logic around req.query.lang, acceptLang (the
variable from req.headers['accept-language']), and the final assignment to
req.targetLang.
In `@community/error-localization-engine/README.md`:
- Around line 40-90: Add a short clarifying note under the "🧪 Testing the API"
section (before the examples for English/Spanish/French) that explains if
LINGO_API_KEY is not set the server runs in mock mode and localized responses
may be mock-prefixed (e.g., "[es] ..." or "[fr] ...") rather than fully
translated; mention that real translations are returned when LINGO_API_KEY is
provided and that language detection still follows the `?lang=xx` query
parameter and `Accept-Language` header priority rules already described.
In `@community/error-localization-engine/routes/auth.js`:
- Around line 73-78: The token is being passed into localizeError (see
localizeError call creating successMsg) which may try to translate it; change
the flow so you first call localizeError with only translatable fields (e.g.,
message) to get the localized payload, then attach the non-translatable token
field (e.g., token: "fake-jwt-token-123456") onto the returned object before
sending res.json, ensuring localizeError is not given the token.
In `@community/error-localization-engine/verify.js`:
- Around line 1-72: The script currently hardcodes port 3000 and always expects
mock-prefixed localization; update runTests to require('dotenv').config(), build
baseURL using const port = process.env.PORT || 3000 and const baseURL =
`http://127.0.0.1:${port}/api/auth`, and detect mock mode via a boolean like
const isMock = !process.env.LINGO_API_KEY || process.env.LINGO_API_KEY ===
'your_lingo_api_key_here'; then adjust the localization assertions (in the POST
/signup error checks and success check) to only expect the mock prefix
'[es]'/'[fr]' when isMock is true, otherwise validate that translated messages
exist without assuming the mock prefix.
🧹 Nitpick comments (4)
community/error-localization-engine/package.json (1)
6-9: Declare a minimum Node version fornode --watch.The
devscript depends onnode --watch, which isn’t available in older Node LTS. Consider adding anengines.nodeconstraint (or a fallback dev script) to prevent confusing runtime errors.♻️ Suggested adjustment
"scripts": { "start": "node server.js", "dev": "node --watch server.js" }, + "engines": { + "node": ">=18.11" + }, "dependencies": {community/error-localization-engine/server.js (1)
29-34: Move dynamic require to module level.The
require('./lingoClient')inside the route handler is evaluated on every request. Move it to the top with other imports for efficiency.Suggested fix
const localizer = require('./middleware/localizer'); const authRoutes = require('./routes/auth'); +const { localizeError } = require('./lingoClient'); const app = express();Then update the route:
// Root endpoint app.get('/', async (req, res) => { - const { localizeError } = require('./lingoClient'); const msg = await localizeError("Welcome to the Error Localization Engine Demo API!", req.targetLang); res.send(msg); });community/error-localization-engine/public/app.js (1)
17-24: Consider encoding the language parameter.While the current language values (en, es, fr, de, hi, ja) are safe, using
encodeURIComponentis a good defensive practice for query parameters.Suggested fix
- const response = await fetch(`/api/auth/signup?lang=${language}`, { + const response = await fetch(`/api/auth/signup?lang=${encodeURIComponent(language)}`, {community/error-localization-engine/lingoClient.js (1)
32-42: Mock mode doesn't handle nested objects.The mock translation only processes top-level string values. Nested objects are copied as-is, which may produce inconsistent behavior between mock and API modes. This is acceptable for the demo's use case (flat error objects), but worth documenting.
For a more robust mock (optional enhancement):
Recursive mock translation
+ const mockTranslateObject = (obj) => { + const translated = {}; + for (const [key, value] of Object.entries(obj)) { + if (typeof value === 'string') { + translated[key] = mockTranslate(value); + } else if (typeof value === 'object' && value !== null) { + translated[key] = mockTranslateObject(value); + } else { + translated[key] = value; + } + } + return translated; + }; + if (isString) { return mockTranslate(content); } else { - const translated = {}; - for (const [key, value] of Object.entries(content)) { - if (typeof value === 'string') { - translated[key] = mockTranslate(value); - } else { - translated[key] = value; - } - } - return translated; + return mockTranslateObject(content); }
| // 1. Check query param ?lang=xx | ||
| let lang = req.query.lang; | ||
|
|
||
| // 2. Check Accept-Language header | ||
| if (!lang && req.headers['accept-language']) { | ||
| // Simple parse: take the first language code (e.g., 'en-US,en;q=0.9' -> 'en-US') | ||
| // And maybe just take the first 2 chars or the full code. | ||
| // Lingo.dev likely supports standard BCP-47. | ||
| const acceptLang = req.headers['accept-language'].split(',')[0].trim(); | ||
| lang = acceptLang; | ||
| } | ||
|
|
||
| // 3. Default to English | ||
| req.targetLang = lang || 'en'; |
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.
Normalize lang before assigning req.targetLang.
req.query.lang can be an array/object and Accept-Language can include ;q= values. Normalizing avoids unexpected locale strings downstream.
🔧 Proposed normalization
- let lang = req.query.lang;
+ let lang = req.query.lang;
+ if (Array.isArray(lang)) lang = lang[0];
+ if (typeof lang === 'string') lang = lang.trim();
+ if (!lang) lang = undefined;
// 2. Check Accept-Language header
if (!lang && req.headers['accept-language']) {
- const acceptLang = req.headers['accept-language'].split(',')[0].trim();
+ const acceptLang = req.headers['accept-language']
+ .split(',')[0]
+ .split(';')[0]
+ .trim();
lang = acceptLang;
}🤖 Prompt for AI Agents
In `@community/error-localization-engine/middleware/localizer.js` around lines 7 -
20, Normalize the derived lang before assigning req.targetLang: if
req.query.lang is an array or object, coerce it to a string by taking the first
element/value; when parsing req.headers['accept-language'], split on ',' then
drop any quality suffix by splitting on ';' and trimming the result; lowercase
or otherwise normalize the token (e.g., toLowerCase()) and optionally validate
it against a simple BCP-47-ish pattern before assigning to req.targetLang;
finally fall back to 'en' if the normalized value is empty or invalid. Ensure
you update the logic around req.query.lang, acceptLang (the variable from
req.headers['accept-language']), and the final assignment to req.targetLang.
| ## 🧪 Testing the API | ||
|
|
||
| The API detects language preference via: | ||
| 1. `?lang=xx` query parameter (High priority) | ||
| 2. `Accept-Language` header (Low priority) | ||
|
|
||
| ### 1. English (Default) | ||
| **Request:** | ||
| ```bash | ||
| curl -X POST http://localhost:3000/api/auth/signup \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"email": "invalid-email", "password": "123"}' | ||
| ``` | ||
| **Response:** | ||
| ```json | ||
| { | ||
| "error": "Validation Failed", | ||
| "message": "Please provide a valid email address." | ||
| } | ||
| ``` | ||
|
|
||
| ### 2. Spanish (via Query Param) | ||
| **Request:** | ||
| ```bash | ||
| curl -X POST "http://localhost:3000/api/auth/signup?lang=es" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{"email": "invalid-email", "password": "123"}' | ||
| ``` | ||
| **Response (Localized):** | ||
| ```json | ||
| { | ||
| "error": "Falló la validación", | ||
| "message": "Por favor proporcione una dirección de correo electrónico válida." | ||
| } | ||
| ``` | ||
|
|
||
| ### 3. French (via Header) | ||
| **Request:** | ||
| ```bash | ||
| curl -X POST http://localhost:3000/api/auth/signup \ | ||
| -H "Content-Type: application/json" \ | ||
| -H "Accept-Language: fr" \ | ||
| -d '{"email": "test@example.com", "password": "short"}' | ||
| ``` | ||
| **Response (Localized):** | ||
| ```json | ||
| { | ||
| "error": "Échec de la validation", | ||
| "message": "Le mot de passe doit contenir au moins 6 caractères." | ||
| } | ||
| ``` |
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.
Clarify mock‑mode output in the API examples.
If the demo runs without LINGO_API_KEY, responses may be mock‑localized (e.g., prefixed with [es]/[fr]) rather than fully translated. A short note here would reduce confusion for first‑time testers.
📝 Suggested doc note
The API detects language preference via:
1. `?lang=xx` query parameter (High priority)
2. `Accept-Language` header (Low priority)
+
+> Note: If `LINGO_API_KEY` is not set, the demo uses mock localization and responses may be prefixed with `[xx]`.🤖 Prompt for AI Agents
In `@community/error-localization-engine/README.md` around lines 40 - 90, Add a
short clarifying note under the "🧪 Testing the API" section (before the
examples for English/Spanish/French) that explains if LINGO_API_KEY is not set
the server runs in mock mode and localized responses may be mock-prefixed (e.g.,
"[es] ..." or "[fr] ...") rather than fully translated; mention that real
translations are returned when LINGO_API_KEY is provided and that language
detection still follows the `?lang=xx` query parameter and `Accept-Language`
header priority rules already described.
| const successMsg = await localizeError({ | ||
| message: "Login successful.", | ||
| token: "fake-jwt-token-123456" | ||
| }, lang); | ||
|
|
||
| res.json(successMsg); |
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.
Token may be incorrectly localized.
The token field is passed to localizeError, which may attempt to translate it. Non-translatable fields like tokens should be added after localization.
Suggested fix
- const successMsg = await localizeError({
- message: "Login successful.",
- token: "fake-jwt-token-123456"
- }, lang);
+ const successMsg = await localizeError({
+ message: "Login successful."
+ }, lang);
+ successMsg.token = "fake-jwt-token-123456";
res.json(successMsg);📝 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 successMsg = await localizeError({ | |
| message: "Login successful.", | |
| token: "fake-jwt-token-123456" | |
| }, lang); | |
| res.json(successMsg); | |
| const successMsg = await localizeError({ | |
| message: "Login successful." | |
| }, lang); | |
| successMsg.token = "fake-jwt-token-123456"; | |
| res.json(successMsg); |
🤖 Prompt for AI Agents
In `@community/error-localization-engine/routes/auth.js` around lines 73 - 78, The
token is being passed into localizeError (see localizeError call creating
successMsg) which may try to translate it; change the flow so you first call
localizeError with only translatable fields (e.g., message) to get the localized
payload, then attach the non-translatable token field (e.g., token:
"fake-jwt-token-123456") onto the returned object before sending res.json,
ensuring localizeError is not given the token.
| const axios = require('axios'); | ||
|
|
||
| async function runTests() { | ||
| const baseURL = 'http://127.0.0.1:3000/api/auth'; | ||
|
|
||
| console.log('--- Starting Verification ---'); | ||
|
|
||
| // Test 1: Signup Validation Error (Spanish) | ||
| try { | ||
| console.log('\n[Test 1] POST /signup?lang=es (Invalid Email)'); | ||
| await axios.post(`${baseURL}/signup?lang=es`, { | ||
| email: 'invalid', | ||
| password: '123' | ||
| }); | ||
| console.log('❌ FAIL: Expected 400 error, got success'); | ||
| } catch (err) { | ||
| if (err.response) { | ||
| console.log('Response Status:', err.response.status); | ||
| console.log('Response Body:', err.response.data); | ||
| const msg = err.response.data.message || ''; | ||
| if (msg.includes('[es]')) { | ||
| console.log('✅ PASS: Localized to Spanish'); | ||
| } else { | ||
| console.log('❌ FAIL: Not localized correctly'); | ||
| } | ||
| } else { | ||
| console.error('❌ FAIL: Network/Server Error:', err.message); | ||
| } | ||
| } | ||
|
|
||
| // Test 2: Signup Validation Error (French via Header) | ||
| try { | ||
| console.log('\n[Test 2] POST /signup (Header: fr) (Short Password)'); | ||
| await axios.post(`${baseURL}/signup`, { | ||
| email: 'valid@email.com', | ||
| password: '123' | ||
| }, { | ||
| headers: { 'Accept-Language': 'fr' } | ||
| }); | ||
| console.log('❌ FAIL: Expected 400 error, got success'); | ||
| } catch (err) { | ||
| if (err.response) { | ||
| console.log('Response Status:', err.response.status); | ||
| console.log('Response Body:', err.response.data); | ||
| const msg = err.response.data.message || ''; | ||
| if (msg.includes('[fr]')) { | ||
| console.log('✅ PASS: Localized to French'); | ||
| } else { | ||
| console.log('❌ FAIL: Not localized correctly'); | ||
| } | ||
| } else { | ||
| console.error('❌ FAIL: Network/Server Error:', err.message); | ||
| } | ||
| } | ||
|
|
||
| // Test 3: Success Case (English Default) | ||
| try { | ||
| console.log('\n[Test 3] POST /signup (Valid Data)'); | ||
| const res = await axios.post(`${baseURL}/signup`, { | ||
| email: `user${Date.now()}@example.com`, | ||
| password: 'securepassword123' | ||
| }); | ||
| console.log('Response Status:', res.status); | ||
| console.log('Response Body:', res.data); | ||
| if (res.data.message && !res.data.message.includes('[')) { | ||
| console.log('✅ PASS: English default (no mock prefix)'); | ||
| } else { | ||
| console.log('❌ FAIL: Unexpected content in success message'); | ||
| } | ||
| } catch (err) { | ||
| console.error('❌ FAIL: Unexpected Error:', err.response ? err.response.data : err.message); | ||
| } |
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:
# First, locate the verify.js file and examine its current state
find . -name "verify.js" -type f 2>/dev/null | head -20Repository: lingodotdev/lingo.dev
Length of output: 112
🏁 Script executed:
# Check for environment variable configuration patterns in the project
find . -name ".env*" -o -name "*.config.js" -o -name "config.js" | head -20Repository: lingodotdev/lingo.dev
Length of output: 138
🏁 Script executed:
# Look for PORT references in the codebase
rg "PORT|port" --type js --type json -B 2 -A 2 | head -100Repository: lingodotdev/lingo.dev
Length of output: 5155
🏁 Script executed:
# Check package.json for dependencies
find . -name "package.json" -type f | head -5Repository: lingodotdev/lingo.dev
Length of output: 266
🏁 Script executed:
# Look for LINGO_API_KEY references
rg "LINGO_API_KEY|lingo" --type js -B 2 -A 2 | head -50Repository: lingodotdev/lingo.dev
Length of output: 3139
🏁 Script executed:
# Check the .env.example file to see what environment variables are expected
cat ./community/error-localization-engine/.env.exampleRepository: lingodotdev/lingo.dev
Length of output: 106
🏁 Script executed:
# Examine the server.js file to see how the port is configured
cat -n ./community/error-localization-engine/server.js | head -50Repository: lingodotdev/lingo.dev
Length of output: 1694
🏁 Script executed:
# Check the lingoClient.js to understand the LINGO_API_KEY behavior
cat -n ./community/error-localization-engine/lingoClient.jsRepository: lingodotdev/lingo.dev
Length of output: 2380
🏁 Script executed:
# Check the package.json in the error-localization-engine folder
cat ./community/error-localization-engine/package.jsonRepository: lingodotdev/lingo.dev
Length of output: 458
🏁 Script executed:
# Check how verify.js is meant to be run (look for npm scripts or documentation)
cat ./community/error-localization-engine/package.jsonRepository: lingodotdev/lingo.dev
Length of output: 458
🏁 Script executed:
# Check if there's a README or any documentation about running verify.js
find ./community/error-localization-engine -name "README*" -o -name "*.md" | xargs cat 2>/dev/nullRepository: lingodotdev/lingo.dev
Length of output: 2432
🏁 Script executed:
# Check the current verify.js file completely to see the full context
cat -n ./community/error-localization-engine/verify.jsRepository: lingodotdev/lingo.dev
Length of output: 3326
Add environment variable support for PORT and improve mock/live mode detection.
The hardcoded port 3000 bypasses the server's environment-based configuration (which respects process.env.PORT). Additionally, the localization assertions assume mock mode prefixes [es]/[fr] without detecting whether live Lingo.dev translation is enabled.
Add require('dotenv').config(), load PORT from environment, and detect mock mode by checking both whether LINGO_API_KEY is missing OR set to the placeholder value:
🛠️ Suggested update
-const axios = require('axios');
+const axios = require('axios');
+require('dotenv').config();
+
+const port = process.env.PORT || 3000;
+const baseURL = `http://127.0.0.1:${port}/api/auth`;
+axios.defaults.timeout = 5000;
+const isMockMode = !process.env.LINGO_API_KEY || process.env.LINGO_API_KEY === 'your_api_key_here';
+
+function checkLocalization(msg, lang) {
+ if (!msg) return false;
+ if (isMockMode) return msg.includes(`[${lang}]`);
+ console.log(`ℹ️ Live mode detected; skipping mock-prefix assertion for ${lang}.`);
+ return true;
+}
async function runTests() {
- const baseURL = 'http://127.0.0.1:3000/api/auth';
-
console.log('--- Starting Verification ---');
@@
- if (msg.includes('[es]')) {
+ if (checkLocalization(msg, 'es')) {
console.log('✅ PASS: Localized to Spanish');
} else {
console.log('❌ FAIL: Not localized correctly');
}
@@
- if (msg.includes('[fr]')) {
+ if (checkLocalization(msg, 'fr')) {
console.log('✅ PASS: Localized to French');
} else {
console.log('❌ FAIL: Not localized correctly');
}📝 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 axios = require('axios'); | |
| async function runTests() { | |
| const baseURL = 'http://127.0.0.1:3000/api/auth'; | |
| console.log('--- Starting Verification ---'); | |
| // Test 1: Signup Validation Error (Spanish) | |
| try { | |
| console.log('\n[Test 1] POST /signup?lang=es (Invalid Email)'); | |
| await axios.post(`${baseURL}/signup?lang=es`, { | |
| email: 'invalid', | |
| password: '123' | |
| }); | |
| console.log('❌ FAIL: Expected 400 error, got success'); | |
| } catch (err) { | |
| if (err.response) { | |
| console.log('Response Status:', err.response.status); | |
| console.log('Response Body:', err.response.data); | |
| const msg = err.response.data.message || ''; | |
| if (msg.includes('[es]')) { | |
| console.log('✅ PASS: Localized to Spanish'); | |
| } else { | |
| console.log('❌ FAIL: Not localized correctly'); | |
| } | |
| } else { | |
| console.error('❌ FAIL: Network/Server Error:', err.message); | |
| } | |
| } | |
| // Test 2: Signup Validation Error (French via Header) | |
| try { | |
| console.log('\n[Test 2] POST /signup (Header: fr) (Short Password)'); | |
| await axios.post(`${baseURL}/signup`, { | |
| email: 'valid@email.com', | |
| password: '123' | |
| }, { | |
| headers: { 'Accept-Language': 'fr' } | |
| }); | |
| console.log('❌ FAIL: Expected 400 error, got success'); | |
| } catch (err) { | |
| if (err.response) { | |
| console.log('Response Status:', err.response.status); | |
| console.log('Response Body:', err.response.data); | |
| const msg = err.response.data.message || ''; | |
| if (msg.includes('[fr]')) { | |
| console.log('✅ PASS: Localized to French'); | |
| } else { | |
| console.log('❌ FAIL: Not localized correctly'); | |
| } | |
| } else { | |
| console.error('❌ FAIL: Network/Server Error:', err.message); | |
| } | |
| } | |
| // Test 3: Success Case (English Default) | |
| try { | |
| console.log('\n[Test 3] POST /signup (Valid Data)'); | |
| const res = await axios.post(`${baseURL}/signup`, { | |
| email: `user${Date.now()}@example.com`, | |
| password: 'securepassword123' | |
| }); | |
| console.log('Response Status:', res.status); | |
| console.log('Response Body:', res.data); | |
| if (res.data.message && !res.data.message.includes('[')) { | |
| console.log('✅ PASS: English default (no mock prefix)'); | |
| } else { | |
| console.log('❌ FAIL: Unexpected content in success message'); | |
| } | |
| } catch (err) { | |
| console.error('❌ FAIL: Unexpected Error:', err.response ? err.response.data : err.message); | |
| } | |
| const axios = require('axios'); | |
| require('dotenv').config(); | |
| const port = process.env.PORT || 3000; | |
| const baseURL = `http://127.0.0.1:${port}/api/auth`; | |
| axios.defaults.timeout = 5000; | |
| const isMockMode = !process.env.LINGO_API_KEY || process.env.LINGO_API_KEY === 'your_api_key_here'; | |
| function checkLocalization(msg, lang) { | |
| if (!msg) return false; | |
| if (isMockMode) return msg.includes(`[${lang}]`); | |
| console.log(`ℹ️ Live mode detected; skipping mock-prefix assertion for ${lang}.`); | |
| return true; | |
| } | |
| async function runTests() { | |
| console.log('--- Starting Verification ---'); | |
| // Test 1: Signup Validation Error (Spanish) | |
| try { | |
| console.log('\n[Test 1] POST /signup?lang=es (Invalid Email)'); | |
| await axios.post(`${baseURL}/signup?lang=es`, { | |
| email: 'invalid', | |
| password: '123' | |
| }); | |
| console.log('❌ FAIL: Expected 400 error, got success'); | |
| } catch (err) { | |
| if (err.response) { | |
| console.log('Response Status:', err.response.status); | |
| console.log('Response Body:', err.response.data); | |
| const msg = err.response.data.message || ''; | |
| if (checkLocalization(msg, 'es')) { | |
| console.log('✅ PASS: Localized to Spanish'); | |
| } else { | |
| console.log('❌ FAIL: Not localized correctly'); | |
| } | |
| } else { | |
| console.error('❌ FAIL: Network/Server Error:', err.message); | |
| } | |
| } | |
| // Test 2: Signup Validation Error (French via Header) | |
| try { | |
| console.log('\n[Test 2] POST /signup (Header: fr) (Short Password)'); | |
| await axios.post(`${baseURL}/signup`, { | |
| email: 'valid@email.com', | |
| password: '123' | |
| }, { | |
| headers: { 'Accept-Language': 'fr' } | |
| }); | |
| console.log('❌ FAIL: Expected 400 error, got success'); | |
| } catch (err) { | |
| if (err.response) { | |
| console.log('Response Status:', err.response.status); | |
| console.log('Response Body:', err.response.data); | |
| const msg = err.response.data.message || ''; | |
| if (checkLocalization(msg, 'fr')) { | |
| console.log('✅ PASS: Localized to French'); | |
| } else { | |
| console.log('❌ FAIL: Not localized correctly'); | |
| } | |
| } else { | |
| console.error('❌ FAIL: Network/Server Error:', err.message); | |
| } | |
| } | |
| // Test 3: Success Case (English Default) | |
| try { | |
| console.log('\n[Test 3] POST /signup (Valid Data)'); | |
| const res = await axios.post(`${baseURL}/signup`, { | |
| email: `user${Date.now()}@example.com`, | |
| password: 'securepassword123' | |
| }); | |
| console.log('Response Status:', res.status); | |
| console.log('Response Body:', res.data); | |
| if (res.data.message && !res.data.message.includes('[')) { | |
| console.log('✅ PASS: English default (no mock prefix)'); | |
| } else { | |
| console.log('❌ FAIL: Unexpected content in success message'); | |
| } | |
| } catch (err) { | |
| console.error('❌ FAIL: Unexpected Error:', err.response ? err.response.data : err.message); | |
| } | |
| } |
🤖 Prompt for AI Agents
In `@community/error-localization-engine/verify.js` around lines 1 - 72, The
script currently hardcodes port 3000 and always expects mock-prefixed
localization; update runTests to require('dotenv').config(), build baseURL using
const port = process.env.PORT || 3000 and const baseURL =
`http://127.0.0.1:${port}/api/auth`, and detect mock mode via a boolean like
const isMock = !process.env.LINGO_API_KEY || process.env.LINGO_API_KEY ===
'your_lingo_api_key_here'; then adjust the localization assertions (in the POST
/signup error checks and success check) to only expect the mock prefix
'[es]'/'[fr]' when isMock is true, otherwise validate that translated messages
exist without assuming the mock prefix.
Error Message & Validation Localization Engine
This PR adds a community demo under
/communitythat showcases how Lingo.dev can be used to dynamically localize backend error and validation messages.This submission is part of the community demo initiative.
Closes #1761
🎯 Overview
A production-grade demo showcasing automatic backend error localization. This addresses a common pain point: most applications translate UI labels but leave backend errors in English.
🚀 What This Demo Does
This project demonstrates dynamic backend error localization - intercepting validation errors before they reach the client and translating them to the user's preferred language in real-time.
Key Features:
?lang=xxquery parameters andAccept-Languageheaders🛠️ Tech Stack
lingo.dev/sdk)📂 Project Structure
community/error-localization-engine
├── server.js # Express server with CORS
├── lingoClient.js # Lingo.dev SDK wrapper
├── middleware/localizer.js # Language detection middleware
├── routes/auth.js # Auth endpoints with validation
└── public/ # Frontend UI
├── index.html
├── styles.css
└── app.js
🧪 How to Test
npm install.envnpm starthttp://localhost:3000💡 Why This Matters
Most developers focus on UI localization but forget about backend errors. This demo shows a scalable pattern for:
Demo Screenshots :
Summary by CodeRabbit
Release Notes
New Features
Documentation
Chores
✏️ Tip: You can customize this high-level summary in your review settings.