fix(triage): VGAP-03..07 replace hardcoded strings with i18n in 5 components
BulkActionBar: action labels, selected count, processing text EmptyStates: search empty, not connected, configure token, open settings IssueListHeader: title, open count, analyze/auto-fix labels, tooltips, filters LabelManager: add label, filter, no match AssigneeManager: assign, search, no match All 28+ hardcoded strings now use t() with keys in en + fr common.json. Updated BulkActionBar test expectations to match i18n keys. Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
986ba6b6cf
commit
bebaefe1f2
@@ -1,4 +1,5 @@
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Plus, X, Check } from 'lucide-react';
|
||||
import { Button } from '../../ui/button';
|
||||
|
||||
@@ -17,6 +18,7 @@ export function AssigneeManager({
|
||||
onRemoveAssignee,
|
||||
disabled,
|
||||
}: AssigneeManagerProps) {
|
||||
const { t } = useTranslation('common');
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const [search, setSearch] = useState('');
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
@@ -85,7 +87,7 @@ export function AssigneeManager({
|
||||
aria-label="Assign"
|
||||
>
|
||||
<Plus className="h-3 w-3" />
|
||||
Assign
|
||||
{t('assignees.assign')}
|
||||
</Button>
|
||||
|
||||
{/* Dropdown */}
|
||||
@@ -95,7 +97,7 @@ export function AssigneeManager({
|
||||
type="text"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
placeholder="Search collaborators..."
|
||||
placeholder={t('assignees.search')}
|
||||
className="w-full px-2 py-1 text-xs border-b border-border bg-transparent focus:outline-none"
|
||||
aria-label="Search collaborators"
|
||||
/>
|
||||
@@ -121,7 +123,7 @@ export function AssigneeManager({
|
||||
})}
|
||||
{filteredCollaborators.length === 0 && (
|
||||
<div className="px-2 py-1.5 text-xs text-muted-foreground">
|
||||
No matching collaborators
|
||||
{t('assignees.noMatch')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -14,14 +14,14 @@ interface BulkActionBarProps {
|
||||
onDeselectAll?: () => void;
|
||||
}
|
||||
|
||||
const BULK_ACTIONS: Array<{ action: BulkActionType; label: string }> = [
|
||||
{ action: 'close', label: 'Close' },
|
||||
{ action: 'reopen', label: 'Reopen' },
|
||||
{ action: 'add-label', label: 'Add Label' },
|
||||
{ action: 'remove-label', label: 'Remove Label' },
|
||||
{ action: 'add-assignee', label: 'Assign' },
|
||||
{ action: 'remove-assignee', label: 'Unassign' },
|
||||
{ action: 'transition', label: 'Transition' },
|
||||
const BULK_ACTIONS: Array<{ action: BulkActionType; labelKey: string }> = [
|
||||
{ action: 'close', labelKey: 'bulk.actionClose' },
|
||||
{ action: 'reopen', labelKey: 'bulk.actionReopen' },
|
||||
{ action: 'add-label', labelKey: 'bulk.actionAddLabel' },
|
||||
{ action: 'remove-label', labelKey: 'bulk.actionRemoveLabel' },
|
||||
{ action: 'add-assignee', labelKey: 'bulk.actionAssign' },
|
||||
{ action: 'remove-assignee', labelKey: 'bulk.actionUnassign' },
|
||||
{ action: 'transition', labelKey: 'bulk.actionTransition' },
|
||||
];
|
||||
|
||||
export function BulkActionBar({
|
||||
@@ -60,7 +60,7 @@ export function BulkActionBar({
|
||||
className="flex items-center gap-2 rounded-md border border-border bg-muted/50 px-3 py-2"
|
||||
>
|
||||
<span className="text-sm font-medium text-foreground">
|
||||
{selectedCount} selected
|
||||
{t('bulk.selected', { count: selectedCount })}
|
||||
</span>
|
||||
|
||||
{onSelectAll && (
|
||||
@@ -106,7 +106,7 @@ export function BulkActionBar({
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-1.5 ml-2">
|
||||
{BULK_ACTIONS.map(({ action, label }) => (
|
||||
{BULK_ACTIONS.map(({ action, labelKey }) => (
|
||||
<button
|
||||
key={action}
|
||||
type="button"
|
||||
@@ -114,7 +114,7 @@ export function BulkActionBar({
|
||||
disabled={isOperating}
|
||||
onClick={() => setPendingAction(action)}
|
||||
>
|
||||
{label}
|
||||
{t(labelKey)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -159,7 +159,7 @@ export function BulkActionBar({
|
||||
|
||||
{isOperating && progress && (
|
||||
<span className="ml-auto text-xs text-muted-foreground">
|
||||
Processing {progress.processedItems}/{progress.totalItems}...
|
||||
{t('bulk.processing', { current: progress.processedItems, total: progress.totalItems })}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,36 +1,39 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Github, Settings2 } from 'lucide-react';
|
||||
import { Button } from '../../ui/button';
|
||||
import type { EmptyStateProps, NotConnectedStateProps } from '../types';
|
||||
|
||||
export function EmptyState({ searchQuery, icon: Icon = Github, message }: EmptyStateProps) {
|
||||
const { t } = useTranslation('common');
|
||||
return (
|
||||
<div className="flex-1 flex flex-col items-center justify-center p-8 text-center">
|
||||
<div className="w-12 h-12 rounded-full bg-muted/50 flex items-center justify-center mb-3">
|
||||
<Icon className="h-6 w-6 text-muted-foreground" />
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{searchQuery ? 'No issues match your search' : message}
|
||||
{searchQuery ? t('issues.emptySearch') : message}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function NotConnectedState({ error, onOpenSettings }: NotConnectedStateProps) {
|
||||
const { t } = useTranslation('common');
|
||||
return (
|
||||
<div className="flex-1 flex flex-col items-center justify-center p-8 text-center">
|
||||
<div className="w-16 h-16 rounded-full bg-muted/50 flex items-center justify-center mb-4">
|
||||
<Github className="h-8 w-8 text-muted-foreground" />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-foreground mb-2">
|
||||
GitHub Not Connected
|
||||
{t('issues.notConnected')}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground mb-4 max-w-md">
|
||||
{error || 'Configure your GitHub token and repository in project settings to sync issues.'}
|
||||
{error || t('issues.configureToken')}
|
||||
</p>
|
||||
{onOpenSettings && (
|
||||
<Button onClick={onOpenSettings} variant="outline">
|
||||
<Settings2 className="h-4 w-4 mr-2" />
|
||||
Open Settings
|
||||
{t('issues.openSettings')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+11
-11
@@ -54,7 +54,7 @@ export function IssueListHeader({
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-foreground">
|
||||
GitHub Issues
|
||||
{t('issues.title')}
|
||||
</h2>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{repoFullName}
|
||||
@@ -63,7 +63,7 @@ export function IssueListHeader({
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{openIssuesCount} open
|
||||
{t('issues.openCount', { count: openIssuesCount })}
|
||||
</Badge>
|
||||
{onToggleTriageMode && (
|
||||
<TooltipProvider>
|
||||
@@ -117,11 +117,11 @@ export function IssueListHeader({
|
||||
) : (
|
||||
<Layers className="h-4 w-4 mr-2" />
|
||||
)}
|
||||
Analyze & Group Issues
|
||||
{t('issues.analyzeGroup')}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="max-w-xs">
|
||||
<p>Analyze up to 200 open issues, group similar ones, and review proposed batches before creating tasks.</p>
|
||||
<p>{t('issues.analyzeGroupTooltip')}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
@@ -140,7 +140,7 @@ export function IssueListHeader({
|
||||
<Wand2 className="h-4 w-4 text-muted-foreground" />
|
||||
)}
|
||||
<Label htmlFor="auto-fix-toggle" className="text-sm cursor-pointer whitespace-nowrap">
|
||||
Auto-Fix New
|
||||
{t('issues.autoFixNew')}
|
||||
</Label>
|
||||
<Switch
|
||||
id="auto-fix-toggle"
|
||||
@@ -151,9 +151,9 @@ export function IssueListHeader({
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" className="max-w-xs">
|
||||
<p>Automatically fix new issues as they come in.</p>
|
||||
<p>{t('issues.autoFixTooltip')}</p>
|
||||
{autoFixRunning && autoFixProcessing !== undefined && autoFixProcessing > 0 && (
|
||||
<p className="mt-1 text-primary">Processing {autoFixProcessing} issue{autoFixProcessing > 1 ? 's' : ''}...</p>
|
||||
<p className="mt-1 text-primary">{t('issues.autoFixProcessing', { count: autoFixProcessing })}</p>
|
||||
)}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
@@ -167,7 +167,7 @@ export function IssueListHeader({
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="Search issues..."
|
||||
placeholder={t('issues.searchPlaceholder')}
|
||||
value={searchQuery}
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
className="pl-9"
|
||||
@@ -179,9 +179,9 @@ export function IssueListHeader({
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="open">Open</SelectItem>
|
||||
<SelectItem value="closed">Closed</SelectItem>
|
||||
<SelectItem value="all">All</SelectItem>
|
||||
<SelectItem value="open">{t('issues.filterOpen')}</SelectItem>
|
||||
<SelectItem value="closed">{t('issues.filterClosed')}</SelectItem>
|
||||
<SelectItem value="all">{t('issues.filterAll')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{onWorkflowFilterChange && (
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Plus, X, Check } from 'lucide-react';
|
||||
import { Button } from '../../ui/button';
|
||||
import { Badge } from '../../ui/badge';
|
||||
@@ -22,6 +23,7 @@ export function LabelManager({
|
||||
disabled,
|
||||
isLoading,
|
||||
}: LabelManagerProps) {
|
||||
const { t } = useTranslation('common');
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
const [search, setSearch] = useState('');
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
@@ -89,7 +91,7 @@ export function LabelManager({
|
||||
aria-label="Add label"
|
||||
>
|
||||
<Plus className="h-3 w-3" />
|
||||
Add Label
|
||||
{t('labels.add')}
|
||||
</Button>
|
||||
|
||||
{/* Dropdown */}
|
||||
@@ -99,7 +101,7 @@ export function LabelManager({
|
||||
type="text"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
placeholder="Filter labels..."
|
||||
placeholder={t('labels.filter')}
|
||||
className="w-full px-2 py-1 text-xs border-b border-border bg-transparent focus:outline-none"
|
||||
aria-label="Filter labels"
|
||||
/>
|
||||
@@ -129,7 +131,7 @@ export function LabelManager({
|
||||
})}
|
||||
{filteredLabels.length === 0 && (
|
||||
<div className="px-2 py-1.5 text-xs text-muted-foreground">
|
||||
No matching labels
|
||||
{t('labels.noMatch')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+6
-6
@@ -35,7 +35,7 @@ describe('BulkActionBar', () => {
|
||||
isOperating={false}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText('5 selected')).toBeDefined();
|
||||
expect(screen.getByText('bulk.selected')).toBeDefined();
|
||||
});
|
||||
|
||||
it('Close button shows confirmation, confirm fires onBulkAction', () => {
|
||||
@@ -48,7 +48,7 @@ describe('BulkActionBar', () => {
|
||||
/>,
|
||||
);
|
||||
// Click Close — should show confirm prompt, NOT fire immediately
|
||||
fireEvent.click(screen.getByText('Close'));
|
||||
fireEvent.click(screen.getByText('bulk.actionClose'));
|
||||
expect(onBulkAction).not.toHaveBeenCalled();
|
||||
expect(screen.getByText('bulk.confirmMessage')).toBeDefined();
|
||||
// Click Confirm — should fire onBulkAction
|
||||
@@ -65,13 +65,13 @@ describe('BulkActionBar', () => {
|
||||
isOperating={false}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(screen.getByText('Close'));
|
||||
fireEvent.click(screen.getByText('bulk.actionClose'));
|
||||
expect(screen.getByText('bulk.confirmMessage')).toBeDefined();
|
||||
// Click Cancel
|
||||
fireEvent.click(screen.getByText('bulk.cancel'));
|
||||
expect(onBulkAction).not.toHaveBeenCalled();
|
||||
// Action buttons should be back
|
||||
expect(screen.getByText('Close')).toBeDefined();
|
||||
expect(screen.getByText('bulk.actionClose')).toBeDefined();
|
||||
});
|
||||
|
||||
it('confirmation dialog has role=alert', () => {
|
||||
@@ -82,7 +82,7 @@ describe('BulkActionBar', () => {
|
||||
isOperating={false}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(screen.getByText('Close'));
|
||||
fireEvent.click(screen.getByText('bulk.actionClose'));
|
||||
expect(screen.getByRole('alert')).toBeDefined();
|
||||
});
|
||||
|
||||
@@ -142,7 +142,7 @@ describe('BulkActionBar', () => {
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText('Processing 2/5...')).toBeDefined();
|
||||
expect(screen.getByText('bulk.processing')).toBeDefined();
|
||||
});
|
||||
|
||||
it('renders Select All button when onSelectAll provided', () => {
|
||||
|
||||
@@ -441,7 +441,23 @@
|
||||
"loadingMore": "Loading more...",
|
||||
"scrollForMore": "Scroll for more",
|
||||
"allLoaded": "All issues loaded",
|
||||
"listLabel": "Issues"
|
||||
"listLabel": "Issues",
|
||||
"title": "GitHub Issues",
|
||||
"openCount": "{{count}} open",
|
||||
"searchPlaceholder": "Search issues...",
|
||||
"filterOpen": "Open",
|
||||
"filterClosed": "Closed",
|
||||
"filterAll": "All",
|
||||
"analyzeGroup": "Analyze & Group Issues",
|
||||
"analyzeGroupTooltip": "Analyze up to 200 open issues, group similar ones, and review proposed batches before creating tasks.",
|
||||
"autoFixNew": "Auto-Fix New",
|
||||
"autoFixTooltip": "Automatically fix new issues as they come in.",
|
||||
"autoFixProcessing": "Processing {{count}} issue(s)...",
|
||||
"emptySearch": "No issues match your search",
|
||||
"emptyList": "No issues found",
|
||||
"notConnected": "GitHub Not Connected",
|
||||
"configureToken": "Configure your GitHub token and repository in project settings to sync issues.",
|
||||
"openSettings": "Open Settings"
|
||||
},
|
||||
"enrichment": {
|
||||
"states": {
|
||||
@@ -509,13 +525,16 @@
|
||||
"add": "Add label",
|
||||
"remove": "Remove label",
|
||||
"search": "Search labels...",
|
||||
"filter": "Filter labels...",
|
||||
"noMatch": "No matching labels",
|
||||
"limit": "Maximum of {{limit}} labels reached"
|
||||
},
|
||||
"assignees": {
|
||||
"manage": "Manage assignees",
|
||||
"assign": "Assign",
|
||||
"unassign": "Unassign",
|
||||
"search": "Search collaborators..."
|
||||
"search": "Search collaborators...",
|
||||
"noMatch": "No matching collaborators"
|
||||
},
|
||||
"bulk": {
|
||||
"selectAll": "Select all",
|
||||
@@ -526,6 +545,13 @@
|
||||
"retryFailed": "Retry {{count}} failed",
|
||||
"dismiss": "Dismiss",
|
||||
"actions": "Bulk actions",
|
||||
"actionClose": "Close",
|
||||
"actionReopen": "Reopen",
|
||||
"actionAddLabel": "Add Label",
|
||||
"actionRemoveLabel": "Remove Label",
|
||||
"actionAssign": "Assign",
|
||||
"actionUnassign": "Unassign",
|
||||
"actionTransition": "Transition",
|
||||
"confirmMessage": "{{action}} {{count}} issues?",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel"
|
||||
|
||||
@@ -441,7 +441,23 @@
|
||||
"loadingMore": "Chargement...",
|
||||
"scrollForMore": "Défiler pour plus",
|
||||
"allLoaded": "Toutes les issues chargées",
|
||||
"listLabel": "Issues"
|
||||
"listLabel": "Issues",
|
||||
"title": "Issues GitHub",
|
||||
"openCount": "{{count}} ouvertes",
|
||||
"searchPlaceholder": "Rechercher des issues...",
|
||||
"filterOpen": "Ouvertes",
|
||||
"filterClosed": "Fermées",
|
||||
"filterAll": "Toutes",
|
||||
"analyzeGroup": "Analyser et regrouper",
|
||||
"analyzeGroupTooltip": "Analyser jusqu'à 200 issues ouvertes, regrouper les similaires et vérifier les lots proposés avant de créer des tâches.",
|
||||
"autoFixNew": "Auto-fix nouvelles",
|
||||
"autoFixTooltip": "Corriger automatiquement les nouvelles issues.",
|
||||
"autoFixProcessing": "Traitement de {{count}} issue(s)...",
|
||||
"emptySearch": "Aucune issue ne correspond à votre recherche",
|
||||
"emptyList": "Aucune issue trouvée",
|
||||
"notConnected": "GitHub non connecté",
|
||||
"configureToken": "Configurez votre token GitHub et le dépôt dans les paramètres du projet pour synchroniser les issues.",
|
||||
"openSettings": "Ouvrir les paramètres"
|
||||
},
|
||||
"enrichment": {
|
||||
"states": {
|
||||
@@ -509,13 +525,16 @@
|
||||
"add": "Ajouter un label",
|
||||
"remove": "Supprimer le label",
|
||||
"search": "Rechercher des labels...",
|
||||
"filter": "Filtrer les labels...",
|
||||
"noMatch": "Aucun label correspondant",
|
||||
"limit": "Maximum de {{limit}} labels atteint"
|
||||
},
|
||||
"assignees": {
|
||||
"manage": "Gérer les assignés",
|
||||
"assign": "Assigner",
|
||||
"unassign": "Désassigner",
|
||||
"search": "Rechercher des collaborateurs..."
|
||||
"search": "Rechercher des collaborateurs...",
|
||||
"noMatch": "Aucun collaborateur correspondant"
|
||||
},
|
||||
"bulk": {
|
||||
"selectAll": "Tout sélectionner",
|
||||
@@ -526,6 +545,13 @@
|
||||
"retryFailed": "Réessayer {{count}} échoués",
|
||||
"dismiss": "Fermer",
|
||||
"actions": "Actions groupées",
|
||||
"actionClose": "Fermer",
|
||||
"actionReopen": "Rouvrir",
|
||||
"actionAddLabel": "Ajouter un label",
|
||||
"actionRemoveLabel": "Supprimer un label",
|
||||
"actionAssign": "Assigner",
|
||||
"actionUnassign": "Désassigner",
|
||||
"actionTransition": "Transition",
|
||||
"confirmMessage": "{{action}} {{count}} issues ?",
|
||||
"confirm": "Confirmer",
|
||||
"cancel": "Annuler"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
**Branch:** `terminal/enhancement-issues-tab`
|
||||
**Created:** 2026-02-13
|
||||
**Total Gaps:** 46 confirmed (from 9-agent triple-verified audit)
|
||||
**Status:** 2 / 17 complete
|
||||
**Status:** 7 / 17 complete
|
||||
|
||||
---
|
||||
|
||||
@@ -61,74 +61,69 @@ Each gap has: ID, description, status, files to modify, verification source, tes
|
||||
## TIER 2 — i18n Hardcoded Strings
|
||||
|
||||
### VGAP-03: BulkActionBar.tsx hardcoded action labels (8 strings)
|
||||
- **Status:** `PENDING`
|
||||
- **Status:** `DONE`
|
||||
- **Priority:** SHOULD-FIX
|
||||
- **Scope:** Medium
|
||||
- **Verified by:** i18n agent + Verifier-2 (CONFIRMED)
|
||||
- **Doc ref:** CLAUDE.md > i18n required
|
||||
- **Files to modify:** `renderer/components/github-issues/components/BulkActionBar.tsx`, `shared/i18n/locales/en/common.json`, `shared/i18n/locales/fr/common.json`
|
||||
- **Problem:** `BULK_ACTIONS` constant (lines 17-25) has hardcoded labels: 'Close', 'Reopen', 'Add Label', 'Remove Label', 'Assign', 'Unassign', 'Transition'. Line 63 has hardcoded `{selectedCount} selected`. Line 162 has hardcoded `Processing X/Y...`
|
||||
- **Fix:** Add `useTranslation` hook, add i18n keys `bulk.actions.close`, `bulk.actions.reopen`, etc. Replace hardcoded text with `t()` calls. Add keys to both en + fr.
|
||||
- **Tests:** Verify translated keys render (existing tests may need i18n mock update)
|
||||
- **Test status:** —
|
||||
- **Files modified:** `renderer/components/github-issues/components/BulkActionBar.tsx`, `shared/i18n/locales/en/common.json`, `shared/i18n/locales/fr/common.json`
|
||||
- **Fix:** Changed `BULK_ACTIONS` constant to use `labelKey` instead of `label`, rendering via `t(labelKey)`. Replaced `{selectedCount} selected` with `t('bulk.selected', { count })`. Replaced `Processing X/Y...` with `t('bulk.processing', { current, total })`. Updated test expectations to match i18n keys.
|
||||
- **Tests:** 3860 pass, lint clean
|
||||
- **Test status:** `PASS`
|
||||
- **Depends on:** None
|
||||
- **Commit:** —
|
||||
- **Commit:** VGAP-03..07
|
||||
|
||||
### VGAP-04: EmptyStates.tsx hardcoded strings (4 strings)
|
||||
- **Status:** `PENDING`
|
||||
- **Status:** `DONE`
|
||||
- **Priority:** SHOULD-FIX
|
||||
- **Scope:** Small
|
||||
- **Verified by:** i18n agent + Verifier-2 (CONFIRMED)
|
||||
- **Doc ref:** CLAUDE.md > i18n required
|
||||
- **Files to modify:** `renderer/components/github-issues/components/EmptyStates.tsx`, `shared/i18n/locales/en/common.json`, `shared/i18n/locales/fr/common.json`
|
||||
- **Problem:** Line 12: 'No issues match your search'. Line 25: 'GitHub Not Connected'. Line 28: 'Configure your GitHub token...'. Line 33: 'Open Settings'.
|
||||
- **Fix:** Add `useTranslation` hook, add i18n keys `issues.emptySearch`, `issues.notConnected`, `issues.configureToken`, `issues.openSettings`. Replace with `t()`. Add to both en + fr.
|
||||
- **Tests:** Verify translated keys render
|
||||
- **Test status:** —
|
||||
- **Files modified:** `renderer/components/github-issues/components/EmptyStates.tsx`, `shared/i18n/locales/en/common.json`, `shared/i18n/locales/fr/common.json`
|
||||
- **Fix:** Added `useTranslation('common')` to both EmptyState and NotConnectedState. Replaced 4 hardcoded strings with `t('issues.emptySearch')`, `t('issues.notConnected')`, `t('issues.configureToken')`, `t('issues.openSettings')`.
|
||||
- **Tests:** 3860 pass, lint clean
|
||||
- **Test status:** `PASS`
|
||||
- **Depends on:** None
|
||||
- **Commit:** —
|
||||
- **Commit:** VGAP-03..07
|
||||
|
||||
### VGAP-05: IssueListHeader.tsx hardcoded strings (9+ strings)
|
||||
- **Status:** `PENDING`
|
||||
- **Status:** `DONE`
|
||||
- **Priority:** SHOULD-FIX
|
||||
- **Scope:** Medium
|
||||
- **Verified by:** i18n agent + Verifier-2 (CONFIRMED)
|
||||
- **Doc ref:** CLAUDE.md > i18n required
|
||||
- **Files to modify:** `renderer/components/github-issues/components/IssueListHeader.tsx`, `shared/i18n/locales/en/common.json`, `shared/i18n/locales/fr/common.json`
|
||||
- **Problem:** Line 57: 'GitHub Issues'. Line 66: '{N} open'. Line 120: 'Analyze & Group Issues'. Line 124: tooltip text. Line 143: 'Auto-Fix New'. Line 154: tooltip. Line 156: 'Processing...'. Line 170: 'Search issues...'. Lines 182-184: 'Open', 'Closed', 'All'.
|
||||
- **Fix:** Add i18n keys for all strings. Use `t()` with interpolation for counts. Add to both en + fr.
|
||||
- **Tests:** Verify translated keys render
|
||||
- **Test status:** —
|
||||
- **Files modified:** `renderer/components/github-issues/components/IssueListHeader.tsx`, `shared/i18n/locales/en/common.json`, `shared/i18n/locales/fr/common.json`
|
||||
- **Fix:** Replaced 10 hardcoded strings with `t()` calls: title, openCount (with interpolation), analyzeGroup, analyzeGroupTooltip, autoFixNew, autoFixTooltip, autoFixProcessing (with interpolation), searchPlaceholder, filterOpen/Closed/All.
|
||||
- **Tests:** 3860 pass, lint clean
|
||||
- **Test status:** `PASS`
|
||||
- **Depends on:** None
|
||||
- **Commit:** —
|
||||
- **Commit:** VGAP-03..07
|
||||
|
||||
### VGAP-06: LabelManager.tsx hardcoded strings (3 strings)
|
||||
- **Status:** `PENDING`
|
||||
- **Status:** `DONE`
|
||||
- **Priority:** SHOULD-FIX
|
||||
- **Scope:** Small
|
||||
- **Verified by:** i18n agent + Verifier-2 (CONFIRMED)
|
||||
- **Doc ref:** CLAUDE.md > i18n required
|
||||
- **Files to modify:** `renderer/components/github-issues/components/LabelManager.tsx`, `shared/i18n/locales/en/common.json`, `shared/i18n/locales/fr/common.json`
|
||||
- **Problem:** Line 92: 'Add Label'. Line 102: 'Filter labels...'. Line 132: 'No matching labels'. Keys exist in common.json (`labels.add`, etc.) but component doesn't import or use `useTranslation`.
|
||||
- **Fix:** Import `useTranslation`, replace hardcoded strings with `t('common:labels.add')`, `t('common:labels.filter')`, `t('common:labels.noMatch')`. Add any missing keys to both en + fr.
|
||||
- **Tests:** Verify translated keys render
|
||||
- **Test status:** —
|
||||
- **Files modified:** `renderer/components/github-issues/components/LabelManager.tsx`, `shared/i18n/locales/en/common.json`, `shared/i18n/locales/fr/common.json`
|
||||
- **Fix:** Added `useTranslation('common')`. Replaced `'Add Label'` → `t('labels.add')`, `'Filter labels...'` → `t('labels.filter')`, `'No matching labels'` → `t('labels.noMatch')`.
|
||||
- **Tests:** 3860 pass, lint clean
|
||||
- **Test status:** `PASS`
|
||||
- **Depends on:** None
|
||||
- **Commit:** —
|
||||
- **Commit:** VGAP-03..07
|
||||
|
||||
### VGAP-07: AssigneeManager.tsx hardcoded strings (3 strings)
|
||||
- **Status:** `PENDING`
|
||||
- **Status:** `DONE`
|
||||
- **Priority:** SHOULD-FIX
|
||||
- **Scope:** Small
|
||||
- **Verified by:** i18n agent + Verifier-2 (CONFIRMED)
|
||||
- **Doc ref:** CLAUDE.md > i18n required
|
||||
- **Files to modify:** `renderer/components/github-issues/components/AssigneeManager.tsx`, `shared/i18n/locales/en/common.json`, `shared/i18n/locales/fr/common.json`
|
||||
- **Problem:** Line 88: 'Assign'. Line 98: 'Search collaborators...'. Line 124: 'No matching collaborators'. Keys exist in common.json (`assignees.assign`, etc.) but component doesn't use `useTranslation`.
|
||||
- **Fix:** Import `useTranslation`, replace hardcoded strings with `t()` calls using existing keys. Add any missing keys to both en + fr.
|
||||
- **Tests:** Verify translated keys render
|
||||
- **Test status:** —
|
||||
- **Files modified:** `renderer/components/github-issues/components/AssigneeManager.tsx`, `shared/i18n/locales/en/common.json`, `shared/i18n/locales/fr/common.json`
|
||||
- **Fix:** Added `useTranslation('common')`. Replaced `'Assign'` → `t('assignees.assign')`, `'Search collaborators...'` → `t('assignees.search')`, `'No matching collaborators'` → `t('assignees.noMatch')`.
|
||||
- **Tests:** 3860 pass, lint clean
|
||||
- **Test status:** `PASS`
|
||||
- **Depends on:** None
|
||||
- **Commit:** —
|
||||
- **Commit:** VGAP-03..07
|
||||
|
||||
---
|
||||
|
||||
@@ -289,10 +284,10 @@ Each gap has: ID, description, status, files to modify, verification source, tes
|
||||
| Tier | Description | Total | Done | Remaining |
|
||||
|------|-------------|-------|------|-----------|
|
||||
| 1 | Critical Wiring | 2 | 2 | 0 |
|
||||
| 2 | i18n Hardcoded Strings | 5 | 0 | 5 |
|
||||
| 2 | i18n Hardcoded Strings | 5 | 5 | 0 |
|
||||
| 3 | Accessibility Keyboard | 2 | 0 | 2 |
|
||||
| 4 | IPC Consistency | 3 | 0 | 3 |
|
||||
| 5 | Phase 3 Audit Gaps | 5 | 0 | 5 |
|
||||
| **Total** | | **17** | **0** | **17** |
|
||||
| **Total** | | **17** | **7** | **10** |
|
||||
|
||||
Note: VGAP-03 through VGAP-07 contain 28+ individual hardcoded strings grouped by component file. The 17 gap count represents work units (one per component/file), not individual string count.
|
||||
|
||||
Reference in New Issue
Block a user