Skip to content

Commit 92a3362

Browse files
committed
en-zh
1 parent c7ef8e8 commit 92a3362

File tree

7 files changed

+237
-78
lines changed

7 files changed

+237
-78
lines changed

src/components/features/automator/AutomatorView.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import '@xyflow/react/dist/style.css';
99

1010
import { Play, Square, Move } from 'lucide-react';
1111
import { useAutomatorStore } from '@/store/useAutomatorStore';
12+
import { useAppStore } from '@/store/useAppStore';
13+
import { getText } from '@/lib/i18n';
1214
import { ActionNode } from './nodes/ActionNode';
1315
import { StartNode, EndNode } from './nodes/SpecialNodes';
1416
import { ConditionNode } from './nodes/ConditionNode';
@@ -45,6 +47,8 @@ function DnDFlow() {
4547
stop, isRunning, currentStepIndex
4648
} = useAutomatorStore();
4749

50+
const { language } = useAppStore();
51+
4852
// 状态同步:重置高亮
4953
useEffect(() => {
5054
if (!isRunning) {
@@ -177,7 +181,7 @@ function DnDFlow() {
177181
// 2. 找到唯一的起点
178182
const startNode = nodes.find((n) => n.type === 'startNode');
179183
if (!startNode) {
180-
alert("请添加 Start Point 节点作为起始。");
184+
alert(getText('automator', 'needStartPoint', language));
181185
return;
182186
}
183187

@@ -247,7 +251,7 @@ function DnDFlow() {
247251
// 4. 找到 startNode 连接的第一个节点
248252
const firstEdge = edges.find((e) => e.source === startNode.id);
249253
if (!firstEdge) {
250-
alert("起点没有连接任何节点。");
254+
alert(getText('automator', 'noConnection', language));
251255
return;
252256
}
253257

@@ -287,7 +291,7 @@ function DnDFlow() {
287291
});
288292
} catch (error) {
289293
console.error('执行失败:', error);
290-
alert(`执行失败: ${error}`);
294+
alert(getText('automator', 'executionFailed', language, { error: String(error) }));
291295
}
292296
};
293297

@@ -307,12 +311,12 @@ function DnDFlow() {
307311
{/* 顶部栏 */}
308312
<div className="h-14 border-b border-border flex items-center px-4 justify-between bg-secondary/5 shrink-0 z-10">
309313
<div className="flex items-center gap-3">
310-
<h2 className="font-semibold text-foreground">Automator Designer</h2>
311-
<div className="px-2 py-0.5 rounded-full bg-secondary text-[10px] text-muted-foreground border border-border">Manual Mode</div>
314+
<h2 className="font-semibold text-foreground">{getText('automator', 'designerTitle', language)}</h2>
315+
<div className="px-2 py-0.5 rounded-full bg-secondary text-[10px] text-muted-foreground border border-border">{getText('automator', 'manualMode', language)}</div>
312316
</div>
313317
<div className="flex items-center gap-2">
314318
<button onClick={isRunning ? stop : handleRun} className={cn("flex items-center gap-2 px-4 py-2 rounded-md text-sm font-medium transition-all shadow-sm", isRunning ? "bg-destructive text-white" : "bg-primary text-primary-foreground")}>
315-
{isRunning ? <><Square size={14} fill="currentColor"/> Stop</> : <><Play size={14} fill="currentColor"/> Run</>}
319+
{isRunning ? <><Square size={14} fill="currentColor"/> {getText('automator', 'stopBtn', language)}</> : <><Play size={14} fill="currentColor"/> {getText('automator', 'runBtn', language)}</>}
316320
</button>
317321
</div>
318322
</div>
@@ -353,7 +357,7 @@ function DnDFlow() {
353357
<div className="absolute inset-0 flex flex-col items-center justify-center pointer-events-none opacity-40">
354358
<div className="border-2 border-dashed border-muted-foreground/30 rounded-xl p-8 flex flex-col items-center">
355359
<Move size={32} className="mb-2" />
356-
<p className="text-sm font-medium">Drag actions here</p>
360+
<p className="text-sm font-medium">{getText('automator', 'dragActionsHere', language)}</p>
357361
</div>
358362
</div>
359363
)}

src/components/features/automator/nodes/ActionNode.tsx

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { MousePointer2, Keyboard, Clock, Move, MousePointerClick, Type, Repeat,
44
import { cn } from '@/lib/utils';
55
import { AutomatorAction, MouseButton } from '@/types/automator';
66
import { invoke } from '@tauri-apps/api/core';
7+
import { useAppStore } from '@/store/useAppStore';
8+
import { getText } from '@/lib/i18n';
79

810
const ICONS: Record<AutomatorAction['type'], any> = {
911
'MoveTo': Move,
@@ -17,16 +19,17 @@ const ICONS: Record<AutomatorAction['type'], any> = {
1719
'Iterate': Repeat,
1820
};
1921

20-
const TITLES: Record<AutomatorAction['type'], string> = {
21-
'MoveTo': 'Move Mouse',
22-
'Click': 'Click',
23-
'DoubleClick': 'Double Click',
24-
'Type': 'Input Text',
25-
'KeyPress': 'Press Key',
26-
'Wait': 'Wait',
27-
'Scroll': 'Scroll',
28-
'CheckColor': 'Check Color',
29-
'Iterate': 'Loop Iterator',
22+
// Title key mapping for i18n
23+
const TITLE_KEYS: Record<AutomatorAction['type'], string> = {
24+
'MoveTo': 'moveTo',
25+
'Click': 'clickLabel',
26+
'DoubleClick': 'doubleClickLabel',
27+
'Type': 'type',
28+
'KeyPress': 'keyPress',
29+
'Wait': 'wait',
30+
'Scroll': 'scroll',
31+
'CheckColor': 'checkColorLabel',
32+
'Iterate': 'loopIteratorLabel',
3033
};
3134

3235
interface ActionNodeData {
@@ -44,8 +47,10 @@ export const ActionNode = memo((props: NodeProps) => {
4447
const payload = data.payload;
4548
const isExecuting = data.isExecuting;
4649

50+
const { language } = useAppStore();
51+
4752
const Icon = ICONS[actionType] || MousePointer2;
48-
const title = TITLES[actionType] || actionType;
53+
const title = getText('automator', TITLE_KEYS[actionType] || actionType, language);
4954

5055
// 取坐标状态(仅用于 MoveTo)
5156
const [isPickingCoords, setIsPickingCoords] = useState(false);
@@ -79,6 +84,8 @@ export const ActionNode = memo((props: NodeProps) => {
7984
}
8085
};
8186

87+
const t = (key: string, vars?: Record<string, string>) => getText('automator', key, language, vars);
88+
8289
return (
8390
<div className={cn(
8491
"min-w-[180px] bg-card border rounded-lg shadow-sm transition-all duration-300 text-xs",
@@ -127,15 +134,15 @@ export const ActionNode = memo((props: NodeProps) => {
127134
"bg-primary/10 text-primary hover:bg-primary/20",
128135
isPickingCoords && "bg-primary/5 animate-pulse"
129136
)}
130-
title={isPickingCoords ? "移动鼠标到目标位置..." : "点击取坐标 (3秒延迟)"}
137+
title={isPickingCoords ? t('pickingCoordsMessage') : t('pickCoordsTooltip')}
131138
>
132139
<Crosshair size={12} className={cn(isPickingCoords && "animate-spin")} />
133-
<span>{isPickingCoords ? "取坐标中..." : "取坐标"}</span>
140+
<span>{isPickingCoords ? t('pickingCoords') : t('pickCoords')}</span>
134141
</button>
135142
{/* 取坐标状态提示 */}
136143
{isPickingCoords && (
137144
<div className="bg-primary/10 border border-primary/30 rounded px-2 py-1 text-center">
138-
<span className="text-[9px] text-primary font-medium">3 秒后取坐标... 移动鼠标到目标位置</span>
145+
<span className="text-[9px] text-primary font-medium">{t('pickingCoordsMessage')}</span>
139146
</div>
140147
)}
141148
</div>
@@ -146,7 +153,7 @@ export const ActionNode = memo((props: NodeProps) => {
146153
<input
147154
type="text"
148155
className="w-full bg-background border border-border rounded px-2 py-1"
149-
placeholder="Text to type..."
156+
placeholder={t('textToType')}
150157
value={(payload as { text: string }).text}
151158
onChange={(e) => handleChange('text', e.target.value)}
152159
/>
@@ -159,9 +166,9 @@ export const ActionNode = memo((props: NodeProps) => {
159166
value={(payload as { button: MouseButton }).button}
160167
onChange={(e) => handleChange('button', e.target.value as MouseButton)}
161168
>
162-
<option value="Left">Left Button</option>
163-
<option value="Right">Right Button</option>
164-
<option value="Middle">Middle</option>
169+
<option value="Left">{t('leftButton')}</option>
170+
<option value="Right">{t('rightButton')}</option>
171+
<option value="Middle">{t('middleButton')}</option>
165172
</select>
166173
)}
167174

src/components/features/automator/nodes/ConditionNode.tsx

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { Handle, Position, NodeProps } from '@xyflow/react';
33
import { Pipette, Crosshair } from 'lucide-react';
44
import { cn } from '@/lib/utils';
55
import { invoke } from '@tauri-apps/api/core';
6+
import { useAppStore } from '@/store/useAppStore';
7+
import { getText } from '@/lib/i18n';
68

79
interface ConditionNodeData {
810
payload: { x: number; y: number; expectedHex: string; tolerance: number };
@@ -20,6 +22,10 @@ export const ConditionNode = memo((props: NodeProps) => {
2022
const isExecuting = data.isExecuting;
2123
const [isPicking, setIsPicking] = useState(false);
2224

25+
const { language } = useAppStore();
26+
27+
const t = (key: string, vars?: Record<string, string>) => getText('automator', key, language, vars);
28+
2329
const handleChange = (key: string, value: any) => {
2430
const newPayload = { ...payload, [key]: value };
2531
data.onChange(newPayload);
@@ -64,7 +70,7 @@ export const ConditionNode = memo((props: NodeProps) => {
6470
"bg-orange-500/10 text-orange-600 px-3 py-2 text-[10px] font-bold border-b border-orange-500/20 flex items-center gap-2 rounded-t-lg"
6571
)}>
6672
<Pipette size={12} />
67-
<span>COLOR CONDITION</span>
73+
<span>{t('colorCondition')}</span>
6874
{isExecuting && <div className="ml-auto w-2 h-2 bg-orange-500 rounded-full animate-ping" />}
6975
</div>
7076

@@ -86,7 +92,7 @@ export const ConditionNode = memo((props: NodeProps) => {
8692
"hover:bg-black/20 active:bg-black/30",
8793
isPicking && "bg-black/10 animate-pulse"
8894
)}
89-
title={isPicking ? "移动鼠标到目标位置..." : "点击取色 (3秒延迟)"}
95+
title={isPicking ? t('pickingColor') : t('pickCoordsTooltip')}
9096
>
9197
<Crosshair size={14} className={cn("text-white drop-shadow-md", isPicking && "animate-spin")} />
9298
</button>
@@ -123,7 +129,7 @@ export const ConditionNode = memo((props: NodeProps) => {
123129
/>
124130
</div>
125131
<div className="col-span-2">
126-
<label className="text-[9px] text-muted-foreground block mb-0.5">容差 (0-255)</label>
132+
<label className="text-[9px] text-muted-foreground block mb-0.5">{t('toleranceRange')}</label>
127133
<input
128134
type="number"
129135
className="w-full bg-background border border-border rounded px-1.5 py-1 text-center font-mono text-xs"
@@ -138,18 +144,18 @@ export const ConditionNode = memo((props: NodeProps) => {
138144
{/* 取色状态提示 */}
139145
{isPicking && (
140146
<div className="bg-orange-500/10 border border-orange-500/30 rounded px-2 py-1.5 text-center">
141-
<span className="text-[9px] text-orange-600 font-medium">3 秒后取色... 移动鼠标到目标位置</span>
147+
<span className="text-[9px] text-orange-600 font-medium">{t('pickingColor')}</span>
142148
</div>
143149
)}
144150

145151
{/* 分支标识 */}
146152
<div className="flex justify-between text-[9px] font-semibold pt-1">
147153
<div className="flex items-center gap-1 text-red-500">
148154
<div className="w-2 h-2 rounded-full bg-red-500" />
149-
<span>FALSE ←</span>
155+
<span>{t('exit')}</span>
150156
</div>
151157
<div className="flex items-center gap-1 text-green-500">
152-
<span>→ TRUE</span>
158+
<span>{t('loop')}</span>
153159
<div className="w-2 h-2 rounded-full bg-green-500" />
154160
</div>
155161
</div>

src/components/features/automator/nodes/IteratorNode.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { memo } from 'react';
22
import { Handle, Position, NodeProps } from '@xyflow/react';
33
import { Repeat } from 'lucide-react';
44
import { cn } from '@/lib/utils';
5+
import { useAppStore } from '@/store/useAppStore';
6+
import { getText } from '@/lib/i18n';
57

68
interface IteratorNodeData {
79
payload: { targetCount: number };
@@ -13,6 +15,10 @@ export const IteratorNode = memo((props: NodeProps) => {
1315
const data = props.data as unknown as IteratorNodeData;
1416
const { payload, onChange, isExecuting } = data;
1517

18+
const { language } = useAppStore();
19+
20+
const t = (key: string, vars?: Record<string, string>) => getText('automator', key, language, vars);
21+
1622
const handleChange = (val: string) => {
1723
onChange({ targetCount: Math.max(1, parseInt(val) || 1) });
1824
};
@@ -25,13 +31,13 @@ export const IteratorNode = memo((props: NodeProps) => {
2531
)}>
2632
<div className="bg-blue-500/10 text-blue-600 px-3 py-2 text-[10px] font-bold border-b border-blue-500/20 flex items-center gap-2 rounded-t-lg">
2733
<Repeat size={12} />
28-
<span>LOOP ITERATOR</span>
34+
<span>{t('loopIteratorNodeLabel')}</span>
2935
{isExecuting && <div className="ml-auto w-2 h-2 bg-blue-500 rounded-full animate-ping" />}
3036
</div>
3137

3238
<div className="p-3 space-y-2 nodrag">
3339
<div>
34-
<label className="text-[9px] text-muted-foreground block mb-1">重复次数 (Target Count)</label>
40+
<label className="text-[9px] text-muted-foreground block mb-1">{t('targetCount')}</label>
3541
<input
3642
type="number"
3743
className="w-full bg-background border border-border rounded px-2 py-1 text-center font-mono text-sm"
@@ -44,10 +50,10 @@ export const IteratorNode = memo((props: NodeProps) => {
4450
<div className="flex justify-between text-[9px] font-semibold pt-1">
4551
<div className="flex items-center gap-1 text-red-500">
4652
<div className="w-2 h-2 rounded-full bg-red-500" />
47-
<span>EXIT ←</span>
53+
<span>{t('exit')}</span>
4854
</div>
4955
<div className="flex items-center gap-1 text-green-500">
50-
<span>LOOP</span>
56+
<span>{t('loop')}</span>
5157
<div className="w-2 h-2 rounded-full bg-green-500" />
5258
</div>
5359
</div>
Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
import { memo } from 'react';
22
import { Handle, Position } from '@xyflow/react';
33
import { PlayCircle, StopCircle } from 'lucide-react';
4+
import { useAppStore } from '@/store/useAppStore';
5+
import { getText } from '@/lib/i18n';
46

5-
export const StartNode = memo(() => (
6-
<div className="px-5 py-2.5 shadow-lg rounded-full bg-emerald-500 border-2 border-emerald-600 text-white flex items-center gap-2 min-w-[120px] justify-center">
7-
<PlayCircle size={18} className="animate-pulse" />
8-
<span className="text-xs font-bold uppercase tracking-[0.1em]">Entry Point</span>
9-
{/* 只有输出桩,禁止输入 */}
10-
<Handle type="source" position={Position.Bottom} className="!bg-white !w-3 !h-3 border-2 border-emerald-600" />
11-
</div>
12-
));
7+
export const StartNode = memo(() => {
8+
const { language } = useAppStore();
9+
return (
10+
<div className="px-5 py-2.5 shadow-lg rounded-full bg-emerald-500 border-2 border-emerald-600 text-white flex items-center gap-2 min-w-[120px] justify-center">
11+
<PlayCircle size={18} className="animate-pulse" />
12+
<span className="text-xs font-bold uppercase tracking-[0.1em]">{getText('automator', 'startNode', language)}</span>
13+
{/* 只有输出桩,禁止输入 */}
14+
<Handle type="source" position={Position.Bottom} className="!bg-white !w-3 !h-3 border-2 border-emerald-600" />
15+
</div>
16+
);
17+
});
1318

14-
export const EndNode = memo(() => (
15-
<div className="px-5 py-2.5 shadow-lg rounded-full bg-rose-500 border-2 border-rose-600 text-white flex items-center gap-2 min-w-[120px] justify-center">
16-
<StopCircle size={18} />
17-
<span className="text-xs font-bold uppercase tracking-[0.1em]">Logic End</span>
18-
{/* 只有输入桩,禁止输出 */}
19-
<Handle type="target" position={Position.Top} className="!bg-white !w-3 !h-3 border-2 border-rose-600" />
20-
</div>
21-
));
19+
export const EndNode = memo(() => {
20+
const { language } = useAppStore();
21+
return (
22+
<div className="px-5 py-2.5 shadow-lg rounded-full bg-rose-500 border-2 border-rose-600 text-white flex items-center gap-2 min-w-[120px] justify-center">
23+
<StopCircle size={18} />
24+
<span className="text-xs font-bold uppercase tracking-[0.1em]">{getText('automator', 'endNode', language)}</span>
25+
{/* 只有输入桩,禁止输出 */}
26+
<Handle type="target" position={Position.Top} className="!bg-white !w-3 !h-3 border-2 border-rose-600" />
27+
</div>
28+
);
29+
});

0 commit comments

Comments
 (0)