feat(i18n): add Portuguese Brazilian (pt-BR) localization#1957
feat(i18n): add Portuguese Brazilian (pt-BR) localization#1957gustavorodcruz96 wants to merge 4 commits intoAndyMik90:developfrom
Conversation
- Add complete pt-BR translation files for all 11 namespaces (common, dialogs, errors, gitlab, navigation, onboarding, settings, taskReview, tasks, terminal, welcome) - Register pt-BR locale in i18n config and resource loader - Migrate 12 components from hardcoded strings to t() calls (Roadmap, Terminal, Changelog, Worktrees, TaskDetail, etc.) - Add missing English translation keys for newly internationalized strings - Wire language parameter through roadmap AI generation pipeline so AI-generated content respects the user's locale Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThreads application language into agent sessions and roadmap generation, injects language instructions into prompt construction, extends types/configs with a language field, and adds pt-BR localization resources while localizing many renderer components. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant App as Application
participant IPC as IPC Handler
participant Agent as Agent Pipeline
participant Worker as AI Worker
participant Loader as Prompt Loader
participant LLM as Language Model
User->>App: select language (e.g., pt-BR)
App->>IPC: ROADMAP_GENERATE (calls getAppLanguage() → pt-BR)
IPC->>Agent: startRoadmap { model, thinkingLevel, language: 'pt-BR' }
Agent->>Agent: runDiscoveryPhase(..., language='pt-BR')
Agent->>Worker: assemblePrompt(session { language: 'pt-BR', ... })
Worker->>Loader: injectContext(context { language: 'pt-BR', ... })
alt language != 'en'
Loader->>Loader: prepend "OUTPUT LANGUAGE" instruction (pt-BR)
end
Loader->>LLM: send prompt
LLM-->>Loader: localized response
Loader-->>Agent: stream/enriched content
Agent-->>App: roadmap results (localized content)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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 Tip CodeRabbit can use your project's `biome` configuration to improve the quality of JS/TS/CSS/JSON code reviews.Add a configuration file to your project to customize how CodeRabbit runs |
There was a problem hiding this comment.
🎉 Thanks for your first PR!
A maintainer will review it soon. Please make sure:
- Your branch is synced with
develop - CI checks pass
- You've followed our contribution guide
Welcome to the Auto Claude community!
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces full support for Brazilian Portuguese (pt-BR) localization, significantly expanding the application's international reach. It involves adding a complete set of translation files for pt-BR and updating various UI components to dynamically fetch text based on the active language. A key enhancement is the integration of language awareness into the AI roadmap generation feature, allowing AI-generated content to be localized according to the user's preference. This change improves user experience for Portuguese-speaking users and lays the groundwork for further internationalization efforts. Highlights
Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces comprehensive Brazilian Portuguese (pt-BR) localization across the application. The changes are well-executed, including the addition of translation files, migrating numerous components from hardcoded strings to i18n keys, and making the AI roadmap generation language-aware. The overall implementation is solid. I've included a couple of suggestions to improve code readability and maintainability.
| sections.push( | ||
| `## OUTPUT LANGUAGE\n\n` + | ||
| `You MUST write ALL human-readable text content in **${context.language}**. ` + | ||
| `This includes: titles, descriptions, rationale, acceptance criteria, user stories, ` + | ||
| `phase names, status messages, and any text shown to the user. ` + | ||
| `Keep JSON keys, code, file paths, technical identifiers, and enum values in English.\n\n` + | ||
| `---\n\n` | ||
| ); |
There was a problem hiding this comment.
For better readability and maintainability, you could use a single multi-line template literal here instead of concatenating multiple strings with +.
sections.push(`## OUTPUT LANGUAGE
You MUST write ALL human-readable text content in **${context.language}**. This includes: titles, descriptions, rationale, acceptance criteria, user stories, phase names, status messages, and any text shown to the user. Keep JSON keys, code, file paths, technical identifiers, and enum values in English.
---
`);| {task.reviewReason === 'completed' ? t('tasks:reviewReason.completed') : | ||
| task.reviewReason === 'errors' ? t('tasks:reviewReason.hasErrors') : | ||
| task.reviewReason === 'plan_review' ? t('tasks:reviewReason.approvePlan') : | ||
| task.reviewReason === 'stopped' ? t('tasks:reviewReason.stopped') : t('tasks:reviewReason.qaIssues')} | ||
| </Badge> |
There was a problem hiding this comment.
This long ternary chain for translating reviewReason is a bit hard to read and maintain. This pattern is also repeated in TaskHeader.tsx.
Consider refactoring this to use a mapping object or a dynamic key, which would make the code cleaner and more scalable if new reasons are added. For example:
const getReviewReasonTranslation = (reason: string | undefined): string => {
const keyMap: Record<string, string> = {
completed: 'completed',
errors: 'hasErrors',
plan_review: 'approvePlan',
stopped: 'stopped',
};
const reasonKey = keyMap[reason || ''] || 'qaIssues'; // Default to 'qaIssues'
return t(`tasks:reviewReason.${reasonKey}`);
};
// Then in your component:
{getReviewReasonTranslation(task.reviewReason)}There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (16)
apps/desktop/src/renderer/components/task-detail/TaskDetailModal.tsx (2)
417-419:⚠️ Potential issue | 🟠 MajorSome UI labels are still hardcoded and bypass i18n.
Overview,Subtasks,Logs, thesubtaskssuffix, and sr-onlyCloseare still plain English strings.🌐 Suggested patch
- <span className="text-xs text-muted-foreground ml-1"> - {completedSubtasks}/{totalSubtasks} subtasks - </span> + <span className="text-xs text-muted-foreground ml-1"> + {t('tasks:labels.subtasksProgress', { completed: completedSubtasks, total: totalSubtasks })} + </span> @@ - <span className="sr-only">Close</span> + <span className="sr-only">{t('common:buttons.close')}</span> @@ - Overview + {t('tasks:tabs.overview')} @@ - Subtasks ({task.subtasks.length}) + {t('tasks:tabs.subtasks', { count: task.subtasks.length })} @@ - Logs + {t('tasks:tabs.logs')}Based on learnings, "All frontend user-facing text must use
react-i18nexttranslation keys. Hardcoded strings in JSX/TSX break localization for non-English users. Add keys to bothen/*.jsonandfr/*.jsontranslation files."Also applies to: 448-448, 485-497
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/task-detail/TaskDetailModal.tsx` around lines 417 - 419, Replace hardcoded UI strings in TaskDetailModal (the JSX rendering "Overview", "Subtasks", "Logs", the subtasks suffix text shown near {completedSubtasks}/{totalSubtasks}, and the sr-only "Close") with react-i18next translation keys using the useTranslation hook (t('...')). Update the corresponding translation files by adding those keys to both en/*.json and fr/*.json so the labels are localized; ensure keys are descriptive (e.g., taskDetail.overview, taskDetail.subtasks, taskDetail.logs, taskDetail.subtasksSuffix, taskDetail.close) and used consistently in the component.
99-103:⚠️ Potential issue | 🟠 MajorLocalize remaining error/toast fallback messages in handlers.
User-visible messages are still hardcoded in English (resume failure and several fallback errors). These should use
t(...)keys to avoid mixed-language UX.🌐 Suggested patch
if (state.isIncomplete) { const isValid = await state.reloadPlanForIncompleteTask(); if (!isValid) { toast({ - title: 'Cannot Resume Task', - description: 'Failed to load implementation plan. Please try again or check the task files.', + title: t('tasks:notifications.cannotResumeTitle'), + description: t('tasks:notifications.cannotResumeDescription'), variant: 'destructive', duration: 5000, }); return; } } @@ - state.setDeleteError(result.error || 'Failed to delete task'); + state.setDeleteError(result.error || t('tasks:errors.deleteFailed')); @@ - state.setWorkspaceError(result.data?.message || result.error || 'Failed to merge changes'); + state.setWorkspaceError(result.data?.message || result.error || t('tasks:errors.mergeFailed')); @@ - state.setWorkspaceError(error instanceof Error ? error.message : 'Unknown error during merge'); + state.setWorkspaceError(error instanceof Error ? error.message : t('tasks:errors.mergeUnknown')); @@ - state.setWorkspaceError(result.data?.message || result.error || 'Failed to discard changes'); + state.setWorkspaceError(result.data?.message || result.error || t('tasks:errors.discardFailed'));As per coding guidelines, "All frontend user-facing text must use
react-i18nexttranslation keys. Hardcoded strings in JSX/TSX break localization for non-English users. Add keys to bothen/*.jsonandfr/*.jsontranslation files."Also applies to: 151-152, 171-175, 188-188
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/task-detail/TaskDetailModal.tsx` around lines 99 - 103, Replace the hardcoded English toast/error strings in TaskDetailModal handlers (calls to toast and any fallback error messages inside the resume/handler functions) with react-i18next translation lookups using the t(...) function (e.g., t('taskDetail.resumeError.title'), t('taskDetail.resumeError.description')). Update the corresponding translation keys in both en/*.json and fr/*.json (add keys such as taskDetail.resumeError.title, taskDetail.resumeError.description and any others used for the fallback messages referenced around the toast calls and other handlers) so all user-facing strings are localized and no plain English literals remain in TaskDetailModal.apps/desktop/src/main/ai/runners/roadmap.ts (1)
343-350:⚠️ Potential issue | 🔴 CriticalConcurrent roadmap writes can clobber each other.
This block uses a PID-only temp filename and no shared lock. Two concurrent writes in the same process can collide on the same
.tmppath, and this also bypasses the lock-based writer used by other roadmap update paths.🔒 Suggested hardening (unique temp file + lock alignment)
+import { randomUUID } from 'node:crypto'; ... - const tmpFile = `${roadmapFile}.tmp.${process.pid}`; + const tmpFile = `${roadmapFile}.tmp.${process.pid}.${randomUUID()}`; writeFileSync(tmpFile, merged, 'utf-8'); renameSync(tmpFile, roadmapFile);Please also align this write with the same file-lock mechanism used in
apps/desktop/src/main/utils/roadmap-utils.tsto avoid cross-path races.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/ai/runners/roadmap.ts` around lines 343 - 350, The atomic write in the preserved-features merge block can collide because tmp filename uses only process.pid and skips the shared file-lock; update the block around mergeFeatures/data to (1) obtain the same roadmap file lock used in apps/desktop/src/main/utils/roadmap-utils.ts (e.g., call the lock helper such as withRoadmapLock/ acquireRoadmapLock or the equivalent exported function) before writing, and (2) create a truly unique temp file name for roadmapFile (include a random suffix or use a secure UUID/crypto random bytes or mkstemp-style temp name instead of `${process.pid}`) then write and rename while holding the lock; ensure you reference mergeFeatures and roadmapFile in the change so the operation remains atomic and consistent with other update paths.apps/desktop/src/renderer/components/SortableFeatureCard.tsx (1)
105-106:⚠️ Potential issue | 🟠 MajorSeveral user-facing strings are still hardcoded in English.
This component is only partially localized; these tooltip/source strings should also move to translation keys.
As per coding guidelines: “All frontend user-facing text must use `react-i18next` translation keys. Hardcoded strings in JSX/TSX break localization for non-English users. Add keys to both `en/*.json` and `fr/*.json` translation files.”🌐 Suggested localization pass
- <TooltipContent> - Phase: {phaseName} - </TooltipContent> + <TooltipContent> + {t('roadmap.phaseLabel', { phase: phaseName })} + </TooltipContent> ... - <TooltipContent> - This feature addresses competitor pain points - </TooltipContent> + <TooltipContent> + {t('roadmap.competitorInsightTooltip')} + </TooltipContent> ... - <TooltipContent> - {feature.votes} votes from user feedback - </TooltipContent> + <TooltipContent> + {t('roadmap.votesFromFeedback', { count: feature.votes })} + </TooltipContent> ... - {feature.source?.provider === 'canny' ? 'Canny' : 'External'} + {feature.source?.provider === 'canny' ? 'Canny' : t('roadmap.externalSource')} ... - <TooltipContent> - Imported from {feature.source?.provider} - </TooltipContent> + <TooltipContent> + {t('roadmap.importedFrom', { provider: feature.source?.provider })} + </TooltipContent>Also applies to: 120-121, 215-216, 227-227, 231-232
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/SortableFeatureCard.tsx` around lines 105 - 106, The SortableFeatureCard component contains hardcoded user-facing strings (e.g., "Phase: {phaseName}" and other tooltip/source strings rendered inside TooltipContent) that must be replaced with react-i18next translation keys; update the JSX in SortableFeatureCard to use the t('...') hook for each string (injecting variables like phaseName via interpolation or t('key', { phaseName })), add corresponding entries to both en/*.json and fr/*.json translation files for each new key, and ensure you import/use the useTranslation hook at the top of the component so all tooltips and labels (including the other strings referenced in the comment) are fully localized.apps/desktop/src/renderer/components/task-detail/TaskHeader.tsx (1)
71-73:⚠️ Potential issue | 🟡 MinorHardcoded "subtasks" string breaks localization.
The string "subtasks" on line 72 is hardcoded while surrounding labels use
t(). This will display English text for non-English users.🌐 Proposed fix
<Badge variant="outline" className="text-xs text-orange-400"> - {taskProgress.completed}/{taskProgress.total} subtasks + {t('tasks:labels.subtaskProgress', { completed: taskProgress.completed, total: taskProgress.total })} </Badge>Add the corresponding key to the translation files:
// en/tasks.json "labels": { "subtaskProgress": "{{completed}}/{{total}} subtasks" }As per coding guidelines: "All frontend user-facing text must use
react-i18nexttranslation keys."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/task-detail/TaskHeader.tsx` around lines 71 - 73, Replace the hardcoded "subtasks" label in TaskHeader.tsx with a react-i18next translation call: use the t() helper to render the progress text (e.g., t('labels.subtaskProgress', { completed: taskProgress.completed, total: taskProgress.total })) inside the Badge where {taskProgress.completed}/{taskProgress.total} subtasks currently appears, and add the matching key ("labels.subtaskProgress": "{{completed}}/{{total}} subtasks") to the translations (e.g., en/tasks.json and other locale files) so all locales get the localized string.apps/desktop/src/main/agent/agent-manager.ts (1)
615-639:⚠️ Potential issue | 🟠 MajorMissing
languagefield in QA process session config.The
startQAProcessmethod builds asessionConfigwithout thelanguagefield, whilestartSpecCreation(line 399) andstartTaskExecution(line 524) both includelanguage: getAppLanguage(). This inconsistency means QA-generated content won't respect the user's language preference.🔧 Proposed fix
const sessionConfig: SerializableSessionConfig = { agentType: 'qa_reviewer', systemPrompt, initialMessages: qaInitialMessages, maxSteps: 1000, specDir: effectiveSpecDir, projectDir: effectiveProjectDir, provider: resolved.provider, modelId: resolved.modelId, apiKey: resolved.auth?.apiKey, baseURL: resolved.auth?.baseURL, configDir: resolved.configDir, oauthTokenFilePath: resolved.auth?.oauthTokenFilePath, + language: getAppLanguage(), mcpOptions: {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/main/agent/agent-manager.ts` around lines 615 - 639, The QA sessionConfig built in startQAProcess is missing the language property so QA output won't respect user preferences; update the SerializableSessionConfig object (the sessionConfig inside startQAProcess) to include language: getAppLanguage() just like startSpecCreation and startTaskExecution do, ensuring the QA reviewer session uses the app language.apps/desktop/src/renderer/components/roadmap/FeatureCard.tsx (1)
46-55:⚠️ Potential issue | 🟡 MinorHardcoded "Competitor Insight" strings break localization.
The "Competitor Insight" badge text (line 51) and tooltip content (line 54) are hardcoded while other labels in this component use
t().🌐 Proposed fix
{hasCompetitorInsight && ( <Tooltip> <TooltipTrigger asChild> <Badge variant="outline" className="text-xs text-primary border-primary/50"> <TrendingUp className="h-3 w-3 mr-1" /> - Competitor Insight + {t('roadmap.competitorInsight.badge')} </Badge> </TooltipTrigger> - <TooltipContent>This feature addresses competitor pain points</TooltipContent> + <TooltipContent>{t('roadmap.competitorInsight.tooltip')}</TooltipContent> </Tooltip> )}As per coding guidelines: "All frontend user-facing text must use
react-i18nexttranslation keys."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/roadmap/FeatureCard.tsx` around lines 46 - 55, The "Competitor Insight" badge and tooltip are hardcoded; replace both occurrences with translation calls using the existing react-i18next t() function (e.g., t('roadmap.competitorInsight.badge') for the Badge text and t('roadmap.competitorInsight.tooltip') for TooltipContent) inside the FeatureCard component where hasCompetitorInsight is used (update Tooltip, TooltipTrigger/Badge text and TooltipContent); add the corresponding i18n keys to the translations files. Ensure you use the same t() import already used in this file.apps/desktop/src/renderer/components/TerminalGrid.tsx (1)
556-566:⚠️ Potential issue | 🟡 MinorHardcoded "Files" string breaks localization.
The "Files" button text on line 564 is hardcoded while other buttons in this component use
t().🌐 Proposed fix
{projectPath && ( <Button variant={fileExplorerOpen ? 'default' : 'outline'} size="sm" className="h-7 text-xs gap-1.5" onClick={toggleFileExplorer} > <FolderTree className="h-3 w-3" /> - Files + {t('common:actions.files')} </Button> )}As per coding guidelines: "All frontend user-facing text must use
react-i18nexttranslation keys."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/TerminalGrid.tsx` around lines 556 - 566, The "Files" button text is hardcoded; update the JSX to use the i18n translation function `t()` instead. Locate the Button rendering guarded by `projectPath` in TerminalGrid (the element with props variant, size, className and onClick={toggleFileExplorer}) and replace the literal "Files" with a translated string such as t('terminal.files') (or the appropriate key used elsewhere). Ensure `t` is imported/available in this component and add the translation key to your locale files if missing.apps/desktop/src/renderer/components/roadmap/FeatureDetailPanel.tsx (1)
97-99:⚠️ Potential issue | 🟠 MajorFeatureDetailPanel still has non-localized user-facing text.
This component still renders many hardcoded English labels/messages (e.g., section headers and delete dialog copy), so users in pt-BR/fr will see mixed-language UI.
As per coding guidelines
apps/desktop/src/renderer/**/*.{tsx,jsx}: all frontend user-facing text must usereact-i18nexttranslation keys.Also applies to: 103-107, 118-129, 135-155, 170-173, 187-190, 212-213, 280-291
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/roadmap/FeatureDetailPanel.tsx` around lines 97 - 99, FeatureDetailPanel renders hardcoded English UI text; import and use react-i18next's useTranslation in the FeatureDetailPanel component and replace all user-facing hardcoded strings (e.g., "Description", section headers, delete dialog title/body, confirm/cancel button labels, and any labels showing statuses) with translation keys via t('featureDetail.description'), t('featureDetail.delete.confirmTitle'), t('featureDetail.delete.confirmBody'), t('common.confirm'), t('common.cancel'), etc.; update the JSX in FeatureDetailPanel (and any inline dialog functions like the delete dialog handler or confirmDelete/onDelete callbacks) to call t(...) and pass interpolations for dynamic values (e.g., feature.title) so all visible text uses i18n keys and add the corresponding keys to the translation resource files.apps/desktop/src/renderer/components/changelog/ChangelogList.tsx (1)
173-179:⚠️ Potential issue | 🟠 MajorRemove hardcoded plural suffix and add missing French translations for changelog badge.
Line 178 appends
's'manually for English pluralization, breaking localization for French and Portuguese. Additionally, the French translation file is missing the entirechangelogsection containingtask,commit, anditemkeys.The pattern
{getSummaryCount() !== 1 ? 's' : ''}violates the i18n guideline since user-facing text must usereact-i18nexttranslation keys for all languages.Suggested fix
- const getSummaryLabel = () => { + const getSummaryLabelKey = () => { switch (sourceMode) { case 'tasks': - return t('changelog.task'); + return 'changelog.task'; case 'git-history': case 'branch-diff': - return t('changelog.commit'); + return 'changelog.commit'; default: - return t('changelog.item'); + return 'changelog.item'; } }; @@ <Badge variant="secondary" className="ml-2"> - {getSummaryCount()} {getSummaryLabel()}{getSummaryCount() !== 1 ? 's' : ''} + {t(getSummaryLabelKey(), { count: getSummaryCount() })} </Badge>Add the
changelogsection toapps/desktop/src/shared/i18n/locales/fr/common.json:"changelog": { "task": "tâche", "task_plural": "tâches", "commit": "commit", "commit_plural": "commits", "item": "élément", "item_plural": "éléments" }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/changelog/ChangelogList.tsx` around lines 173 - 179, Remove the hardcoded "'s'" plural suffix in the Badge rendering (referencing getSummaryCount(), getSummaryLabel(), Badge, Button) and instead use react-i18next pluralization by calling the translation key with the count (e.g. t('changelog.item', { count: getSummaryCount() })) or update getSummaryLabel() to return a translated, count-aware label; additionally add the missing French changelog keys to the French locale common.json so translations exist: provide "task"/"task_plural", "commit"/"commit_plural", and "item"/"item_plural" entries as described so getSummaryLabel()/t(...) work for French.apps/desktop/src/renderer/components/Worktrees.tsx (6)
465-471:⚠️ Potential issue | 🟡 MinorHardcoded string breaks localization.
This message needs to use a translation key.
🌐 Suggested fix
if (!selectedProject) { return ( <div className="flex h-full items-center justify-center"> - <p className="text-muted-foreground">Select a project to view worktrees</p> + <p className="text-muted-foreground">{t('dialogs:worktrees.selectProject')}</p> </div> ); }As per coding guidelines: "All frontend user-facing text must use
react-i18nexttranslation keys. Hardcoded strings in JSX/TSX break localization for non-English users."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/Worktrees.tsx` around lines 465 - 471, The hardcoded placeholder text in the Worktrees component (inside the selectedProject null check) must use react-i18next; replace the literal "Select a project to view worktrees" with a translation lookup via useTranslation (or t) and a new translation key (e.g., worktrees.selectProjectMessage), update the component to import/use the t function and call t('worktrees.selectProjectMessage') in the JSX, and add the corresponding key/value to the i18n resource files so localization works.
620-639:⚠️ Potential issue | 🟡 MinorMultiple hardcoded strings in stats section.
Several UI strings are not internationalized: "files changed", "commits ahead".
🌐 Suggested fix
<div className="flex flex-wrap gap-4 text-sm mb-4"> <div className="flex items-center gap-1.5 text-muted-foreground"> <FileCode className="h-3.5 w-3.5" /> - <span>{worktree.filesChanged ?? 0} files changed</span> + <span>{t('dialogs:worktrees.filesChanged', { count: worktree.filesChanged ?? 0 })}</span> </div> <div className="flex items-center gap-1.5 text-muted-foreground"> <ChevronRight className="h-3.5 w-3.5" /> - <span>{worktree.commitCount ?? 0} commits ahead</span> + <span>{t('dialogs:worktrees.commitsAhead', { count: worktree.commitCount ?? 0 })}</span> </div>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/Worktrees.tsx` around lines 620 - 639, The stats UI in Worktrees.tsx uses hardcoded strings ("files changed", "commits ahead") inside the CardContent block; replace those literals with i18n translation calls (e.g., use the project's translation hook/function) and add corresponding keys for worktree.filesChanged and worktree.commitCount (for example "worktree.filesChanged" and "worktree.commitsAhead") to the translation files; update the component to import/use the translator (e.g., useTranslation or t) and call it where the span text is rendered so the UI strings are localized consistently.
758-786:⚠️ Potential issue | 🟡 MinorHardcoded strings in terminal worktrees section.
Lines 761, 776, 785 contain hardcoded English text: "Created", "Copy Path", "Delete".
🌐 Suggested fix
{wt.createdAt && ( <div className="text-xs text-muted-foreground mb-4"> - Created {new Date(wt.createdAt).toLocaleDateString()} + {t('dialogs:worktrees.createdAt', { date: new Date(wt.createdAt).toLocaleDateString() })} </div> )} {/* Actions */} <div className="flex flex-wrap gap-2"> <Button variant="outline" size="sm" onClick={() => { navigator.clipboard.writeText(wt.worktreePath); }} > <FolderOpen className="h-3.5 w-3.5 mr-1.5" /> - Copy Path + {t('dialogs:worktrees.copyPath')} </Button> <Button variant="outline" size="sm" className="text-destructive hover:text-destructive hover:bg-destructive/10" onClick={() => setTerminalWorktreeToDelete(wt)} > <Trash2 className="h-3.5 w-3.5 mr-1.5" /> - Delete + {t('common:buttons.delete')} </Button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/Worktrees.tsx` around lines 758 - 786, Replace hardcoded English UI strings in the Worktrees component with calls to the app's i18n/translation utility (e.g., t or translate) so the text can be localized: change the "Created" label that renders when wt.createdAt is present to use t('worktrees.created') (or equivalent), replace the Button label "Copy Path" (the navigator.clipboard.writeText handler) with t('worktrees.copyPath'), and replace the Delete button label that triggers setTerminalWorktreeToDelete(wt) with t('worktrees.delete'); ensure you import the translation hook/function used across the app and update any aria/tooltip text if present to use the same keys.
650-700:⚠️ Potential issue | 🟡 MinorMultiple hardcoded button labels.
Lines 657, 688, 698 contain hardcoded English text: "Merge to {branch}", "Copy Path", "Delete".
🌐 Suggested fix
<Button variant="default" size="sm" onClick={() => openMergeDialog(worktree)} disabled={!task} > <GitMerge className="h-3.5 w-3.5 mr-1.5" /> - Merge to {worktree.baseBranch} + {t('dialogs:worktrees.mergeTo', { branch: worktree.baseBranch })} </Button><Button variant="outline" size="sm" onClick={() => { navigator.clipboard.writeText(worktree.path); }} > <FolderOpen className="h-3.5 w-3.5 mr-1.5" /> - Copy Path + {t('dialogs:worktrees.copyPath')} </Button> <Button variant="outline" size="sm" className="text-destructive hover:text-destructive hover:bg-destructive/10" onClick={() => confirmDelete(worktree)} disabled={!task && !worktree.isOrphaned} > <Trash2 className="h-3.5 w-3.5 mr-1.5" /> - Delete + {t('common:buttons.delete')} </Button>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/Worktrees.tsx` around lines 650 - 700, Replace the hardcoded English button labels in the Worktrees component with i18n translations using the existing t(...) function: use a translation key that accepts interpolation for the merge button (e.g., t('worktrees:mergeTo', { branch: worktree.baseBranch })) for "Merge to {branch}", and replace "Copy Path" and "Delete" with t('common:buttons.copyPath') and t('common:buttons.delete') (or equivalent keys you add); update the translation resource files to include these keys and ensure callers remain openMergeDialog(worktree) and confirmDelete(worktree) unchanged.
811-865:⚠️ Potential issue | 🟡 MinorHardcoded strings in merge dialog.
Multiple hardcoded strings in the merge dialog: "Source Branch", "Target Branch", "Changes", "commits", "files", "Merge Successful", "Merge Failed", "Conflicting files:".
🌐 Suggested fix
<div className="rounded-lg bg-muted p-4 text-sm space-y-3"> <div className="flex items-center justify-between"> - <span className="text-muted-foreground">Source Branch</span> + <span className="text-muted-foreground">{t('dialogs:worktrees.sourceBranch')}</span> <span className="font-mono text-info">{selectedWorktree.isOrphaned ? t('common:labels.orphaned') : selectedWorktree.branch}</span> </div> <div className="flex items-center justify-center"> <ChevronRight className="h-4 w-4 text-muted-foreground rotate-90" /> </div> <div className="flex items-center justify-between"> - <span className="text-muted-foreground">Target Branch</span> + <span className="text-muted-foreground">{t('dialogs:worktrees.targetBranch')}</span> <span className="font-mono">{selectedWorktree.baseBranch}</span> </div> <div className="border-t border-border pt-3 mt-3"> <div className="flex items-center justify-between text-xs"> - <span className="text-muted-foreground">Changes</span> + <span className="text-muted-foreground">{t('dialogs:worktrees.changes')}</span> <span> - {selectedWorktree.commitCount ?? 0} commits, {selectedWorktree.filesChanged ?? 0} files + {t('dialogs:worktrees.changesSummary', { commits: selectedWorktree.commitCount ?? 0, files: selectedWorktree.filesChanged ?? 0 })} </span> </div> </div> </div><p className={`font-medium ${mergeResult.success ? 'text-success' : 'text-destructive'}`}> - {mergeResult.success ? 'Merge Successful' : 'Merge Failed'} + {mergeResult.success ? t('dialogs:worktrees.mergeSuccessful') : t('dialogs:worktrees.mergeFailed')} </p> <p className="text-muted-foreground mt-1">{mergeResult.message}</p> {mergeResult.conflictFiles && mergeResult.conflictFiles.length > 0 && ( <div className="mt-2"> - <p className="text-xs font-medium">Conflicting files:</p> + <p className="text-xs font-medium">{t('dialogs:worktrees.conflictingFiles')}</p>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/Worktrees.tsx` around lines 811 - 865, The merge dialog contains hardcoded UI strings (e.g., "Source Branch", "Target Branch", "Changes", "commits", "files", "Merge Successful", "Merge Failed", "Conflicting files:") inside the Worktrees component around the selectedWorktree and mergeResult rendering; replace each literal with the i18n translation function (t) using descriptive keys (e.g., t('worktrees:sourceBranch'), t('worktrees:targetBranch'), t('worktrees:changes'), t('worktrees:commits'), t('worktrees:files'), t('worktrees:mergeSuccessful'), t('worktrees:mergeFailed'), t('worktrees:conflictingFiles')) so strings are localized, and add those keys to the appropriate locale resource files; ensure you update the places that render mergeResult.success, mergeResult.message, and the conflictFiles header to use the translated keys while preserving the existing structure and classes.
544-555:⚠️ Potential issue | 🟡 MinorHardcoded "Error" label.
The error label on line 550 is hardcoded in English.
🌐 Suggested fix
<div> - <p className="font-medium text-destructive">Error</p> + <p className="font-medium text-destructive">{t('common:labels.error')}</p> <p className="text-muted-foreground mt-1 whitespace-pre-line">{error}</p> </div>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/Worktrees.tsx` around lines 544 - 555, The "Error" heading in the Worktrees component is hardcoded; replace the literal text in the element that renders the label (the <p className="font-medium text-destructive">Error</p> in apps/desktop/src/renderer/components/Worktrees.tsx) with a localized string obtained from your project's i18n mechanism (e.g., call a translation function like t('error') or useTranslation().t('worktrees.error') and add the corresponding key to your locale files), and add/update the necessary import(s) for the translation hook/function so the component compiles.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/renderer/components/roadmap/RoadmapEmptyState.tsx`:
- Around line 2-19: The French locale is missing the translation keys used by
RoadmapEmptyState; add entries for "roadmap.emptyTitle",
"roadmap.emptyDescription", and "roadmap.generateButton" to fr/common.json using
appropriate French strings so useTranslation('common') in RoadmapEmptyState (and
the keys referenced) can resolve; ensure key names exactly match those in
en/common.json and pt-BR/common.json and follow the same JSON structure and
encoding.
In `@apps/desktop/src/renderer/components/SortableFeatureCard.tsx`:
- Around line 196-201: The badge text is ambiguous because it renders only
t(`roadmap.impact.${feature.impact}`); update the Badge content to use the label
template key so it provides context (e.g., use the "label" key under
roadmap.impact with the impact level injected or a dedicated label key like
roadmap.impact.label.<level>), referencing the Badge component,
ROADMAP_IMPACT_COLORS, feature.impact and the t() call in
SortableFeatureCard.tsx to locate the change.
In `@apps/desktop/src/shared/constants/i18n.ts`:
- Around line 6-12: The spellcheck mappings are missing entries for pt-BR; add a
mapping for the SupportedLanguage key 'pt-BR' in both SPELL_CHECK_LANGUAGE_MAP
(map 'pt-BR' to the correct spellchecker locale used by your engine, e.g.,
'pt-BR' or 'pt_BR' consistent with existing keys) and ADD_TO_DICTIONARY_LABELS
(add a Portuguese label such as "Adicionar ao dicionário" for the 'pt-BR' key)
so that when users select pt-BR the spellchecker and "Add to Dictionary" context
menu use Portuguese instead of falling back to en-US; update the objects named
SPELL_CHECK_LANGUAGE_MAP and ADD_TO_DICTIONARY_LABELS accordingly.
In `@apps/desktop/src/shared/i18n/index.ts`:
- Around line 71-84: Update the two spellcheck constants so pt-BR is supported:
in SPELL_CHECK_LANGUAGE_MAP add a "pt-BR" entry mapping to the appropriate
spellchecker codes (e.g. ["pt-BR"] or ["pt-BR","pt-PT"]) and in
ADD_TO_DICTIONARY_LABELS add "pt-BR": "Adicionar ao dicionário"; modify the
constants named SPELL_CHECK_LANGUAGE_MAP and ADD_TO_DICTIONARY_LABELS to include
these entries so Brazilian Portuguese uses the correct spellchecker and
context-menu label.
In `@apps/desktop/src/shared/i18n/locales/en/common.json`:
- Around line 743-744: The key "featuresCount" currently always uses the plural
form ("{{count}} features"), which is incorrect for count=1; update the locale
to supply singular and plural variants by adding a singular base "featuresCount"
(e.g., "{{count}} feature") and a corresponding "featuresCount_plural" (e.g.,
"{{count}} features") so i18n pluralization logic can pick the right form;
ensure you keep the "{{count}}" interpolation token in both strings.
In `@apps/desktop/src/shared/i18n/locales/en/terminal.json`:
- Around line 23-34: Add the missing French translations by adding the entire
"grid" object with these keys to the fr/terminal.json file: title, description,
newTerminal, terminalCount, history, restoreSessionsFrom, sessionCount,
sessionCount_plural, invokeClaudeAll, and clearTask; mirror the structure and
placeholders from the en/terminal.json "grid" object (e.g., "{{count}} / {{max}}
terminals" and pluralization keys) and provide appropriate French strings for
each key so fr/terminal.json matches the new UI entries introduced in
en/terminal.json.
In `@apps/desktop/src/shared/i18n/locales/pt-BR/common.json`:
- Around line 476-477: The pluralization key is inconsistent: change
"messageCount_other" to "messageCount_plural" in the locale object to match the
project's plural suffix convention (e.g., bulkDeleteSuccess_plural,
worktrees.bulkDeleteSuccess_plural); then search for any code referencing
"messageCount_other" and update those references to "messageCount_plural" (or
use i18n.t('messageCount', { count })) so runtime lookups use the unified plural
key.
In `@apps/desktop/src/shared/i18n/locales/pt-BR/terminal.json`:
- Around line 1-70: The French terminal locale is missing the entire "grid"
keyset (grid.title, grid.description, grid.newTerminal, grid.terminalCount,
grid.history, grid.restoreSessionsFrom, grid.sessionCount,
grid.sessionCount_plural, grid.invokeClaudeAll, grid.clearTask); add a "grid"
object to fr/terminal.json mirroring the keys present in pt-BR/terminal.json
(use the same key names and plural forms) and provide appropriate French
translations for each value so the FR locale matches the structure of EN/PT-BR
and avoids missing-key errors when code references grid.*.
---
Outside diff comments:
In `@apps/desktop/src/main/agent/agent-manager.ts`:
- Around line 615-639: The QA sessionConfig built in startQAProcess is missing
the language property so QA output won't respect user preferences; update the
SerializableSessionConfig object (the sessionConfig inside startQAProcess) to
include language: getAppLanguage() just like startSpecCreation and
startTaskExecution do, ensuring the QA reviewer session uses the app language.
In `@apps/desktop/src/main/ai/runners/roadmap.ts`:
- Around line 343-350: The atomic write in the preserved-features merge block
can collide because tmp filename uses only process.pid and skips the shared
file-lock; update the block around mergeFeatures/data to (1) obtain the same
roadmap file lock used in apps/desktop/src/main/utils/roadmap-utils.ts (e.g.,
call the lock helper such as withRoadmapLock/ acquireRoadmapLock or the
equivalent exported function) before writing, and (2) create a truly unique temp
file name for roadmapFile (include a random suffix or use a secure UUID/crypto
random bytes or mkstemp-style temp name instead of `${process.pid}`) then write
and rename while holding the lock; ensure you reference mergeFeatures and
roadmapFile in the change so the operation remains atomic and consistent with
other update paths.
In `@apps/desktop/src/renderer/components/changelog/ChangelogList.tsx`:
- Around line 173-179: Remove the hardcoded "'s'" plural suffix in the Badge
rendering (referencing getSummaryCount(), getSummaryLabel(), Badge, Button) and
instead use react-i18next pluralization by calling the translation key with the
count (e.g. t('changelog.item', { count: getSummaryCount() })) or update
getSummaryLabel() to return a translated, count-aware label; additionally add
the missing French changelog keys to the French locale common.json so
translations exist: provide "task"/"task_plural", "commit"/"commit_plural", and
"item"/"item_plural" entries as described so getSummaryLabel()/t(...) work for
French.
In `@apps/desktop/src/renderer/components/roadmap/FeatureCard.tsx`:
- Around line 46-55: The "Competitor Insight" badge and tooltip are hardcoded;
replace both occurrences with translation calls using the existing react-i18next
t() function (e.g., t('roadmap.competitorInsight.badge') for the Badge text and
t('roadmap.competitorInsight.tooltip') for TooltipContent) inside the
FeatureCard component where hasCompetitorInsight is used (update Tooltip,
TooltipTrigger/Badge text and TooltipContent); add the corresponding i18n keys
to the translations files. Ensure you use the same t() import already used in
this file.
In `@apps/desktop/src/renderer/components/roadmap/FeatureDetailPanel.tsx`:
- Around line 97-99: FeatureDetailPanel renders hardcoded English UI text;
import and use react-i18next's useTranslation in the FeatureDetailPanel
component and replace all user-facing hardcoded strings (e.g., "Description",
section headers, delete dialog title/body, confirm/cancel button labels, and any
labels showing statuses) with translation keys via
t('featureDetail.description'), t('featureDetail.delete.confirmTitle'),
t('featureDetail.delete.confirmBody'), t('common.confirm'), t('common.cancel'),
etc.; update the JSX in FeatureDetailPanel (and any inline dialog functions like
the delete dialog handler or confirmDelete/onDelete callbacks) to call t(...)
and pass interpolations for dynamic values (e.g., feature.title) so all visible
text uses i18n keys and add the corresponding keys to the translation resource
files.
In `@apps/desktop/src/renderer/components/SortableFeatureCard.tsx`:
- Around line 105-106: The SortableFeatureCard component contains hardcoded
user-facing strings (e.g., "Phase: {phaseName}" and other tooltip/source strings
rendered inside TooltipContent) that must be replaced with react-i18next
translation keys; update the JSX in SortableFeatureCard to use the t('...') hook
for each string (injecting variables like phaseName via interpolation or
t('key', { phaseName })), add corresponding entries to both en/*.json and
fr/*.json translation files for each new key, and ensure you import/use the
useTranslation hook at the top of the component so all tooltips and labels
(including the other strings referenced in the comment) are fully localized.
In `@apps/desktop/src/renderer/components/task-detail/TaskDetailModal.tsx`:
- Around line 417-419: Replace hardcoded UI strings in TaskDetailModal (the JSX
rendering "Overview", "Subtasks", "Logs", the subtasks suffix text shown near
{completedSubtasks}/{totalSubtasks}, and the sr-only "Close") with react-i18next
translation keys using the useTranslation hook (t('...')). Update the
corresponding translation files by adding those keys to both en/*.json and
fr/*.json so the labels are localized; ensure keys are descriptive (e.g.,
taskDetail.overview, taskDetail.subtasks, taskDetail.logs,
taskDetail.subtasksSuffix, taskDetail.close) and used consistently in the
component.
- Around line 99-103: Replace the hardcoded English toast/error strings in
TaskDetailModal handlers (calls to toast and any fallback error messages inside
the resume/handler functions) with react-i18next translation lookups using the
t(...) function (e.g., t('taskDetail.resumeError.title'),
t('taskDetail.resumeError.description')). Update the corresponding translation
keys in both en/*.json and fr/*.json (add keys such as
taskDetail.resumeError.title, taskDetail.resumeError.description and any others
used for the fallback messages referenced around the toast calls and other
handlers) so all user-facing strings are localized and no plain English literals
remain in TaskDetailModal.
In `@apps/desktop/src/renderer/components/task-detail/TaskHeader.tsx`:
- Around line 71-73: Replace the hardcoded "subtasks" label in TaskHeader.tsx
with a react-i18next translation call: use the t() helper to render the progress
text (e.g., t('labels.subtaskProgress', { completed: taskProgress.completed,
total: taskProgress.total })) inside the Badge where
{taskProgress.completed}/{taskProgress.total} subtasks currently appears, and
add the matching key ("labels.subtaskProgress": "{{completed}}/{{total}}
subtasks") to the translations (e.g., en/tasks.json and other locale files) so
all locales get the localized string.
In `@apps/desktop/src/renderer/components/TerminalGrid.tsx`:
- Around line 556-566: The "Files" button text is hardcoded; update the JSX to
use the i18n translation function `t()` instead. Locate the Button rendering
guarded by `projectPath` in TerminalGrid (the element with props variant, size,
className and onClick={toggleFileExplorer}) and replace the literal "Files" with
a translated string such as t('terminal.files') (or the appropriate key used
elsewhere). Ensure `t` is imported/available in this component and add the
translation key to your locale files if missing.
In `@apps/desktop/src/renderer/components/Worktrees.tsx`:
- Around line 465-471: The hardcoded placeholder text in the Worktrees component
(inside the selectedProject null check) must use react-i18next; replace the
literal "Select a project to view worktrees" with a translation lookup via
useTranslation (or t) and a new translation key (e.g.,
worktrees.selectProjectMessage), update the component to import/use the t
function and call t('worktrees.selectProjectMessage') in the JSX, and add the
corresponding key/value to the i18n resource files so localization works.
- Around line 620-639: The stats UI in Worktrees.tsx uses hardcoded strings
("files changed", "commits ahead") inside the CardContent block; replace those
literals with i18n translation calls (e.g., use the project's translation
hook/function) and add corresponding keys for worktree.filesChanged and
worktree.commitCount (for example "worktree.filesChanged" and
"worktree.commitsAhead") to the translation files; update the component to
import/use the translator (e.g., useTranslation or t) and call it where the span
text is rendered so the UI strings are localized consistently.
- Around line 758-786: Replace hardcoded English UI strings in the Worktrees
component with calls to the app's i18n/translation utility (e.g., t or
translate) so the text can be localized: change the "Created" label that renders
when wt.createdAt is present to use t('worktrees.created') (or equivalent),
replace the Button label "Copy Path" (the navigator.clipboard.writeText handler)
with t('worktrees.copyPath'), and replace the Delete button label that triggers
setTerminalWorktreeToDelete(wt) with t('worktrees.delete'); ensure you import
the translation hook/function used across the app and update any aria/tooltip
text if present to use the same keys.
- Around line 650-700: Replace the hardcoded English button labels in the
Worktrees component with i18n translations using the existing t(...) function:
use a translation key that accepts interpolation for the merge button (e.g.,
t('worktrees:mergeTo', { branch: worktree.baseBranch })) for "Merge to
{branch}", and replace "Copy Path" and "Delete" with
t('common:buttons.copyPath') and t('common:buttons.delete') (or equivalent keys
you add); update the translation resource files to include these keys and ensure
callers remain openMergeDialog(worktree) and confirmDelete(worktree) unchanged.
- Around line 811-865: The merge dialog contains hardcoded UI strings (e.g.,
"Source Branch", "Target Branch", "Changes", "commits", "files", "Merge
Successful", "Merge Failed", "Conflicting files:") inside the Worktrees
component around the selectedWorktree and mergeResult rendering; replace each
literal with the i18n translation function (t) using descriptive keys (e.g.,
t('worktrees:sourceBranch'), t('worktrees:targetBranch'),
t('worktrees:changes'), t('worktrees:commits'), t('worktrees:files'),
t('worktrees:mergeSuccessful'), t('worktrees:mergeFailed'),
t('worktrees:conflictingFiles')) so strings are localized, and add those keys to
the appropriate locale resource files; ensure you update the places that render
mergeResult.success, mergeResult.message, and the conflictFiles header to use
the translated keys while preserving the existing structure and classes.
- Around line 544-555: The "Error" heading in the Worktrees component is
hardcoded; replace the literal text in the element that renders the label (the
<p className="font-medium text-destructive">Error</p> in
apps/desktop/src/renderer/components/Worktrees.tsx) with a localized string
obtained from your project's i18n mechanism (e.g., call a translation function
like t('error') or useTranslation().t('worktrees.error') and add the
corresponding key to your locale files), and add/update the necessary import(s)
for the translation hook/function so the component compiles.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 84d629f8-5d8f-4ed8-b568-1be317504980
📒 Files selected for processing (37)
apps/desktop/src/main/agent/agent-manager.tsapps/desktop/src/main/agent/agent-queue.tsapps/desktop/src/main/agent/types.tsapps/desktop/src/main/ai/agent/types.tsapps/desktop/src/main/ai/agent/worker.tsapps/desktop/src/main/ai/prompts/prompt-loader.tsapps/desktop/src/main/ai/prompts/types.tsapps/desktop/src/main/ai/runners/roadmap.tsapps/desktop/src/main/ipc-handlers/roadmap-handlers.tsapps/desktop/src/renderer/components/AddFeatureDialog.tsxapps/desktop/src/renderer/components/RoadmapKanbanView.tsxapps/desktop/src/renderer/components/SortableFeatureCard.tsxapps/desktop/src/renderer/components/TerminalGrid.tsxapps/desktop/src/renderer/components/Worktrees.tsxapps/desktop/src/renderer/components/changelog/ChangelogList.tsxapps/desktop/src/renderer/components/roadmap/FeatureCard.tsxapps/desktop/src/renderer/components/roadmap/FeatureDetailPanel.tsxapps/desktop/src/renderer/components/roadmap/RoadmapEmptyState.tsxapps/desktop/src/renderer/components/roadmap/RoadmapTabs.tsxapps/desktop/src/renderer/components/task-detail/TaskDetailModal.tsxapps/desktop/src/renderer/components/task-detail/TaskHeader.tsxapps/desktop/src/shared/constants/i18n.tsapps/desktop/src/shared/constants/roadmap.tsapps/desktop/src/shared/i18n/index.tsapps/desktop/src/shared/i18n/locales/en/common.jsonapps/desktop/src/shared/i18n/locales/en/terminal.jsonapps/desktop/src/shared/i18n/locales/pt-BR/common.jsonapps/desktop/src/shared/i18n/locales/pt-BR/dialogs.jsonapps/desktop/src/shared/i18n/locales/pt-BR/errors.jsonapps/desktop/src/shared/i18n/locales/pt-BR/gitlab.jsonapps/desktop/src/shared/i18n/locales/pt-BR/navigation.jsonapps/desktop/src/shared/i18n/locales/pt-BR/onboarding.jsonapps/desktop/src/shared/i18n/locales/pt-BR/settings.jsonapps/desktop/src/shared/i18n/locales/pt-BR/taskReview.jsonapps/desktop/src/shared/i18n/locales/pt-BR/tasks.jsonapps/desktop/src/shared/i18n/locales/pt-BR/terminal.jsonapps/desktop/src/shared/i18n/locales/pt-BR/welcome.json
| import { useTranslation } from 'react-i18next'; | ||
| import { Button } from '../ui/button'; | ||
| import { Card } from '../ui/card'; | ||
| import type { RoadmapEmptyStateProps } from './types'; | ||
|
|
||
| export function RoadmapEmptyState({ onGenerate }: RoadmapEmptyStateProps) { | ||
| const { t } = useTranslation('common'); | ||
| return ( | ||
| <div className="flex h-full items-center justify-center"> | ||
| <Card className="w-full max-w-lg p-8 text-center"> | ||
| <Map className="h-12 w-12 text-muted-foreground mx-auto mb-4" /> | ||
| <h2 className="text-xl font-semibold mb-2">No Roadmap Yet</h2> | ||
| <h2 className="text-xl font-semibold mb-2">{t('roadmap.emptyTitle')}</h2> | ||
| <p className="text-muted-foreground mb-6"> | ||
| Generate an AI-powered roadmap that understands your project's target audience and | ||
| creates a strategic feature plan. | ||
| {t('roadmap.emptyDescription')} | ||
| </p> | ||
| <Button onClick={onGenerate} size="lg"> | ||
| <Sparkles className="h-4 w-4 mr-2" /> | ||
| Generate Roadmap | ||
| {t('roadmap.generateButton')} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify roadmap translation keys exist in all locale files
for locale in en fr pt-BR; do
echo "=== Checking $locale/common.json ==="
fd -t f "common.json" "apps/desktop/src/shared/i18n/locales/$locale" --exec cat {} | \
jq '.roadmap | keys' 2>/dev/null || echo "roadmap key not found or file missing"
doneRepository: AndyMik90/Aperant
Length of output: 1393
French translation keys missing for roadmap component.
The component correctly uses useTranslation('common') and replaces hardcoded strings with translation keys. However, verification shows that the required keys (roadmap.emptyTitle, roadmap.emptyDescription, roadmap.generateButton) are present in en/common.json and pt-BR/common.json but missing from fr/common.json. Add these translations to the French locale file to complete localization support.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/components/roadmap/RoadmapEmptyState.tsx` around
lines 2 - 19, The French locale is missing the translation keys used by
RoadmapEmptyState; add entries for "roadmap.emptyTitle",
"roadmap.emptyDescription", and "roadmap.generateButton" to fr/common.json using
appropriate French strings so useTranslation('common') in RoadmapEmptyState (and
the keys referenced) can resolve; ensure key names exactly match those in
en/common.json and pt-BR/common.json and follow the same JSON structure and
encoding.
| <Badge | ||
| variant="outline" | ||
| className={cn('text-[10px] px-1.5 py-0', ROADMAP_IMPACT_COLORS[feature.impact])} | ||
| > | ||
| {feature.impact} | ||
| {t(`roadmap.impact.${feature.impact}`)} | ||
| </Badge> |
There was a problem hiding this comment.
Impact badge lost context and can be confused with complexity.
Rendering only roadmap.impact.<level> makes the badge ambiguous (e.g., two “Low” badges). Use the label template key for clearer output.
🎯 Suggested patch
- {t(`roadmap.impact.${feature.impact}`)}
+ {t('roadmap.impact.label', { impact: t(`roadmap.impact.${feature.impact}`) })}📝 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.
| <Badge | |
| variant="outline" | |
| className={cn('text-[10px] px-1.5 py-0', ROADMAP_IMPACT_COLORS[feature.impact])} | |
| > | |
| {feature.impact} | |
| {t(`roadmap.impact.${feature.impact}`)} | |
| </Badge> | |
| <Badge | |
| variant="outline" | |
| className={cn('text-[10px] px-1.5 py-0', ROADMAP_IMPACT_COLORS[feature.impact])} | |
| > | |
| {t('roadmap.impact.label', { impact: t(`roadmap.impact.${feature.impact}`) })} | |
| </Badge> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/components/SortableFeatureCard.tsx` around lines
196 - 201, The badge text is ambiguous because it renders only
t(`roadmap.impact.${feature.impact}`); update the Badge content to use the label
template key so it provides context (e.g., use the "label" key under
roadmap.impact with the impact level injected or a dedicated label key like
roadmap.impact.label.<level>), referencing the Badge component,
ROADMAP_IMPACT_COLORS, feature.impact and the t() call in
SortableFeatureCard.tsx to locate the change.
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/shared/i18n/locales/en/common.json (1)
691-759:⚠️ Potential issue | 🟠 MajorMirror these new common keys into
fr/common.jsonbefore shipping.This adds roadmap and changelog UI copy in English, but the French locale only picked up
roadmap.featureDetail. The new empty-state strings, status/priority/complexity/impact labels, tabs, kanban text,insight,unknownStatus, and the wholechangelog.*block will fall back to English for an already supported locale.As per coding guidelines "When adding new UI text, add keys to ALL language files in
en/*.jsonandfr/*.json."Also applies to: 987-1003
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/shared/i18n/locales/en/common.json` around lines 691 - 759, Add the new English localization keys into the French common file: mirror all keys added in en/common.json (e.g. "emptyTitle", "emptyDescription", "generateButton", "showMoreFeatures"/"showMoreFeatures_plural", "showLessFeatures", "archiveFeatureConfirmTitle"/"archiveFeatureConfirmDescription", "status" object, "priority" object, "complexity" object, "featureDetail" entries like "severityLabel", "impact" object, "tabs", "kanban" object, "insight", "featuresCount", "unknownStatus", and the entire "changelog.*" block) into fr/common.json with appropriate French translations (or placeholders if translation is pending) so the French locale doesn't fall back to English.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/renderer/components/roadmap/FeatureDetailPanel.tsx`:
- Line 53: In the FeatureDetailPanel component, update the drawer container div
(the element with className containing w-[30rem]) to use responsive sizing by
replacing w-[30rem] with w-full max-w-[30rem]; i.e., locate the div in
FeatureDetailPanel.tsx that currently reads className="fixed inset-y-0 right-0
w-[30rem] bg-card border-l border-border shadow-lg flex flex-col z-50" and
change it to className="fixed inset-y-0 right-0 w-full max-w-[30rem] bg-card
border-l border-border shadow-lg flex flex-col z-50".
- Line 212: The severity enum is being passed raw into the translation key,
causing mixed-language output; update FeatureDetailPanel (where
t('roadmap.featureDetail.severityLabel', { severity: insight.severity }) is
used) to first translate the severity via
t('roadmap.featureDetail.severity.{low|medium|high}', { }), then pass that
translated string into the severityLabel interpolation (e.g. severity: t(...));
also add the new keys roadmap.featureDetail.severity.low, .medium, and .high
with correct translations to all locale files (en/common.json, fr/common.json,
pt-BR/common.json).
In `@apps/desktop/src/renderer/components/task-detail/TaskMetadata.tsx`:
- Around line 172-175: The hardcoded English fragments in formatRelativeTime
produce mixed-language timestamps; update formatRelativeTime (in
apps/desktop/src/renderer/lib/utils.ts) to use Intl.RelativeTimeFormat with the
current i18next language (or accept a locale parameter) and return a fully
localized string, then keep TaskMetadata.tsx using t('tasks:metadata.created', {
time: formatRelativeTime(task.createdAt) }); ensure you handle fallbacks for
environments without Intl.RelativeTimeFormat and preserve existing granularity
(seconds/minutes/hours/days).
In `@apps/desktop/src/shared/i18n/locales/pt-BR/common.json`:
- Around line 345-349: The "merged" PR state in the JSON "state" object is still
in English; update the "merged" key value to the Portuguese translation (e.g.,
"Mesclado") so the pt-BR locale shows all PR states translated, and keep the
keys "state", "open", "closed", "merged" intact to preserve usage elsewhere.
In `@apps/desktop/src/shared/i18n/locales/pt-BR/settings.json`:
- Around line 32-39: The settings.json locale file contains partial English in
the "accounts" and "updates" entries; update the "accounts.description" value to
a full Portuguese string (e.g., replace "Claude e API endpoints" with a proper
translation like "Claude e endpoints da API") and translate
"updates.description" from "Updates do app" to full Portuguese (e.g.,
"Atualizações do aplicativo"), then scan the rest of pt-BR locale files for any
remaining English phrases and complete all translations before enabling the
pt-BR locale; target the JSON keys "accounts.title", "accounts.description",
"updates.title", and "updates.description" to make the edits.
---
Outside diff comments:
In `@apps/desktop/src/shared/i18n/locales/en/common.json`:
- Around line 691-759: Add the new English localization keys into the French
common file: mirror all keys added in en/common.json (e.g. "emptyTitle",
"emptyDescription", "generateButton",
"showMoreFeatures"/"showMoreFeatures_plural", "showLessFeatures",
"archiveFeatureConfirmTitle"/"archiveFeatureConfirmDescription", "status"
object, "priority" object, "complexity" object, "featureDetail" entries like
"severityLabel", "impact" object, "tabs", "kanban" object, "insight",
"featuresCount", "unknownStatus", and the entire "changelog.*" block) into
fr/common.json with appropriate French translations (or placeholders if
translation is pending) so the French locale doesn't fall back to English.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 65c3d531-924e-4040-9543-c0c5c24814ba
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json,!**/package-lock.json
📒 Files selected for processing (11)
apps/desktop/electron.vite.config.tsapps/desktop/src/renderer/components/roadmap/FeatureDetailPanel.tsxapps/desktop/src/renderer/components/settings/AppSettings.tsxapps/desktop/src/renderer/components/task-detail/TaskMetadata.tsxapps/desktop/src/shared/i18n/locales/en/common.jsonapps/desktop/src/shared/i18n/locales/en/tasks.jsonapps/desktop/src/shared/i18n/locales/fr/common.jsonapps/desktop/src/shared/i18n/locales/fr/tasks.jsonapps/desktop/src/shared/i18n/locales/pt-BR/common.jsonapps/desktop/src/shared/i18n/locales/pt-BR/settings.jsonapps/desktop/src/shared/i18n/locales/pt-BR/tasks.json
|
|
||
| return ( | ||
| <div className="fixed inset-y-0 right-0 w-96 bg-card border-l border-border shadow-lg flex flex-col z-50"> | ||
| <div className="fixed inset-y-0 right-0 w-[30rem] bg-card border-l border-border shadow-lg flex flex-col z-50"> |
There was a problem hiding this comment.
Keep the drawer width responsive.
w-[30rem] forces a 480px panel even when the desktop window is narrower, so part of the drawer can end up off-screen. w-full max-w-[30rem] keeps the extra room for localized copy without breaking small windows.
♻️ Proposed fix
- <div className="fixed inset-y-0 right-0 w-[30rem] bg-card border-l border-border shadow-lg flex flex-col z-50">
+ <div className="fixed inset-y-0 right-0 w-full max-w-[30rem] bg-card border-l border-border shadow-lg flex flex-col z-50">📝 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 className="fixed inset-y-0 right-0 w-[30rem] bg-card border-l border-border shadow-lg flex flex-col z-50"> | |
| <div className="fixed inset-y-0 right-0 w-full max-w-[30rem] bg-card border-l border-border shadow-lg flex flex-col z-50"> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/components/roadmap/FeatureDetailPanel.tsx` at line
53, In the FeatureDetailPanel component, update the drawer container div (the
element with className containing w-[30rem]) to use responsive sizing by
replacing w-[30rem] with w-full max-w-[30rem]; i.e., locate the div in
FeatureDetailPanel.tsx that currently reads className="fixed inset-y-0 right-0
w-[30rem] bg-card border-l border-border shadow-lg flex flex-col z-50" and
change it to className="fixed inset-y-0 right-0 w-full max-w-[30rem] bg-card
border-l border-border shadow-lg flex flex-col z-50".
| }`} | ||
| > | ||
| {insight.severity} severity | ||
| {t('roadmap.featureDetail.severityLabel', { severity: insight.severity })} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python - <<'PY'
import json
from pathlib import Path
root = Path("apps/desktop/src/shared/i18n/locales")
for path in sorted(root.glob("*/common.json")):
data = json.loads(path.read_text(encoding="utf-8"))
feature_detail = data.get("roadmap", {}).get("featureDetail", {})
severity = feature_detail.get("severity")
print(path)
print(" severityLabel:", feature_detail.get("severityLabel"))
print(" severity keys:", sorted(severity.keys()) if isinstance(severity, dict) else None)
PY
echo
rg -n -C2 "severityLabel|insight\\.severity" apps/desktop/src/renderer/components/roadmap/FeatureDetailPanel.tsx || trueRepository: AndyMik90/Aperant
Length of output: 1040
Translate the severity value before interpolating it.
insight.severity is the raw enum (high/medium/low), so this badge renders mixed-language text like Severidade high in pt-BR. Add locale keys for the severity values and pass the translated value into severityLabel.
♻️ Proposed fix
- {t('roadmap.featureDetail.severityLabel', { severity: insight.severity })}
+ {t('roadmap.featureDetail.severityLabel', {
+ severity: t(`roadmap.featureDetail.severity.${insight.severity}`),
+ })}Add roadmap.featureDetail.severity.{low,medium,high} keys to all locale files (en/common.json, fr/common.json, pt-BR/common.json) with appropriate translations for each language.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/components/roadmap/FeatureDetailPanel.tsx` at line
212, The severity enum is being passed raw into the translation key, causing
mixed-language output; update FeatureDetailPanel (where
t('roadmap.featureDetail.severityLabel', { severity: insight.severity }) is
used) to first translate the severity via
t('roadmap.featureDetail.severity.{low|medium|high}', { }), then pass that
translated string into the severityLabel interpolation (e.g. severity: t(...));
also add the new keys roadmap.featureDetail.severity.low, .medium, and .high
with correct translations to all locale files (en/common.json, fr/common.json,
pt-BR/common.json).
| {t('tasks:metadata.created', { time: formatRelativeTime(task.createdAt) })} | ||
| </span> | ||
| <span className="text-border">•</span> | ||
| <span>Updated {formatRelativeTime(task.updatedAt)}</span> | ||
| <span>{t('tasks:metadata.updated', { time: formatRelativeTime(task.updatedAt) })}</span> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Definition(s) and call sites:"
rg -n -C4 -g '*.ts' -g '*.tsx' 'formatRelativeTime' apps/desktop/src
echo
echo "Potential hardcoded relative-time fragments:"
rg -n -C2 -g '*.ts' -g '*.tsx' '"Just now"|"m ago"|"h ago"|"d ago"' apps/desktop/srcRepository: AndyMik90/Aperant
Length of output: 6670
🏁 Script executed:
cat -n apps/desktop/src/renderer/lib/utils.ts | sed -n '23,60p'Repository: AndyMik90/Aperant
Length of output: 1739
Move relative time formatting into i18n or use locale-aware Intl.RelativeTimeFormat.
The formatRelativeTime() function in apps/desktop/src/renderer/lib/utils.ts (lines 34–37) hardcodes English fragments ('just now', 'm ago', 'h ago', 'd ago') regardless of the active language. This causes mixed-language timestamps in the UI—for example, a French user will see "Créé 2m ago" (French translation key + English time fragment).
Options:
- Refactor
formatRelativeTime()to useIntl.RelativeTimeFormatwith the current i18next language - Move relative time formatting into the i18n translation keys (e.g.,
tasks:metadata.createdcould handle the "2m ago" → "il y a 2m" conversion)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/components/task-detail/TaskMetadata.tsx` around
lines 172 - 175, The hardcoded English fragments in formatRelativeTime produce
mixed-language timestamps; update formatRelativeTime (in
apps/desktop/src/renderer/lib/utils.ts) to use Intl.RelativeTimeFormat with the
current i18next language (or accept a locale parameter) and return a fully
localized string, then keep TaskMetadata.tsx using t('tasks:metadata.created', {
time: formatRelativeTime(task.createdAt) }); ensure you handle fallbacks for
environments without Intl.RelativeTimeFormat and preserve existing granularity
(seconds/minutes/hours/days).
| "state": { | ||
| "open": "Aberto", | ||
| "closed": "Fechado", | ||
| "merged": "Merged" | ||
| }, |
There was a problem hiding this comment.
Localize the merged PR state too.
open and closed are translated, but merged is still English. That leaves the PR state badge mixed-language in the pt-BR UI.
♻️ Proposed fix
"open": "Aberto",
"closed": "Fechado",
- "merged": "Merged"
+ "merged": "Mesclado"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/shared/i18n/locales/pt-BR/common.json` around lines 345 -
349, The "merged" PR state in the JSON "state" object is still in English;
update the "merged" key value to the Portuguese translation (e.g., "Mesclado")
so the pt-BR locale shows all PR states translated, and keep the keys "state",
"open", "closed", "merged" intact to preserve usage elsewhere.
| "accounts": { | ||
| "title": "Contas", | ||
| "description": "Claude e API endpoints" | ||
| }, | ||
| "updates": { | ||
| "title": "Atualizações", | ||
| "description": "Updates do app" | ||
| }, |
There was a problem hiding this comment.
Finish the remaining pt-BR copy pass before enabling this locale.
The first settings screen still mixes English into the Portuguese UI (API endpoints, Updates do app), so users will immediately get a partial translation experience.
📝 Example fix
"accounts": {
"title": "Contas",
- "description": "Claude e API endpoints"
+ "description": "Claude e endpoints de API"
},
"updates": {
"title": "Atualizações",
- "description": "Updates do app"
+ "description": "Atualizações do app"
},Based on learnings "Before adding a new language to AVAILABLE_LANGUAGES, ensure UI translations exist and are complete in apps/frontend/src/shared/i18n/locales. Do not advertise official support for a language until its locale files are fully translated and validated, to avoid user-facing issues."
📝 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.
| "accounts": { | |
| "title": "Contas", | |
| "description": "Claude e API endpoints" | |
| }, | |
| "updates": { | |
| "title": "Atualizações", | |
| "description": "Updates do app" | |
| }, | |
| "accounts": { | |
| "title": "Contas", | |
| "description": "Claude e endpoints de API" | |
| }, | |
| "updates": { | |
| "title": "Atualizações", | |
| "description": "Atualizações do app" | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/shared/i18n/locales/pt-BR/settings.json` around lines 32 -
39, The settings.json locale file contains partial English in the "accounts" and
"updates" entries; update the "accounts.description" value to a full Portuguese
string (e.g., replace "Claude e API endpoints" with a proper translation like
"Claude e endpoints da API") and translate "updates.description" from "Updates
do app" to full Portuguese (e.g., "Atualizações do aplicativo"), then scan the
rest of pt-BR locale files for any remaining English phrases and complete all
translations before enabling the pt-BR locale; target the JSON keys
"accounts.title", "accounts.description", "updates.title", and
"updates.description" to make the edits.
…ded strings, fix race condition, add missing locale keys
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/desktop/src/renderer/components/TerminalGrid.tsx (1)
653-657:⚠️ Potential issue | 🟡 MinorHardcoded fallback string breaks localization.
The
'Terminal'fallback on line 656 is a user-facing string that should use a translation key to maintain localization for non-English users.Proposed fix
{draggingTerminal && ( <div className="flex items-center gap-2 bg-card border border-primary rounded-md px-3 py-2 shadow-lg"> <TerminalSquare className="h-4 w-4 text-primary" /> - <span className="text-sm font-medium">{draggingTerminal.title || 'Terminal'}</span> + <span className="text-sm font-medium">{draggingTerminal.title || t('terminal:grid.newTerminal')}</span> </div> )}As per coding guidelines: "All frontend user-facing text must use
react-i18nexttranslation keys. Hardcoded strings in JSX/TSX break localization for non-English users."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/TerminalGrid.tsx` around lines 653 - 657, The hardcoded fallback 'Terminal' breaks localization; replace it with a react-i18next translation key by using the useTranslation hook's t function and feeding t('terminal.fallback') (or your existing key like t('terminal.label')) instead of the literal string in the JSX where draggingTerminal.title is used; ensure the component (TerminalGrid / the function rendering the dragging preview) imports and calls const { t } = useTranslation() and that the chosen translation key is added to your i18n resource files.apps/desktop/src/renderer/components/task-detail/TaskDetailModal.tsx (1)
164-164:⚠️ Potential issue | 🟡 MinorHardcoded fallback string breaks localization.
The fallback message
'Changes staged in main project'bypasses i18n. This should use a translation key for consistency with the rest of the component.🌐 Proposed fix
- state.setStagedSuccess(result.data.message || 'Changes staged in main project'); + state.setStagedSuccess(result.data.message || t('tasks:notifications.changesStaged'));Ensure the key
tasks:notifications.changesStagedis added to all locale files (en/tasks.json,fr/tasks.json,pt-BR/tasks.json).Based on learnings: "All frontend user-facing text must use
react-i18nexttranslation keys. Hardcoded strings in JSX/TSX break localization for non-English users."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/desktop/src/renderer/components/task-detail/TaskDetailModal.tsx` at line 164, Replace the hardcoded fallback string in the state.setStagedSuccess call with a react-i18next translation key (use t('tasks:notifications.changesStaged')) so the message is localized; update the useTranslation usage in this component if needed to ensure t is available, and add the key tasks:notifications.changesStaged to all locale files (en/tasks.json, fr/tasks.json, pt-BR/tasks.json) with appropriate translations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/desktop/src/main/ai/runners/roadmap.ts`:
- Around line 129-132: Duplicate multilingual instruction strings
(languageInstruction used in the discovery phase and the similar features-phase
text) should be extracted into a single helper function to avoid duplication and
ease maintenance; add a function (e.g., buildLanguageInstruction(language:
string | undefined, enumExamples: string): string) that returns '' for
undefined/'en' and otherwise composes the LANGUAGE block with the provided
enumExamples, then replace the inline constructions used to set
languageInstruction and the features-phase equivalent with calls to
buildLanguageInstruction (pass the appropriate enumExamples for discovery vs
features) so callers like the discovery contextBlock and the features context
block reuse the single helper.
In `@apps/desktop/src/renderer/components/SortableFeatureCard.tsx`:
- Line 227: The hardcoded provider label in SortableFeatureCard.tsx bypasses
i18n; replace the conditional that currently renders {feature.source?.provider
=== 'canny' ? 'Canny' : t('roadmap.externalSource')} with a translation lookup
(e.g., use t('roadmap.providers.canny') when feature.source?.provider ===
'canny') and fallback to t('roadmap.externalSource') for others, and add the
corresponding "roadmap.providers.canny" key to the locale files so the provider
name is localized consistently.
In `@apps/desktop/src/renderer/components/task-detail/TaskDetailModal.tsx`:
- Around line 407-416: Extract the inline reviewReasonKeyMap inside
TaskDetailModal to a module-level constant named REVIEW_REASON_KEY_MAP (place it
near the top of the file after imports), keep the same key/value pairs
(completed, errors -> hasErrors, plan_review -> approvePlan, stopped ->
stopped), and then update the JSX where reviewReasonKeyMap is referenced to use
REVIEW_REASON_KEY_MAP and preserve the existing logic that resolves reasonKey
with REVIEW_REASON_KEY_MAP[task.reviewReason || ''] || 'qaIssues' before passing
to t(`tasks:reviewReason.${reasonKey}`).
In `@apps/desktop/src/shared/i18n/locales/en/dialogs.json`:
- Around line 66-79: Add the 13 missing French translations for the new worktree
UI keys to the fr/dialogs.json file: add entries for "selectProject",
"filesChanged", "commitsAhead", "mergeTo", "copyPath", "createdAt",
"sourceBranch", "targetBranch", "changes", "changesSummary", "mergeSuccessful",
"mergeFailed", and "conflictingFiles" with appropriate French strings (e.g.,
"Select a project to view worktrees" -> "Sélectionnez un projet pour afficher
les worktrees", "filesChanged" -> "{{count}} fichiers modifiés", "commitsAhead"
-> "{{count}} commits d'avance", "mergeTo" -> "Fusionner vers {{branch}}",
"copyPath" -> "Copier le chemin", "createdAt" -> "Créé le {{date}}",
"sourceBranch" -> "Branche source", "targetBranch" -> "Branche cible", "changes"
-> "Modifications", "changesSummary" -> "{{commits}} commits, {{files}}
fichiers", "mergeSuccessful" -> "Fusion réussie", "mergeFailed" -> "Échec de la
fusion", "conflictingFiles" -> "Fichiers en conflit :"), ensure keys match
exactly the English identifiers and the JSON stays valid (comma placement,
escaping).
In `@apps/desktop/src/shared/i18n/locales/en/tasks.json`:
- Around line 35-37: Remove the duplicate translation key by deleting the unused
"subtasksProgress" entry and keep only "subtaskProgress" (or vice-versa if you
prefer) in both locales so there's a single canonical key; also verify
TaskHeader.tsx references the retained key (subtaskProgress) and update the fr
locale to mirror the same change to keep translations in sync.
In `@apps/desktop/src/shared/i18n/locales/fr/tasks.json`:
- Around line 35-37: Duplicate translation keys subtaskProgress and
subtasksProgress both map to the same French string; TaskHeader.tsx only uses
subtaskProgress, so remove the unused key or confirm both are required. Locate
the keys subtaskProgress and subtasksProgress in the locales/fr/tasks.json and
delete subtasksProgress (or rename/merge if another component needs it), then
run a quick search for subtasksProgress usage across the codebase to ensure no
other component depends on it and update any references to use subtaskProgress
if necessary.
In `@apps/desktop/src/shared/i18n/locales/pt-BR/tasks.json`:
- Around line 36-38: The two i18n keys "subtasksProgress" and "subtaskProgress"
contain identical Portuguese values and likely represent a duplicate or naming
mistake; inspect usages of subtasksProgress and subtaskProgress across the
codebase (components and react-i18next calls) to determine which one is actually
referenced, then either remove the unused key or consolidate them into a single
key and update callers accordingly; if pluralization is intended, follow
react-i18next conventions (use a single base key with plural/_plural or add
count usage) and rename keys to a clear, distinct name to reflect singular vs
plural contexts.
---
Outside diff comments:
In `@apps/desktop/src/renderer/components/task-detail/TaskDetailModal.tsx`:
- Line 164: Replace the hardcoded fallback string in the state.setStagedSuccess
call with a react-i18next translation key (use
t('tasks:notifications.changesStaged')) so the message is localized; update the
useTranslation usage in this component if needed to ensure t is available, and
add the key tasks:notifications.changesStaged to all locale files
(en/tasks.json, fr/tasks.json, pt-BR/tasks.json) with appropriate translations.
In `@apps/desktop/src/renderer/components/TerminalGrid.tsx`:
- Around line 653-657: The hardcoded fallback 'Terminal' breaks localization;
replace it with a react-i18next translation key by using the useTranslation
hook's t function and feeding t('terminal.fallback') (or your existing key like
t('terminal.label')) instead of the literal string in the JSX where
draggingTerminal.title is used; ensure the component (TerminalGrid / the
function rendering the dragging preview) imports and calls const { t } =
useTranslation() and that the chosen translation key is added to your i18n
resource files.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 0a159722-4995-42a9-8598-57af25ca04f5
📒 Files selected for processing (20)
apps/desktop/src/main/agent/agent-manager.tsapps/desktop/src/main/ai/prompts/prompt-loader.tsapps/desktop/src/main/ai/runners/roadmap.tsapps/desktop/src/renderer/components/SortableFeatureCard.tsxapps/desktop/src/renderer/components/TerminalGrid.tsxapps/desktop/src/renderer/components/Worktrees.tsxapps/desktop/src/renderer/components/changelog/ChangelogList.tsxapps/desktop/src/renderer/components/roadmap/FeatureCard.tsxapps/desktop/src/renderer/components/task-detail/TaskDetailModal.tsxapps/desktop/src/renderer/components/task-detail/TaskHeader.tsxapps/desktop/src/shared/constants/spellcheck.tsapps/desktop/src/shared/i18n/locales/en/common.jsonapps/desktop/src/shared/i18n/locales/en/dialogs.jsonapps/desktop/src/shared/i18n/locales/en/tasks.jsonapps/desktop/src/shared/i18n/locales/fr/common.jsonapps/desktop/src/shared/i18n/locales/fr/tasks.jsonapps/desktop/src/shared/i18n/locales/fr/terminal.jsonapps/desktop/src/shared/i18n/locales/pt-BR/common.jsonapps/desktop/src/shared/i18n/locales/pt-BR/dialogs.jsonapps/desktop/src/shared/i18n/locales/pt-BR/tasks.json
| const languageInstruction = language && language !== 'en' | ||
| ? `\n\n**LANGUAGE**: You MUST write ALL human-readable text content in ${language}. This includes: project descriptions, pain points, goals, vision statements, value propositions, success metrics, feature names, known gaps, differentiators, market position, and constraints. Keep JSON keys, technical terms (framework names, language names), and file paths in English.` | ||
| : ''; | ||
| const contextBlock = `\n\n---\n\n## CONTEXT (injected by runner)\n\n**Project Directory**: ${projectDir}\n**Project Index**: ${projectIndexFile}\n**Output Directory**: ${outputDir}\n**Output File**: ${discoveryFile}\n\nUse the paths above when reading input files and writing output.${languageInstruction}`; |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider extracting duplicated language instruction string.
The language instruction text is nearly identical between discovery and features phases. While the enum value lists differ slightly, the core pattern is duplicated. Consider extracting to a helper function for maintainability.
♻️ Suggested refactor
function buildLanguageInstruction(language: string | undefined, enumExamples: string): string {
if (!language || language === 'en') return '';
return `\n\n**LANGUAGE**: You MUST write ALL human-readable text content in ${language}. This includes: ${enumExamples}. Keep JSON keys, technical terms (framework names, language names), file paths, and enum values in English.`;
}
// Usage in discovery:
const languageInstruction = buildLanguageInstruction(
language,
'project descriptions, pain points, goals, vision statements, value propositions, success metrics, feature names, known gaps, differentiators, market position, and constraints'
);
// Usage in features:
const featuresLanguageInstruction = buildLanguageInstruction(
language,
'feature titles, descriptions, rationales, acceptance criteria, user stories, phase names, phase descriptions, milestone titles, milestone descriptions, vision statement, and audience descriptions'
);Also applies to: 265-268
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/main/ai/runners/roadmap.ts` around lines 129 - 132,
Duplicate multilingual instruction strings (languageInstruction used in the
discovery phase and the similar features-phase text) should be extracted into a
single helper function to avoid duplication and ease maintenance; add a function
(e.g., buildLanguageInstruction(language: string | undefined, enumExamples:
string): string) that returns '' for undefined/'en' and otherwise composes the
LANGUAGE block with the provided enumExamples, then replace the inline
constructions used to set languageInstruction and the features-phase equivalent
with calls to buildLanguageInstruction (pass the appropriate enumExamples for
discovery vs features) so callers like the discovery contextBlock and the
features context block reuse the single helper.
| className="text-[10px] px-1.5 py-0 text-orange-500 border-orange-500/30" | ||
| > | ||
| {feature.source?.provider === 'canny' ? 'Canny' : 'External'} | ||
| {feature.source?.provider === 'canny' ? 'Canny' : t('roadmap.externalSource')} |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Hardcoded brand name 'Canny' bypasses localization.
While 'Canny' is a proper noun (company name), the conditional logic shows inconsistent treatment—other external sources use t('roadmap.externalSource') but Canny gets a hardcoded string. Consider using a translation key like t('roadmap.providers.canny') for consistency, even if the value is the same across locales.
♻️ Suggested fix
- {feature.source?.provider === 'canny' ? 'Canny' : t('roadmap.externalSource')}
+ {t(`roadmap.providers.${feature.source?.provider}`, { defaultValue: t('roadmap.externalSource') })}Then add to locale files:
"roadmap": {
"providers": {
"canny": "Canny"
}
}As per coding guidelines: "All frontend user-facing text must use react-i18next translation keys."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/components/SortableFeatureCard.tsx` at line 227,
The hardcoded provider label in SortableFeatureCard.tsx bypasses i18n; replace
the conditional that currently renders {feature.source?.provider === 'canny' ?
'Canny' : t('roadmap.externalSource')} with a translation lookup (e.g., use
t('roadmap.providers.canny') when feature.source?.provider === 'canny') and
fallback to t('roadmap.externalSource') for others, and add the corresponding
"roadmap.providers.canny" key to the locale files so the provider name is
localized consistently.
| {(() => { | ||
| const reviewReasonKeyMap: Record<string, string> = { | ||
| completed: 'completed', | ||
| errors: 'hasErrors', | ||
| plan_review: 'approvePlan', | ||
| stopped: 'stopped', | ||
| }; | ||
| const reasonKey = reviewReasonKeyMap[task.reviewReason || ''] || 'qaIssues'; | ||
| return t(`tasks:reviewReason.${reasonKey}`); | ||
| })()} |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Extract reviewReasonKeyMap as a module-level constant.
The mapping object is recreated on every render inside the JSX. Moving it outside the component improves readability and avoids unnecessary allocations.
♻️ Proposed refactor
Add near the top of the file (after imports):
const REVIEW_REASON_KEY_MAP: Record<string, string> = {
completed: 'completed',
errors: 'hasErrors',
plan_review: 'approvePlan',
stopped: 'stopped',
};Then simplify the JSX:
- {(() => {
- const reviewReasonKeyMap: Record<string, string> = {
- completed: 'completed',
- errors: 'hasErrors',
- plan_review: 'approvePlan',
- stopped: 'stopped',
- };
- const reasonKey = reviewReasonKeyMap[task.reviewReason || ''] || 'qaIssues';
- return t(`tasks:reviewReason.${reasonKey}`);
- })()}
+ {t(`tasks:reviewReason.${REVIEW_REASON_KEY_MAP[task.reviewReason || ''] || 'qaIssues'}`)}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/renderer/components/task-detail/TaskDetailModal.tsx` around
lines 407 - 416, Extract the inline reviewReasonKeyMap inside TaskDetailModal to
a module-level constant named REVIEW_REASON_KEY_MAP (place it near the top of
the file after imports), keep the same key/value pairs (completed, errors ->
hasErrors, plan_review -> approvePlan, stopped -> stopped), and then update the
JSX where reviewReasonKeyMap is referenced to use REVIEW_REASON_KEY_MAP and
preserve the existing logic that resolves reasonKey with
REVIEW_REASON_KEY_MAP[task.reviewReason || ''] || 'qaIssues' before passing to
t(`tasks:reviewReason.${reasonKey}`).
| "deleteSelected": "Delete Selected", | ||
| "selectProject": "Select a project to view worktrees", | ||
| "filesChanged": "{{count}} files changed", | ||
| "commitsAhead": "{{count}} commits ahead", | ||
| "mergeTo": "Merge to {{branch}}", | ||
| "copyPath": "Copy Path", | ||
| "createdAt": "Created {{date}}", | ||
| "sourceBranch": "Source Branch", | ||
| "targetBranch": "Target Branch", | ||
| "changes": "Changes", | ||
| "changesSummary": "{{commits}} commits, {{files}} files", | ||
| "mergeSuccessful": "Merge Successful", | ||
| "mergeFailed": "Merge Failed", | ||
| "conflictingFiles": "Conflicting files:" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check if French dialogs.json has the new worktree keys
echo "=== Checking fr/dialogs.json for new worktree keys ==="
FR_FILE="apps/desktop/src/shared/i18n/locales/fr/dialogs.json"
if [ -f "$FR_FILE" ]; then
echo "French dialogs.json exists. Checking for keys..."
for key in "deleteSelected" "selectProject" "filesChanged" "commitsAhead" "mergeTo" "copyPath" "createdAt" "sourceBranch" "targetBranch" "changes" "changesSummary" "mergeSuccessful" "mergeFailed" "conflictingFiles"; do
if grep -q "\"$key\"" "$FR_FILE"; then
echo "✓ Found: $key"
else
echo "✗ Missing: $key"
fi
done
else
echo "French dialogs.json not found at expected path"
fd -t f "dialogs.json" apps/desktop/src/shared/i18n/locales
fiRepository: AndyMik90/Aperant
Length of output: 492
Add missing French translations for new worktree keys.
The following 13 keys were added to en/dialogs.json but are missing from fr/dialogs.json, breaking French localization:
- selectProject
- filesChanged
- commitsAhead
- mergeTo
- copyPath
- createdAt
- sourceBranch
- targetBranch
- changes
- changesSummary
- mergeSuccessful
- mergeFailed
- conflictingFiles
Add these keys with French translations to apps/desktop/src/shared/i18n/locales/fr/dialogs.json to comply with the requirement that all new UI text must be added to ALL language files.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/shared/i18n/locales/en/dialogs.json` around lines 66 - 79,
Add the 13 missing French translations for the new worktree UI keys to the
fr/dialogs.json file: add entries for "selectProject", "filesChanged",
"commitsAhead", "mergeTo", "copyPath", "createdAt", "sourceBranch",
"targetBranch", "changes", "changesSummary", "mergeSuccessful", "mergeFailed",
and "conflictingFiles" with appropriate French strings (e.g., "Select a project
to view worktrees" -> "Sélectionnez un projet pour afficher les worktrees",
"filesChanged" -> "{{count}} fichiers modifiés", "commitsAhead" -> "{{count}}
commits d'avance", "mergeTo" -> "Fusionner vers {{branch}}", "copyPath" ->
"Copier le chemin", "createdAt" -> "Créé le {{date}}", "sourceBranch" ->
"Branche source", "targetBranch" -> "Branche cible", "changes" ->
"Modifications", "changesSummary" -> "{{commits}} commits, {{files}} fichiers",
"mergeSuccessful" -> "Fusion réussie", "mergeFailed" -> "Échec de la fusion",
"conflictingFiles" -> "Fichiers en conflit :"), ensure keys match exactly the
English identifiers and the JSON stays valid (comma placement, escaping).
| "needsResume": "Needs Resume", | ||
| "subtasksProgress": "{{completed}}/{{total}} subtasks", | ||
| "subtaskProgress": "{{completed}}/{{total}} subtasks" |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Duplicate translation keys with identical values.
Both subtasksProgress (line 36) and subtaskProgress (line 37) have the same value. The context snippet from TaskHeader.tsx shows only subtaskProgress is used. This duplication exists in both en and fr files.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/shared/i18n/locales/en/tasks.json` around lines 35 - 37,
Remove the duplicate translation key by deleting the unused "subtasksProgress"
entry and keep only "subtaskProgress" (or vice-versa if you prefer) in both
locales so there's a single canonical key; also verify TaskHeader.tsx references
the retained key (subtaskProgress) and update the fr locale to mirror the same
change to keep translations in sync.
| "needsResume": "Reprise requise", | ||
| "subtaskProgress": "{{completed}}/{{total}} sous-tâches", | ||
| "subtasksProgress": "{{completed}}/{{total}} sous-tâches" |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Duplicate translation keys with identical values.
Both subtaskProgress and subtasksProgress have the same French translation. Based on the relevant code snippet from TaskHeader.tsx (line 72), only subtaskProgress is used. Consider removing the duplicate subtasksProgress key or clarifying if both are needed for different contexts.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/shared/i18n/locales/fr/tasks.json` around lines 35 - 37,
Duplicate translation keys subtaskProgress and subtasksProgress both map to the
same French string; TaskHeader.tsx only uses subtaskProgress, so remove the
unused key or confirm both are required. Locate the keys subtaskProgress and
subtasksProgress in the locales/fr/tasks.json and delete subtasksProgress (or
rename/merge if another component needs it), then run a quick search for
subtasksProgress usage across the codebase to ensure no other component depends
on it and update any references to use subtaskProgress if necessary.
| "subtasksProgress": "{{completed}}/{{total}} subtarefas", | ||
| "subtaskProgress": "{{completed}}/{{total}} subtarefas" | ||
| }, |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Potential duplicate keys: subtasksProgress vs subtaskProgress.
Both keys have the same Portuguese value "{{completed}}/{{total}} subtarefas". Typically, react-i18next uses a base key with _plural suffix for pluralization. Having both may indicate:
- A typo (one should be removed), or
- Different usage contexts that aren't clear from the naming.
Verify if both are intentional. If only one is needed, remove the duplicate to avoid confusion.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/desktop/src/shared/i18n/locales/pt-BR/tasks.json` around lines 36 - 38,
The two i18n keys "subtasksProgress" and "subtaskProgress" contain identical
Portuguese values and likely represent a duplicate or naming mistake; inspect
usages of subtasksProgress and subtaskProgress across the codebase (components
and react-i18next calls) to determine which one is actually referenced, then
either remove the unused key or consolidate them into a single key and update
callers accordingly; if pluralization is intended, follow react-i18next
conventions (use a single base key with plural/_plural or add count usage) and
rename keys to a clear, distinct name to reflect singular vs plural contexts.
Summary
Details
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Chores