diff --git a/.changeset/fn-6770-localize-workflow-task-pr.md b/.changeset/fn-6770-localize-workflow-task-pr.md new file mode 100644 index 0000000000..5a8a7d6413 --- /dev/null +++ b/.changeset/fn-6770-localize-workflow-task-pr.md @@ -0,0 +1,5 @@ +--- +"@runfusion/fusion": patch +--- + +Localized the dashboard workflow/task/setup/PR component cluster and removed the obsolete i18n lint deferrals for those files so the hardcoded-string guardrail scans them again. diff --git a/i18next.config.ts b/i18next.config.ts index adce4b12ab..031170db6c 100644 --- a/i18next.config.ts +++ b/i18next.config.ts @@ -6,23 +6,8 @@ import { const DEFERRED_I18N_LINT_FILES = [ - // FNXC:i18n-LintBaseline 2026-06-19-00:00: - // These exact files still carry pre-existing user-facing copy debt after FN-6749 restored the guardrail scope and token suppression. - // Keep the deferral file-scoped and remove entries as those follow-ups localize each cluster. // FNXC:i18n-LintBaseline 2026-06-20-00:00: - // FN-6771 localized the settings/sections cluster, so those files are no longer deferred and must stay covered by i18n lint. - "packages/dashboard/app/components/WorkflowSelector.tsx", - "packages/dashboard/app/components/WorkflowResultsTab.tsx", - "packages/dashboard/app/components/WorkflowNodeEditor.tsx", - "packages/dashboard/app/components/TaskDetailModal.tsx", - "packages/dashboard/app/components/TaskComments.tsx", - "packages/dashboard/app/components/TaskChatTab.tsx", - "packages/dashboard/app/components/SetupWizardModal.tsx", - "packages/dashboard/app/components/SettingsModal.tsx", - "packages/dashboard/app/components/PullRequestView.tsx", - "packages/dashboard/app/components/PrPanel.tsx", - "packages/dashboard/app/components/PrCreateModal.tsx", - "packages/dashboard/app/components/Board.tsx", + // FN-6770 and FN-6771 localized the remaining workflow/task/setup/PR and settings/sections clusters, so no dashboard component files remain deferred from hardcoded-string lint. ] as const; /** diff --git a/packages/dashboard/app/components/Board.tsx b/packages/dashboard/app/components/Board.tsx index 0fb9e53099..29fec0adb7 100644 --- a/packages/dashboard/app/components/Board.tsx +++ b/packages/dashboard/app/components/Board.tsx @@ -5,6 +5,7 @@ import { Column } from "./Column"; import "./Lane.css"; import type { ToastType } from "../hooks/useToast"; import { useState, useMemo, useEffect, useCallback, useRef } from "react"; +import { useTranslation } from "react-i18next"; import { Pencil, Plus } from "lucide-react"; import { fetchWorkflowSteps, fetchBoardWorkflows, promoteTask, type ModelInfo, type BoardWorkflowDefinition, type BoardWorkflowsPayload } from "../api"; import { useBlockerFanout } from "../hooks/useBlockerFanout"; @@ -124,6 +125,7 @@ function areWorkflowNameLookupsEqual(previous: ReadonlyMap, next } export function Board({ tasks, projectId, maxConcurrent, onMoveTask, onPauseTask, onOpenDetail, onOpenGroupModal, addToast, onQuickCreate, onNewTask, autoMerge, onToggleAutoMerge, globalPaused, onUpdateTask, onRetryTask, onArchiveTask, onUnarchiveTask, onDeleteTask, onArchiveAllDone, onLoadArchivedTasks, searchQuery = "", availableModels, onPlanningMode, onSubtaskBreakdown, onOpenDetailWithTab, favoriteProviders, favoriteModels, onToggleFavorite, onToggleModelFavorite, taskStuckTimeoutMs, onOpenMission, staleHighFanoutBlockerAgeThresholdMs, lastFetchTimeMs, prAuthAvailable, onOpenWorkflowEditor, onCreateWorkflow }: BoardProps) { + const { t } = useTranslation("app"); const [archivedCollapsed, setArchivedCollapsed] = useState(true); const archivedLoadedRef = useRef(false); const [workflowStepNameLookup, setWorkflowStepNameLookup] = useState>(EMPTY_WORKFLOW_STEP_NAME_LOOKUP); @@ -527,8 +529,8 @@ export function Board({ tasks, projectId, maxConcurrent, onMoveTask, onPauseTask type="button" className="btn btn-icon btn-sm board-workflow-edit-btn" onClick={() => onOpenWorkflowEditor(selectedWorkflow.id)} - title="Edit workflows" - aria-label="Edit workflows" + title={t("board.workflow.edit", "Edit workflows")} + aria-label={t("board.workflow.edit", "Edit workflows")} > @@ -538,8 +540,8 @@ export function Board({ tasks, projectId, maxConcurrent, onMoveTask, onPauseTask type="button" className="btn btn-icon btn-sm board-workflow-create-btn" onClick={onCreateWorkflow} - title="New workflow" - aria-label="New workflow" + title={t("board.workflow.new", "New workflow")} + aria-label={t("board.workflow.new", "New workflow")} > diff --git a/packages/dashboard/app/components/PrCreateModal.tsx b/packages/dashboard/app/components/PrCreateModal.tsx index 8397fa9075..5f7313d8bb 100644 --- a/packages/dashboard/app/components/PrCreateModal.tsx +++ b/packages/dashboard/app/components/PrCreateModal.tsx @@ -488,7 +488,7 @@ export function PrCreateModal({ <>

{t("pr.preflightChecks", "Pre-flight checks")}

- {preflightLoading ?
: null} + {preflightLoading ?
: null} {preflightError ?

{preflightError}

: null} {!preflightLoading && !preflightError ? ( <> @@ -511,8 +511,8 @@ export function PrCreateModal({ {!preflight?.branchOnRemote ? (
-

Push branch to remote

-

Fusion will push this task's branch to origin so the PR can be created.

+

{t("pr.pushBranch.title", "Push branch to remote")}

+

{t("pr.pushBranch.message", "Fusion will push this task's branch to origin so the PR can be created.")}

) : null} {preflight?.conflictsWithBase ? (
-

Resolve conflicts with AI

-

Fusion will use AI to resolve conflicts on this branch and push it.

+

{t("pr.resolveConflicts.title", "Resolve conflicts with AI")}

+

{t("pr.resolveConflicts.message", "Fusion will use AI to resolve conflicts on this branch and push it.")}

) : null} @@ -554,7 +554,7 @@ export function PrCreateModal({ {userEditedTitle && } - {metadataLoading ?
: null} + {metadataLoading ?
: null} {metadataError ?

{metadataError}

: null} { setTitle(event.target.value); setUserEditedTitle(true); }} />
@@ -567,7 +567,7 @@ export function PrCreateModal({ {userEditedBody && } - {metadataLoading ?
: null} + {metadataLoading ?
: null}