Fix/channels i18n hardcoded text#2509
Conversation
Channel configuration UI (Discord, Telegram, Web, MCP) was displaying untranslated English strings because app/src/lib/channels/definitions.ts hard-codes all labels, descriptions, placeholders, and auth mode names. Changes: - I18nContext.tsx: t() now accepts an optional fallback string so components can gracefully degrade to the original English when a key is missing. - ChannelStatusBadge.tsx: status labels (Connected/Connecting/Disconnected/Error) now use channels.status.* keys already present in zh-CN. - ChannelConfigPanel.tsx: display_name, description, and MCP title/description now go through t(). - DiscordConfig.tsx & TelegramConfig.tsx: auth mode labels, mode descriptions, field labels, field placeholders, and the "is required" validation message now use t() with fallback. - en.ts & zh-CN chunks: add 39 new keys covering all Discord/Telegram/Web/MCP channel config strings plus shared auth mode labels and field validation.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughChannel UI components now call the i18n ChangesChannel UI Internationalization
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
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. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Checking status of this PR |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/lib/i18n/en.ts`:
- Around line 1356-1359: The i18n keys use camelCase
('channels.authMode.managedDm', 'channels.authMode.botToken',
'channels.authMode.apiKey') but runtime resolves channels.authMode.${spec.mode}
with snake_case modes (managed_dm, bot_token, api_key), so rename those keys to
'channels.authMode.managed_dm', 'channels.authMode.bot_token', and
'channels.authMode.api_key' (leave 'channels.authMode.oauth' as-is) in this file
and make the identical key-shape changes in the corresponding zh-CN chunk file
so the localized labels are resolved at runtime.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: af3c6d31-dec5-4300-a2ad-0b489b2c63fb
📒 Files selected for processing (8)
app/src/components/channels/ChannelConfigPanel.tsxapp/src/components/channels/ChannelStatusBadge.tsxapp/src/components/channels/DiscordConfig.tsxapp/src/components/channels/TelegramConfig.tsxapp/src/lib/i18n/I18nContext.tsxapp/src/lib/i18n/chunks/zh-CN-1.tsapp/src/lib/i18n/chunks/zh-CN-3.tsapp/src/lib/i18n/en.ts
…x component t() calls - Fix en.ts ↔ en-N.ts chunk parity by adding 27 channels.* keys to en-1.ts/en-3.ts - Rename camelCase keys to snake_case (managedDm→managed_dm, botToken→bot_token, apiKey→api_key) to match runtime template literals - Add the 27 keys to all 11 locale chunks (zh-CN, de, hi, es, ar, fr, bn, pt, ru, id, it) - Fix ChannelConfigPanel.tsx and ChannelStatusBadge.tsx to use single-arg t() - Fix DiscordConfig.tsx/TelegramConfig.tsx template literals and remove unused AUTH_MODE_LABELS import - Keep I18nContext.tsx at t(key: string) to avoid type conflict with MemoryGraph.tsx
|
Coverage override request The 2 uncovered lines are pure string extractions ( |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
app/src/components/channels/DiscordConfig.tsx (1)
169-172: ⚡ Quick winPreserve pre-i18n fallback behavior in validation message.
On Line 169, pass explicit fallbacks so missing keys never surface as raw i18n keys and the old label behavior is retained.
Proposed patch
- lastError: t('channels.fieldRequired').replace( + lastError: t('channels.fieldRequired', '{field} is required').replace( '{field}', - t(`channels.discord.fields.${field.key}.label`) + t(`channels.discord.fields.${field.key}.label`, field.label) ),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/components/channels/DiscordConfig.tsx` around lines 169 - 172, The validation message currently inserts the inner i18n key directly which can surface raw keys if missing; update the lastError construction in DiscordConfig.tsx so the inner t call supplies an explicit fallback (e.g. t(`channels.discord.fields.${field.key}.label`, { defaultValue: field.label || field.key })) and use that value in t('channels.fieldRequired').replace('{field}', ...), ensuring missing translations fall back to the original label or key.app/src/components/channels/TelegramConfig.tsx (1)
196-199: ⚡ Quick winAdd explicit fallbacks for required-field i18n interpolation.
On Line 196, use fallback arguments to avoid raw key output when a translation key is absent and keep prior field-label behavior.
Proposed patch
- lastError: t('channels.fieldRequired').replace( + lastError: t('channels.fieldRequired', '{field} is required').replace( '{field}', - t(`channels.telegram.fields.${field.key}.label`) + t(`channels.telegram.fields.${field.key}.label`, field.label) ),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/components/channels/TelegramConfig.tsx` around lines 196 - 199, Replace the current string-replace approach for lastError with an i18n-safe interpolation that provides explicit fallbacks: call t('channels.fieldRequired', { field: t(`channels.telegram.fields.${field.key}.label`, { defaultValue: field.label || field.key }) }) (or, if your i18n t signature differs, compute a fallbackLabel = t(`channels.telegram.fields.${field.key}.label`, { defaultValue: '' }) || field.label || field.key and pass that into t('channels.fieldRequired', { field: fallbackLabel })); update the lastError assignment to use this pattern so missing translation keys don't render raw keys.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@app/src/components/channels/DiscordConfig.tsx`:
- Around line 169-172: The validation message currently inserts the inner i18n
key directly which can surface raw keys if missing; update the lastError
construction in DiscordConfig.tsx so the inner t call supplies an explicit
fallback (e.g. t(`channels.discord.fields.${field.key}.label`, { defaultValue:
field.label || field.key })) and use that value in
t('channels.fieldRequired').replace('{field}', ...), ensuring missing
translations fall back to the original label or key.
In `@app/src/components/channels/TelegramConfig.tsx`:
- Around line 196-199: Replace the current string-replace approach for lastError
with an i18n-safe interpolation that provides explicit fallbacks: call
t('channels.fieldRequired', { field:
t(`channels.telegram.fields.${field.key}.label`, { defaultValue: field.label ||
field.key }) }) (or, if your i18n t signature differs, compute a fallbackLabel =
t(`channels.telegram.fields.${field.key}.label`, { defaultValue: '' }) ||
field.label || field.key and pass that into t('channels.fieldRequired', { field:
fallbackLabel })); update the lastError assignment to use this pattern so
missing translation keys don't render raw keys.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e8729d9b-af88-4f00-b631-5a698b2a5add
📒 Files selected for processing (27)
.codegraph/.gitignoreapp/src/components/channels/DiscordConfig.tsxapp/src/components/channels/TelegramConfig.tsxapp/src/lib/i18n/chunks/ar-1.tsapp/src/lib/i18n/chunks/ar-3.tsapp/src/lib/i18n/chunks/bn-1.tsapp/src/lib/i18n/chunks/bn-3.tsapp/src/lib/i18n/chunks/de-1.tsapp/src/lib/i18n/chunks/de-3.tsapp/src/lib/i18n/chunks/en-1.tsapp/src/lib/i18n/chunks/en-3.tsapp/src/lib/i18n/chunks/es-1.tsapp/src/lib/i18n/chunks/es-3.tsapp/src/lib/i18n/chunks/fr-1.tsapp/src/lib/i18n/chunks/fr-3.tsapp/src/lib/i18n/chunks/hi-1.tsapp/src/lib/i18n/chunks/hi-3.tsapp/src/lib/i18n/chunks/id-1.tsapp/src/lib/i18n/chunks/id-3.tsapp/src/lib/i18n/chunks/it-1.tsapp/src/lib/i18n/chunks/it-3.tsapp/src/lib/i18n/chunks/pt-1.tsapp/src/lib/i18n/chunks/pt-3.tsapp/src/lib/i18n/chunks/ru-1.tsapp/src/lib/i18n/chunks/ru-3.tsapp/src/lib/i18n/chunks/zh-CN-3.tsapp/src/lib/i18n/en.ts
✅ Files skipped from review due to trivial changes (14)
- app/src/lib/i18n/chunks/de-1.ts
- .codegraph/.gitignore
- app/src/lib/i18n/chunks/pt-1.ts
- app/src/lib/i18n/en.ts
- app/src/lib/i18n/chunks/hi-1.ts
- app/src/lib/i18n/chunks/fr-3.ts
- app/src/lib/i18n/chunks/en-3.ts
- app/src/lib/i18n/chunks/it-3.ts
- app/src/lib/i18n/chunks/zh-CN-3.ts
- app/src/lib/i18n/chunks/bn-3.ts
- app/src/lib/i18n/chunks/ru-1.ts
- app/src/lib/i18n/chunks/id-3.ts
- app/src/lib/i18n/chunks/es-1.ts
- app/src/lib/i18n/chunks/ru-3.ts
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
app/src/components/channels/DiscordConfig.tsx (1)
317-323: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winProvide fallbacks for field label and placeholder translations.
The field translations don't provide fallback values, so missing translation keys will render as the key string itself (e.g.,
"channels.discord.fields.bot_token.label"). Per the PR summary, components should uset(key, originalEnglish)to preserve prior behavior when translations are missing.♻️ Add fallbacks to field translations
<ChannelFieldInput key={field.key} field={{ ...field, - label: t(`channels.discord.fields.${field.key}.label`), + label: t(`channels.discord.fields.${field.key}.label`, field.label), placeholder: field.placeholder - ? t(`channels.discord.fields.${field.key}.placeholder`) - : field.placeholder, + ? t(`channels.discord.fields.${field.key}.placeholder`, field.placeholder) + : undefined, }} value={fieldValues[compositeKey]?.[field.key] ?? ''} onChange={val => updateField(compositeKey, field.key, val)} disabled={busy} />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/components/channels/DiscordConfig.tsx` around lines 317 - 323, The current assignment for field.label and field.placeholder uses t(...) without fallbacks so missing translations render the key; update the spread in DiscordConfig (where field is rebuilt) to call t with the original English defaults: use t(`channels.discord.fields.${field.key}.label`, field.label) for the label and for the placeholder use t(`channels.discord.fields.${field.key}.placeholder`, field.placeholder || field.placeholder) (i.e., pass field.placeholder as the default) so the original values are preserved when translations are missing.app/src/components/channels/TelegramConfig.tsx (1)
377-383: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winProvide fallbacks for field label and placeholder translations.
The field translations don't provide fallback values, so missing translation keys will render as the key string itself (e.g.,
"channels.telegram.fields.bot_token.label"). Per the PR summary, components should uset(key, originalEnglish)to preserve prior behavior when translations are missing.♻️ Add fallbacks to field translations
<ChannelFieldInput key={field.key} field={{ ...field, - label: t(`channels.telegram.fields.${field.key}.label`), + label: t(`channels.telegram.fields.${field.key}.label`, field.label), placeholder: field.placeholder - ? t(`channels.telegram.fields.${field.key}.placeholder`) - : field.placeholder, + ? t(`channels.telegram.fields.${field.key}.placeholder`, field.placeholder) + : undefined, }} value={fieldValues[compositeKey]?.[field.key] ?? ''} onChange={val => updateField(compositeKey, field.key, val)} disabled={busyKeys[compositeKey]} />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/src/components/channels/TelegramConfig.tsx` around lines 377 - 383, The translation calls for field.label and field.placeholder should supply fallbacks so missing keys return the original text; update the spread in TelegramConfig (the field object being built) to call t(`channels.telegram.fields.${field.key}.label`, field.label) for the label and t(`channels.telegram.fields.${field.key}.placeholder`, field.placeholder) for the placeholder (keep the conditional that only translates when field.placeholder exists), ensuring the second argument passes the original English string from the existing field.label/field.placeholder values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/src/components/channels/DiscordConfig.tsx`:
- Around line 169-176: Replace the manual fallback logic inside the lastError
IIFE by calling the translation helper with its fallback parameter: use t(key,
field.label || field.key) (or t(key, field.label ?? field.key)) so you don't
compare label === key; update the replacement to
t('channels.fieldRequired').replace('{field}', t(key, field.label || field.key))
while keeping the surrounding lastError IIFE and field.key reference intact.
In `@app/src/components/channels/TelegramConfig.tsx`:
- Around line 196-203: Replace the manual fallback detection in the lastError
computation with the new t(key, fallback) API: build the key as `const key =
\`channels.telegram.fields.${field.key}.label\`` and call `t(key, field.label ||
field.key)` to get the label with a proper fallback, then use that result in the
`t('channels.fieldRequired').replace('{field}', ...)` call (update the inline
IIFE around lastError to use this t call instead of comparing label === key).
---
Outside diff comments:
In `@app/src/components/channels/DiscordConfig.tsx`:
- Around line 317-323: The current assignment for field.label and
field.placeholder uses t(...) without fallbacks so missing translations render
the key; update the spread in DiscordConfig (where field is rebuilt) to call t
with the original English defaults: use
t(`channels.discord.fields.${field.key}.label`, field.label) for the label and
for the placeholder use t(`channels.discord.fields.${field.key}.placeholder`,
field.placeholder || field.placeholder) (i.e., pass field.placeholder as the
default) so the original values are preserved when translations are missing.
In `@app/src/components/channels/TelegramConfig.tsx`:
- Around line 377-383: The translation calls for field.label and
field.placeholder should supply fallbacks so missing keys return the original
text; update the spread in TelegramConfig (the field object being built) to call
t(`channels.telegram.fields.${field.key}.label`, field.label) for the label and
t(`channels.telegram.fields.${field.key}.placeholder`, field.placeholder) for
the placeholder (keep the conditional that only translates when
field.placeholder exists), ensuring the second argument passes the original
English string from the existing field.label/field.placeholder values.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 080d0d9e-c293-4138-88ba-a95c67b71f1d
📒 Files selected for processing (2)
app/src/components/channels/DiscordConfig.tsxapp/src/components/channels/TelegramConfig.tsx
# Conflicts: # app/src/lib/i18n/chunks/id-1.ts
Addresses CodeRabbit on app/src/components/channels/DiscordConfig.tsx:176 and app/src/components/channels/TelegramConfig.tsx:203. - t() now accepts an optional fallback string used when neither the active locale nor English contains the key. This is sturdier than the prior `label === key` heuristic, which would mis-fire whenever a translation legitimately equalled its key. - DiscordConfig and TelegramConfig now use t(key, field.label || field.key) for the missing-field error label instead of the brittle IIFE. - MemoryGraph.tooltipFor: align signature with the real t. The unused params object was previously dropped at runtime (t had no interpolation), so this is behavior-preserving.
…field i18n
Addresses CodeRabbit review:
- Use the new t(key, fallback) signature for field label/placeholder so
missing translations render the original English instead of raw keys.
- Add explicit '{field} is required' fallback to channels.fieldRequired.
|
@senamakel Thanks for the maintainer boost! 😂 I was still staring at CodeRabbit's review comments trying to figure out the i18n fallback, refreshed the page and boom — you already pushed the fix plus rebased everything. Speedrun strats. |
speed run ftw :D thanks for your contributino.. LFG! |
Summary
t()i18n translator so Discord, Telegram, Web, and MCP panels render in the user's locale.en.tsand thezh-CNchunks covering auth mode labels, field labels/placeholders, mode descriptions, MCP copy, status badges, and validation messages.I18nContext.t()signature to accept an optionalfallbackstring so components can degrade gracefully to the original English text when a key is missing.Problem
app/src/lib/channels/definitions.tshard-codes alldisplay_name,description,AUTH_MODE_LABELS, and field metadata in English. React components consumed these values directly, bypassing the i18n system entirely. Chinese (and other non-English) users saw untranslated English strings throughout the channel configuration UI.Solution
I18nContext.tsx—t(key, fallback?)now accepts an optional fallback. Lookup chain:currentLocale[key] → en[key] → fallback → key. This lets us migrate hardcoded→i18n incrementally without breaking existing behavior.ChannelStatusBadge.tsx— ReplacedSTATUS_STYLES[status].labelwitht('channels.status.' + status)(keys already existed in zh-CN).ChannelConfigPanel.tsx—definition.display_name/definition.descriptionnow route throught('channels.{id}.title')andt('channels.{id}.desc'); MCP section uses newchannels.mcp.*keys.DiscordConfig.tsx&TelegramConfig.tsx— Auth mode labels, mode descriptions, field labels, placeholders, and the "is required" validation message all uset(key, originalEnglish)so missing translations fall back to the prior hardcoded text.en.ts& zh-CN chunks — Added 39 keys:channels.status.*,channels.authMode.*,channels.fieldRequired,channels.mcp.*,channels.capabilitiesSubmission Checklist
npx vitest run --config test/vitest.config.ts src/components/channels/__tests__→ 6 files, 33 tests, all passed.Closes #NNNImpact
Related
channels.*keys incrementally.AI Authored PR Metadata
Linear Issue
Commit & Branch
fix/channels-i18n-hardcoded-textf1f7f161Validation Run
pnpm --filter openhuman-app format:checkpnpm typechecknpx vitest run --config test/vitest.config.ts src/components/channels/__tests__→ 6 passed, 33 testsValidation Blocked
command:pnpm --filter openhuman-app format:checkerror:pnpmpackage manager not found in environmentimpact:CI will run format check; local run blocked onlyBehavior Changes
t()with English fallback.Parity Contract
t(key, fallback)call uses the original hardcoded English as fallback.Duplicate / Superseded PR Handling
Summary by CodeRabbit