Skip to content

feat(ui): localize interpolated backend error messages (RI-8268)#6142

Merged
valkirilov merged 2 commits into
fe/RI-8268/translate-error-codesfrom
fe/RI-8268/error-message-resources
Jul 2, 2026
Merged

feat(ui): localize interpolated backend error messages (RI-8268)#6142
valkirilov merged 2 commits into
fe/RI-8268/translate-error-codesfrom
fe/RI-8268/error-message-resources

Conversation

@valkirilov

@valkirilov valkirilov commented Jul 1, 2026

Copy link
Copy Markdown
Member

What

The backend now emits the runtime value baked into certain error messages as a structured resource (see the BE PR #6141). This adds the translated api.error.code.<n>.message templates that interpolate those values, so previously title-only codes get a fully localized body.

getTranslatedApiError already spreads response.data.resource into i18n.t(key, {...resource}), so no infra change — just data + one render tweak.

Locale messages (en + bg)

  • structured: 10904 / 10907 → {{url}}, 11706 → {{filename}}
  • freeform: 10900, 11009, 11100, RDI 11402/11403/11405/11406/11410 → {{detail}} on its own line

Titles unchanged. When the backend omits resource (its generic-default guard), the code falls back to title-only.

EN BG
Screenshot 2026-07-01 at 14 39 47 Screenshot 2026-07-01 at 14 40 00

How to test (local, no backend needed)

Add this throwaway helper to fire each resource-carrying error toast, then remove it before committing. Create redisinsight/ui/src/utils/devApiErrorTrigger.ts:

import { AxiosError } from 'axios'
import { store } from 'uiSrc/slices/store'
import { addErrorNotification } from 'uiSrc/slices/app/notifications'

const SAMPLES: Record<number, { resource: Record<string, unknown>; message: string }> = {
  // structured
  10904: { resource: { url: 'localhost:1111' }, message: 'Could not connect to localhost:1111, please check the connection details.' },
  10907: { resource: { url: 'localhost:6379' }, message: 'Could not connect to localhost:6379, please check the CA or Client certificate.' },
  11706: { resource: { filename: 'my-databases.json' }, message: 'Unable to parse my-databases.json' },
  // freeform { detail }
  10900: { resource: { detail: 'connect ECONNREFUSED 127.0.0.1:6379' }, message: 'connect ECONNREFUSED 127.0.0.1:6379' },
  11009: { resource: { detail: 'email is missing from the profile' }, message: 'email is missing from the profile' },
  11100: { resource: { detail: 'Task timed out after 60s' }, message: 'Task timed out after 60s' },
  11402: { resource: { detail: 'Invalid API key' }, message: 'Invalid API key' },
  11403: { resource: { detail: 'Internal pipeline crash' }, message: 'Internal pipeline crash' },
  11405: { resource: { id: 'a1b2-c3d4' }, message: 'RDI with id a1b2-c3d4 was not found' },
  11406: { resource: { detail: 'Read-only token' }, message: 'Read-only token' },
  11410: { resource: { detail: 'Malformed job spec' }, message: 'Malformed job spec' },
}

const makeError = (errorCode: number, resource: Record<string, unknown>, message: string): AxiosError =>
  ({ response: { status: 400, data: { errorCode, resource, message, name: `DevError${errorCode}` } } }) as AxiosError

export const installDevApiErrorTrigger = (): void => {
  const triggerApiError = (code: number, resource?: Record<string, unknown>) => {
    const sample = SAMPLES[code]
    if (!sample) return
    store.dispatch(addErrorNotification(makeError(code, resource ?? sample.resource, sample.message)))
  }
  // stagger — the toast id is `${Date.now()}`, so synchronous dispatches collide and get dropped
  const triggerAllApiErrors = () =>
    Object.keys(SAMPLES).forEach((code, i) => setTimeout(() => triggerApiError(Number(code)), i * 80))
  Object.assign(window, { triggerApiError, triggerAllApiErrors })
}

Then call it once from App.tsx:

import { installDevApiErrorTrigger } from 'uiSrc/utils/devApiErrorTrigger'
// inside the App component:
useEffect(() => {
  if (process.env.NODE_ENV === 'development') installDevApiErrorTrigger()
}, [])

Run yarn dev:ui, then in the browser console:

triggerApiError(11706)   // "Unable to parse my-databases.json"
triggerApiError(11403)   // "RDI encountered a server error." \n "Internal pipeline crash"
triggerAllApiErrors()    // one toast per code

Use ?lang=bg to verify the Bulgarian translations interpolate the same values.

🤖 Generated with Claude Code


Note

Low Risk
Locale and notification presentation changes only; no auth or API client logic changes beyond showing richer translated error copy.

Overview
Adds localized api.error.code.*.message strings in English and Bulgarian for API errors that now ship structured resource from the backend, so toasts show a full translated body instead of title-only where a template was missing.

Templates use {{url}}, {{filename}}, {{id}}, or a second line with {{detail}} (e.g. connection, auth profile, cloud unexpected, RDI, import parse). Existing getTranslatedApiError already passes resource into i18n; this PR is locale data plus display.

DefaultErrorContent now renders through a styled ErrorText with white-space: pre-line so \n in messages (especially {{detail}} on its own line) shows as a line break in error notifications.

Reviewed by Cursor Bugbot for commit 60b784a. Bugbot is set up for automated code reviews on this repo. Configure here.

@valkirilov valkirilov requested a review from a team as a code owner July 1, 2026 12:26
@jit-ci

jit-ci Bot commented Jul 1, 2026

Copy link
Copy Markdown

🛡️ Jit Security Scan Results

CRITICAL HIGH MEDIUM

✅ No security findings were detected in this PR


Security scan by Jit

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b505d9d. Configure here.

Comment thread redisinsight/ui/src/i18n/locales/en.json

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b505d9d087

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread redisinsight/ui/src/i18n/locales/en.json
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Code Coverage - Backend unit tests

St.
Category Percentage Covered / Total
🟢 Statements 93.06% 16372/17593
🟡 Branches 75.25% 5163/6861
🟢 Functions 87.36% 2516/2880
🟢 Lines 92.91% 15649/16843

Test suite run success

3621 tests passing in 319 suites.

Report generated by 🧪jest coverage report action from b505d9d

@valkirilov valkirilov force-pushed the fe/RI-8268/error-message-resources branch from b505d9d to 12af3e9 Compare July 1, 2026 12:36
@valkirilov valkirilov self-assigned this Jul 1, 2026
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Code Coverage - Integration Tests

Status Category Percentage Covered / Total
🟡 Statements 79.15% 18077/22838
🟡 Branches 61.94% 8384/13534
🟡 Functions 66.89% 2449/3661
🟡 Lines 78.76% 17015/21602

@valkirilov valkirilov force-pushed the fe/RI-8268/error-message-resources branch from 12af3e9 to e7a0c5a Compare July 1, 2026 13:41
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Code Coverage - Frontend unit tests

St.
Category Percentage Covered / Total
🟢 Statements 83.51% 26388/31598
🟡 Branches 69.13% 11206/16210
🟡 Functions 78.8% 7073/8976
🟢 Lines 83.98% 25722/30630

Test suite run success

7474 tests passing in 840 suites.

Report generated by 🧪jest coverage report action from f2651b9

@valkirilov valkirilov force-pushed the fe/RI-8268/translate-error-codes branch from 7dc70db to 5c6f079 Compare July 2, 2026 09:58
@valkirilov valkirilov force-pushed the fe/RI-8268/error-message-resources branch from e7a0c5a to f2651b9 Compare July 2, 2026 10:39
@valkirilov valkirilov force-pushed the fe/RI-8268/translate-error-codes branch from 5c6f079 to 8dc98e3 Compare July 2, 2026 11:43
@valkirilov valkirilov force-pushed the fe/RI-8268/error-message-resources branch from f2651b9 to db539ee Compare July 2, 2026 11:43
@valkirilov valkirilov force-pushed the fe/RI-8268/translate-error-codes branch from 8dc98e3 to b0a0d6b Compare July 2, 2026 12:10
valkirilov and others added 2 commits July 2, 2026 15:14
Add api.error.code.<n>.message templates (en + bg) for the codes whose
backend now emits a structured `resource`, interpolating the values:
{{url}} (10904/10907), {{filename}} (11706), {{id}} (11405), and {{detail}}
for the freeform framing codes (detail on its own line). Titles are
unchanged; getTranslatedApiError already spreads `resource` into the
interpolation, so no infra change is needed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Freeform localized error messages place the interpolated detail on its own
line ("…framing.\n{{detail}}"); apply white-space: pre-line so the break
renders instead of collapsing to a space.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@valkirilov valkirilov force-pushed the fe/RI-8268/error-message-resources branch from db539ee to 60b784a Compare July 2, 2026 12:15

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 60b784a209

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"api.error.code.11403.message": "RDI encountered a server error.\n{{detail}}",
"api.error.code.11404.title": "Validation error",
"api.error.code.11405.title": "Resource not found",
"api.error.code.11405.message": "RDI with id {{id}} was not found.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve backend detail for RDI 404s without ids

This template assumes every 11405 response includes resource.id, but the RDI upstream-error path still does not: wrapRdiPipelineError constructs RdiPipelineNotFoundException(message, errorOptions) for 404s, and the existing rdi-pipeline.error.handler.spec.ts expectation for that response contains no resource. Fresh evidence from that path means these RDI API 404s now take the localized-template branch in getTranslatedApiError/parseCustomError and render a missing-id message instead of the backend detail (for example “User was not found”); please fall back unless resource.id is present or template this case from detail/message.

Useful? React with 👍 / 👎.

@valkirilov valkirilov merged commit 37d4e63 into fe/RI-8268/translate-error-codes Jul 2, 2026
18 checks passed
@valkirilov valkirilov deleted the fe/RI-8268/error-message-resources branch July 2, 2026 12:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants