From 1f310a121e07350c8c8616fc676ed20d5c0ab055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 17 Apr 2026 01:13:19 -0400 Subject: [PATCH] i18n(automation): localize RuleEditor and WorkflowEditor form strings Adds ~40 automation.ruleEditor.* and automation.workflowEditor.* keys to en.ts + zh.ts (labels, placeholders, error messages, action labels). RuleFormModal and WorkflowFormModal now route every hardcoded English string through useT(), including validation errors and modal titles. Closes remaining Z-B i18n work after the prompt-builder locale dispatch (cf92e0c), dept-keywords typing (a78bdb9), and sidebar/ formatters/notification/skills i18n landed earlier. --- .../components/automation/RuleEditor.tsx | 34 +++++++-------- .../components/automation/WorkflowEditor.tsx | 28 +++++++------ src/renderer/i18n/en.ts | 41 +++++++++++++++++++ src/renderer/i18n/zh.ts | 41 +++++++++++++++++++ 4 files changed, 115 insertions(+), 29 deletions(-) diff --git a/src/renderer/components/automation/RuleEditor.tsx b/src/renderer/components/automation/RuleEditor.tsx index 343eabd7..9f94c8b5 100644 --- a/src/renderer/components/automation/RuleEditor.tsx +++ b/src/renderer/components/automation/RuleEditor.tsx @@ -3,40 +3,42 @@ import { Gear, Lightning } from "../ui/icons" import type { AutomationAction, AutomationRuleRecord, AutomationTrigger, DepartmentId } from "@shared/types" import { ActionButton, Modal, Select } from "../ui" import { triggerConfig, actionConfig, departmentOptions, safeJsonStringify } from "./automation-config" +import { useT } from "../../i18n" interface RuleFormState { name: string; description: string; trigger: AutomationTrigger; conditionsJson: string; action: AutomationAction; actionConfigJson: string; sourceDepartment: string; targetDepartment: string; priority: number } const defaultRuleForm: RuleFormState = { name: "", description: "", trigger: "task_status_changed", conditionsJson: "{}", action: "assign_task", actionConfigJson: "{}", sourceDepartment: "", targetDepartment: "", priority: 50 } function ruleToFormState(rule: AutomationRuleRecord): RuleFormState { return { name: rule.name, description: rule.description, trigger: rule.trigger, conditionsJson: safeJsonStringify(rule.conditionsJson), action: rule.action, actionConfigJson: safeJsonStringify(rule.actionConfigJson), sourceDepartment: rule.sourceDepartment ?? "", targetDepartment: rule.targetDepartment ?? "", priority: rule.priority } } export function RuleFormModal({ open, editingRule, companyId, onClose, onSaved }: { open: boolean; editingRule: AutomationRuleRecord | null; companyId: string; onClose: () => void; onSaved: () => void }) { + const t = useT() const isEditing = editingRule !== null; const [form, setForm] = useState(defaultRuleForm); const [saving, setSaving] = useState(false); const [error, setError] = useState(null) useEffect(() => { if (open) { setForm(editingRule ? ruleToFormState(editingRule) : defaultRuleForm); setError(null) } }, [open, editingRule]) const updateField = useCallback((key: K, value: RuleFormState[K]) => { setForm((prev) => ({ ...prev, [key]: value })) }, []) const handleSave = useCallback(async () => { - if (!form.name.trim()) { setError("Rule name is required"); return } - try { JSON.parse(form.conditionsJson) } catch { setError("Conditions must be valid JSON"); return } - try { JSON.parse(form.actionConfigJson) } catch { setError("Action config must be valid JSON"); return } + if (!form.name.trim()) { setError(t("automation.ruleEditor.errNameRequired")); return } + try { JSON.parse(form.conditionsJson) } catch { setError(t("automation.ruleEditor.errConditionsJson")); return } + try { JSON.parse(form.actionConfigJson) } catch { setError(t("automation.ruleEditor.errActionConfigJson")); return } setSaving(true); setError(null) - try { const result = await window.agentCompany.saveAutomationRule({ id: editingRule?.id ?? null, companyId, name: form.name.trim(), description: form.description.trim(), trigger: form.trigger, conditionsJson: form.conditionsJson, action: form.action, actionConfigJson: form.actionConfigJson, sourceDepartment: (form.sourceDepartment || null) as DepartmentId | null, targetDepartment: (form.targetDepartment || null) as DepartmentId | null, priority: form.priority, status: editingRule?.status ?? "active" }); if (!result.ok) { setError(result.error.message); return }; onSaved(); onClose() } catch (err) { setError(err instanceof Error ? err.message : "Failed to save rule") } finally { setSaving(false) } - }, [form, editingRule, companyId, onSaved, onClose]) + try { const result = await window.agentCompany.saveAutomationRule({ id: editingRule?.id ?? null, companyId, name: form.name.trim(), description: form.description.trim(), trigger: form.trigger, conditionsJson: form.conditionsJson, action: form.action, actionConfigJson: form.actionConfigJson, sourceDepartment: (form.sourceDepartment || null) as DepartmentId | null, targetDepartment: (form.targetDepartment || null) as DepartmentId | null, priority: form.priority, status: editingRule?.status ?? "active" }); if (!result.ok) { setError(result.error.message); return }; onSaved(); onClose() } catch (err) { setError(err instanceof Error ? err.message : t("automation.ruleEditor.errSaveFailed")) } finally { setSaving(false) } + }, [form, editingRule, companyId, onSaved, onClose, t]) const triggerOptions = Object.entries(triggerConfig).map(([key, c]) => ({ value: key, label: c.label })); const actionOptions = Object.entries(actionConfig).map(([key, c]) => ({ value: key, label: c.label })) const selectedTriggerInfo = triggerConfig[form.trigger]; const selectedActionInfo = actionConfig[form.action] return ( - +
{error ?
{error}
: null} -
updateField("name", e.target.value)} placeholder="e.g., Auto-assign blocked tasks" className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 w-full rounded-lg border border-border bg-card px-3.5 py-2.5 text-[14px] text-foreground placeholder:text-muted-foreground" />
-