Fix hardcoded user-facing strings (email fallbacks, AI parse errors, API error messages) #53

Closed
opened 2026-06-08 19:28:45 +00:00 by claude · 0 comments
Member

Context

A review found three categories of hardcoded English strings shown to users (including Romanian users).


Fix 1 — API error messages (frontend-only)

Why frontend: Language is already known on the frontend. The error code field is already set on all ErrorResponse objects (cv_file_missing, captcha_verification_failed, request_cancelled). The t() function returns the key string when a key is not found, which lets us detect a missing translation safely.

1a — web/wwwroot/js/utils/form-helpers.js

Update extractApiError to check body.code before falling back to body.error:

javascript function extractApiError(body, status, fallbackKey, rateLimitKey) { if (status === 429) return window.MyAi.t(rateLimitKey || 'form.rateLimited'); if (status >= 400 && status < 500) { if (body && body.code) { var codeKey = 'error.' + body.code; var translated = window.MyAi.t(codeKey); if (translated !== codeKey) return translated; } var msg = body && (body.error || body.Error || body.title); if (msg) return msg; } return window.MyAi.t(fallbackKey); }

1b — web/wwwroot/js/i18n.js

Add to both en and ro dictionaries:

Key en ro
error.cv_file_missing Missing CV PDF. Fișierul CV PDF lipsește.
error.captcha_verification_failed Captcha verification failed. Verificarea captcha a eșuat.
error.request_cancelled Request was cancelled. Cererea a fost anulată.

Fix 2 — Email fallback values

2a — New email-data migration

Command:
powershell dotnet ef migrations add AddFallbackStringTemplates --context EmailDbContext --project Apis/email-data --startup-project Apis/email-api

Seed in emailApi.Templates:

Key en ro
email.match.fallback-na N/A N/A
email.match.subject-fallback-label Job Job
email.notification.unknown-ip Unknown Necunoscut
email.search-results.keywords-empty none detected niciunul detectat
email.search-results.providers-empty none niciunul
email.search-results.location-empty - -

2b — Apis/api/Services/EmailApiEmailSender.cs

Line Before After
218 jobLabel ?? "N/A" jobLabel ?? _emailTemplates.Get("email.match.fallback-na", language)
219 result.JobUrl ?? "N/A" result.JobUrl ?? _emailTemplates.Get("email.match.fallback-na", language)
241 jobLabel ?? "Job" jobLabel ?? _emailTemplates.Get("email.match.subject-fallback-label", language)
141 userIp ?? "Unknown" userIp ?? _emailTemplates.Get("email.notification.unknown-ip", "en")

2c — Jobs/cv-search-job/Services/CvSearchEmailSender.cs

In BuildScanSummary (already has language param and _emailTemplates):

`csharp
var keywordsHtml = keywords.Count > 0
? string.Join(...)
: $"<span style="...">{_emailTemplates.Get("email.search-results.keywords-empty", language)}";

var providers = providerNames.Count > 0
? string.Join(", ", providerNames)
: _emailTemplates.Get("email.search-results.providers-empty", language);

var locationDisplay = string.IsNullOrWhiteSpace(location)
? _emailTemplates.Get("email.search-results.location-empty", language)
: location;
`


Fix 3 — AI parse failure messages

3a — New cv-matcher-data migration

Command:
powershell dotnet ef migrations add AddParseErrorPrompts --context CvMatcherDbContext --project Apis/cv-matcher-data --startup-project Apis/cv-matcher-api

Seed in cvMatcher.AiPrompts:

Key Language Value
parse-error.summary en The AI response could not be parsed. Please try again.
parse-error.summary ro Răspunsul AI nu a putut fi interpretat. Vă rugăm să încercați din nou.
parse-error.recommendation en If the problem persists, try a different job link or description.
parse-error.recommendation ro Dacă problema persistă, încercați un alt link sau descriere de job.

3b — Apis/cv-matcher-api/Services/CvMatcherService.cs

In MatchJobAsync, look up messages before calling ParseResult:

csharp var errorSummary = await _aiPrompts.GetAsync("parse-error.summary", normalizedLanguage, ct); var errorRec = await _aiPrompts.GetAsync("parse-error.recommendation", normalizedLanguage, ct); var result = ParseResult(json, errorSummary, errorRec);

Change ParseResult signature:

csharp private static JobMatchResponse ParseResult( string json, string? errorSummary = null, string? errorRec = null) { try { /* existing deserialize */ } catch { } return new JobMatchResponse { Score = 0, Summary = errorSummary ?? "The AI response could not be parsed as structured JSON.", Recommendations = [errorRec ?? "Inspect the raw model output and tune the scoring prompt."] }; }


Verification

  • API errors (ro): Set browser to Romanian, try uploading without a file → confirm "Fișierul CV PDF lipsește." is shown
  • Match email fallbacks: Trigger a match with no job URL in ro → confirm email uses Romanian labels
  • Job search results email: Trigger session with empty keywords/no location → confirm Romanian fallback text in scan summary
  • AI parse error: Temporarily corrupt a prompt row in the DB → confirm Romanian parse-error message in match email
## Context A review found three categories of hardcoded English strings shown to users (including Romanian users). --- ## Fix 1 — API error messages (frontend-only) **Why frontend**: Language is already known on the frontend. The error `code` field is already set on all `ErrorResponse` objects (`cv_file_missing`, `captcha_verification_failed`, `request_cancelled`). The `t()` function returns the key string when a key is not found, which lets us detect a missing translation safely. ### 1a — `web/wwwroot/js/utils/form-helpers.js` Update `extractApiError` to check `body.code` before falling back to `body.error`: `javascript function extractApiError(body, status, fallbackKey, rateLimitKey) { if (status === 429) return window.MyAi.t(rateLimitKey || 'form.rateLimited'); if (status >= 400 && status < 500) { if (body && body.code) { var codeKey = 'error.' + body.code; var translated = window.MyAi.t(codeKey); if (translated !== codeKey) return translated; } var msg = body && (body.error || body.Error || body.title); if (msg) return msg; } return window.MyAi.t(fallbackKey); } ` ### 1b — `web/wwwroot/js/i18n.js` Add to both `en` and `ro` dictionaries: | Key | en | ro | |-----|----|----| | `error.cv_file_missing` | `Missing CV PDF.` | `Fișierul CV PDF lipsește.` | | `error.captcha_verification_failed` | `Captcha verification failed.` | `Verificarea captcha a eșuat.` | | `error.request_cancelled` | `Request was cancelled.` | `Cererea a fost anulată.` | --- ## Fix 2 — Email fallback values ### 2a — New `email-data` migration Command: `powershell dotnet ef migrations add AddFallbackStringTemplates --context EmailDbContext --project Apis/email-data --startup-project Apis/email-api ` Seed in `emailApi.Templates`: | Key | en | ro | |-----|----|----| | `email.match.fallback-na` | `N/A` | `N/A` | | `email.match.subject-fallback-label` | `Job` | `Job` | | `email.notification.unknown-ip` | `Unknown` | `Necunoscut` | | `email.search-results.keywords-empty` | `none detected` | `niciunul detectat` | | `email.search-results.providers-empty` | `none` | `niciunul` | | `email.search-results.location-empty` | `-` | `-` | ### 2b — `Apis/api/Services/EmailApiEmailSender.cs` | Line | Before | After | |------|--------|-------| | 218 | `jobLabel ?? "N/A"` | `jobLabel ?? _emailTemplates.Get("email.match.fallback-na", language)` | | 219 | `result.JobUrl ?? "N/A"` | `result.JobUrl ?? _emailTemplates.Get("email.match.fallback-na", language)` | | 241 | `jobLabel ?? "Job"` | `jobLabel ?? _emailTemplates.Get("email.match.subject-fallback-label", language)` | | 141 | `userIp ?? "Unknown"` | `userIp ?? _emailTemplates.Get("email.notification.unknown-ip", "en")` | ### 2c — `Jobs/cv-search-job/Services/CvSearchEmailSender.cs` In `BuildScanSummary` (already has `language` param and `_emailTemplates`): `csharp var keywordsHtml = keywords.Count > 0 ? string.Join(...) : $"<span style=\"...\">{_emailTemplates.Get("email.search-results.keywords-empty", language)}</span>"; var providers = providerNames.Count > 0 ? string.Join(", ", providerNames) : _emailTemplates.Get("email.search-results.providers-empty", language); var locationDisplay = string.IsNullOrWhiteSpace(location) ? _emailTemplates.Get("email.search-results.location-empty", language) : location; ` --- ## Fix 3 — AI parse failure messages ### 3a — New `cv-matcher-data` migration Command: `powershell dotnet ef migrations add AddParseErrorPrompts --context CvMatcherDbContext --project Apis/cv-matcher-data --startup-project Apis/cv-matcher-api ` Seed in `cvMatcher.AiPrompts`: | Key | Language | Value | |-----|----------|-------| | `parse-error.summary` | `en` | `The AI response could not be parsed. Please try again.` | | `parse-error.summary` | `ro` | `Răspunsul AI nu a putut fi interpretat. Vă rugăm să încercați din nou.` | | `parse-error.recommendation` | `en` | `If the problem persists, try a different job link or description.` | | `parse-error.recommendation` | `ro` | `Dacă problema persistă, încercați un alt link sau descriere de job.` | ### 3b — `Apis/cv-matcher-api/Services/CvMatcherService.cs` In `MatchJobAsync`, look up messages before calling `ParseResult`: `csharp var errorSummary = await _aiPrompts.GetAsync("parse-error.summary", normalizedLanguage, ct); var errorRec = await _aiPrompts.GetAsync("parse-error.recommendation", normalizedLanguage, ct); var result = ParseResult(json, errorSummary, errorRec); ` Change `ParseResult` signature: `csharp private static JobMatchResponse ParseResult( string json, string? errorSummary = null, string? errorRec = null) { try { /* existing deserialize */ } catch { } return new JobMatchResponse { Score = 0, Summary = errorSummary ?? "The AI response could not be parsed as structured JSON.", Recommendations = [errorRec ?? "Inspect the raw model output and tune the scoring prompt."] }; } ` --- ## Verification - **API errors (ro)**: Set browser to Romanian, try uploading without a file → confirm "Fișierul CV PDF lipsește." is shown - **Match email fallbacks**: Trigger a match with no job URL in `ro` → confirm email uses Romanian labels - **Job search results email**: Trigger session with empty keywords/no location → confirm Romanian fallback text in scan summary - **AI parse error**: Temporarily corrupt a prompt row in the DB → confirm Romanian parse-error message in match email
gelu closed this issue 2026-06-08 19:34:31 +00:00
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: AI/myAi#53