@aixnn/aixautots 0.0.0-stage → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +239 -2
  2. package/dist/ScriptStudio-DaCqOF5H.d.ts +43 -0
  3. package/dist/agent-runner-V60B0is1.d.ts +57 -0
  4. package/dist/automation-queue-8HqRJV_K.d.ts +72 -0
  5. package/dist/chunk-536WVCTB.js +5744 -0
  6. package/dist/chunk-5BMFMDQR.js +18 -0
  7. package/dist/chunk-64Q7BBIQ.js +1347 -0
  8. package/dist/chunk-6YUAACW6.js +866 -0
  9. package/dist/chunk-AIUOLJZA.js +412 -0
  10. package/dist/chunk-CVV7FOMQ.js +175 -0
  11. package/dist/chunk-EHF2WQUR.js +2042 -0
  12. package/dist/chunk-EVHTAXQG.js +273 -0
  13. package/dist/chunk-FW23FLAF.js +56 -0
  14. package/dist/chunk-GKEPMDXO.js +410 -0
  15. package/dist/chunk-H7MHGYF3.js +341 -0
  16. package/dist/chunk-MRQ7VJFJ.js +1074 -0
  17. package/dist/chunk-NWFYTFMI.js +65 -0
  18. package/dist/chunk-OEZFYKL6.js +1 -0
  19. package/dist/chunk-PCYERBNZ.js +1564 -0
  20. package/dist/chunk-PUPVZRKZ.js +668 -0
  21. package/dist/chunk-SUKFJ73W.js +217 -0
  22. package/dist/config-Bm1hmXhD.d.ts +1380 -0
  23. package/dist/coords-LPR_MPyp.d.ts +22 -0
  24. package/dist/core/index.d.ts +135 -0
  25. package/dist/core/index.js +5 -0
  26. package/dist/device-normalize-BaThgnH7.d.ts +43 -0
  27. package/dist/device-session-KZVGLPOI.js +2 -0
  28. package/dist/i18n/index.d.ts +30 -0
  29. package/dist/i18n/index.js +1 -0
  30. package/dist/mirror-session-C_1vFkRV.d.ts +108 -0
  31. package/dist/nav/index.d.ts +19 -0
  32. package/dist/nav/index.js +1 -0
  33. package/dist/react/index.d.ts +487 -0
  34. package/dist/react/index.js +7 -0
  35. package/dist/script-debugger-Cw5jVRoz.d.ts +114 -0
  36. package/dist/task-agent-runner-eD9-SCJz.d.ts +160 -0
  37. package/dist/types-C2WXVcam.d.ts +1005 -0
  38. package/dist/ui/billing.d.ts +48 -0
  39. package/dist/ui/billing.js +425 -0
  40. package/dist/ui/device-create.d.ts +55 -0
  41. package/dist/ui/device-create.js +9 -0
  42. package/dist/ui/device-mirror.d.ts +42 -0
  43. package/dist/ui/device-mirror.js +10 -0
  44. package/dist/ui/devices.d.ts +223 -0
  45. package/dist/ui/devices.js +15 -0
  46. package/dist/ui/editor.d.ts +64 -0
  47. package/dist/ui/editor.js +344 -0
  48. package/dist/ui/index.d.ts +104 -0
  49. package/dist/ui/index.js +676 -0
  50. package/dist/ui/inspector.d.ts +5 -0
  51. package/dist/ui/inspector.js +1250 -0
  52. package/dist/ui/market.d.ts +10 -0
  53. package/dist/ui/market.js +60 -0
  54. package/dist/ui/mirror.d.ts +52 -0
  55. package/dist/ui/mirror.js +3 -0
  56. package/dist/ui/primitives/index.d.ts +101 -0
  57. package/dist/ui/primitives/index.js +1 -0
  58. package/dist/ui/script-studio.d.ts +85 -0
  59. package/dist/ui/script-studio.js +10 -0
  60. package/dist/ui/scripts.d.ts +18 -0
  61. package/dist/ui/scripts.js +210 -0
  62. package/dist/ui/tasks.d.ts +10 -0
  63. package/dist/ui/tasks.js +1039 -0
  64. package/dist/use-script-run-Diay7ibY.d.ts +50 -0
  65. package/package.json +76 -4
  66. package/styles.css +97 -0
  67. package/tailwind-preset.cjs +84 -0
@@ -0,0 +1,1347 @@
1
+ import { createContext, useMemo, createElement, useContext } from 'react';
2
+
3
+ // src/ui/i18n/index.ts
4
+
5
+ // src/ui/i18n/dict.ts
6
+ var en = {
7
+ "common.refresh": "Refresh",
8
+ "common.create": "Create",
9
+ "common.cancel": "Cancel",
10
+ "common.confirm": "Confirm",
11
+ "common.delete": "Delete",
12
+ "common.edit": "Edit",
13
+ "common.save": "Save",
14
+ "common.close": "Close",
15
+ "common.search": "Search",
16
+ "common.loading": "Loading\u2026",
17
+ "common.empty": "Nothing here yet",
18
+ "common.online": "online",
19
+ "common.offline": "offline",
20
+ "common.saving": "Saving\u2026",
21
+ "common.run": "Run",
22
+ "common.back": "Back",
23
+ // ── Devices page (devices.tsx) ──
24
+ "devices.title": "Devices",
25
+ "devices.refreshing": "Refreshing...",
26
+ "devices.connectedCount": "{n} connected",
27
+ "devices.addDevice": "Add Device",
28
+ "devices.searchPlaceholder": "Search UDID or Name...",
29
+ "devices.allPlatforms": "All Platforms",
30
+ "devices.allNodes": "All Nodes",
31
+ "devices.unassignedNode": "Unassigned / \u2014",
32
+ "devices.allGroups": "All Groups",
33
+ "devices.sort.online": "Sort: Online first",
34
+ "devices.sort.name": "Sort: Name",
35
+ "devices.sort.created": "Sort: Created",
36
+ "devices.gridView": "Grid view",
37
+ "devices.listView": "List view",
38
+ "devices.recycleBin": "Recycle Bin",
39
+ "devices.backToList": "Back to devices",
40
+ "devices.selectAll": "Select All",
41
+ "devices.bulk.connect": "Connect",
42
+ "devices.bulk.disconnect": "Disconnect",
43
+ "devices.bulk.runScript": "Run Script",
44
+ "devices.bulk.installApps": "Install Apps",
45
+ "devices.bulk.power": "Power",
46
+ "devices.bulk.powerOn": "Power On",
47
+ "devices.bulk.powerOff": "Power Off",
48
+ "devices.bulk.reboot": "Reboot",
49
+ "devices.bulk.powerDone": "{n} device(s) done",
50
+ "devices.bulk.powerPartial": "{ok} succeeded, {failed} failed",
51
+ "devices.groupControl": "Group control ({n})",
52
+ "devices.stat.total": "Total {n}",
53
+ "devices.stat.online": "Online {n}",
54
+ "devices.stat.booting": "Booting {n}",
55
+ "devices.stat.offline": "Offline {n}",
56
+ "devices.stat.selected": "\xB7 {n} selected",
57
+ "devices.loadingDevices": "Loading devices...",
58
+ "devices.addPhysicalTitle": "Add New Physical Device",
59
+ "devices.addPhysicalDesc": "Connect via USB or WiFi Debugging",
60
+ "devices.ungrouped": "Ungrouped",
61
+ "devices.expandGroup": "Expand group",
62
+ "devices.collapseGroup": "Collapse group",
63
+ "devices.checkGroup": "Select group {group}",
64
+ "devices.checkGroupTitle": "Select whole group",
65
+ "devices.selectToPreview": "Select a device on the left to view its live screen",
66
+ "devices.offlineNoPreview": "Device offline, no preview",
67
+ "devices.maximize": "Maximize to dialog",
68
+ "devices.checkDevice": "Select device (group control)",
69
+ "devices.deviceDetail": "Device details",
70
+ "devices.enlargeMirror": "Enlarge mirror",
71
+ "devices.poweredOff": "Device powered off",
72
+ "devices.powerOn": "Power on",
73
+ "devices.poweringOn": "Powering on\u2026",
74
+ "devices.nodeOfflineCannotPower": "This device\u2019s node is offline; cannot power on. A device can only be restored while its node is online \u2014 please bring the node online first.",
75
+ "devices.ctx.editNote": "Edit note/group",
76
+ "devices.ctx.runScript": "Run script",
77
+ "devices.ctx.enlargeMirror": "Enlarge mirror",
78
+ "devices.ctx.detail": "Device details",
79
+ "devices.ctx.delete": "Delete device",
80
+ // pagination
81
+ "devices.page.summary": "{total} devices \xB7 {start}\u2013{end}",
82
+ "devices.page.first": "First page",
83
+ "devices.page.prev": "Previous page",
84
+ "devices.page.next": "Next page",
85
+ "devices.page.last": "Last page",
86
+ "devices.page.size": "{n} / page",
87
+ // ── DeviceCard ──
88
+ "card.openMirror": "Open live mirror",
89
+ "card.booting": "Booting \u2014 click to view boot progress",
90
+ "card.offline": "Device offline",
91
+ "card.bootingShort": "Booting\u2026",
92
+ "card.liveMirror": "Live mirror",
93
+ "card.sleeping": "Sleeping",
94
+ "card.restore": "Restore",
95
+ "card.waking": "Waking\u2026",
96
+ "card.wakeFailed": "Wake failed",
97
+ "card.poweredOff": "Powered off",
98
+ "card.refreshShot": "Refresh screenshot",
99
+ "card.editNote": "Edit note/group",
100
+ "card.deleteDevice": "Delete device",
101
+ "card.noNote": "No note/group",
102
+ "card.status.connected": "CONNECTED",
103
+ "card.status.connecting": "CONNECTING",
104
+ "card.status.disconnected": "DISCONNECTED",
105
+ // ── DeviceDetailDrawer ──
106
+ "drawer.deviceDetails": "Device Details",
107
+ "drawer.tab.overview": "Overview",
108
+ "drawer.tab.files": "Files",
109
+ "drawer.tab.terminal": "Terminal",
110
+ "drawer.tab.fingerprint": "Fingerprint",
111
+ "drawer.capability": "Capability",
112
+ "drawer.deviceId": "Device ID",
113
+ "drawer.status": "Status",
114
+ "drawer.osVersion": "OS version",
115
+ "drawer.model": "Model",
116
+ "drawer.manufacturer": "Manufacturer",
117
+ "drawer.rooted": "Rooted",
118
+ "drawer.tier": "Tier",
119
+ "drawer.transport": "Transport",
120
+ "drawer.kind": "Kind",
121
+ "drawer.kind.virtual": "virtual (redroid)",
122
+ "drawer.kind.real": "real",
123
+ "drawer.node": "Node",
124
+ "drawer.nodeOffline": "(offline)",
125
+ "drawer.yes": "yes",
126
+ "drawer.no": "no",
127
+ "drawer.videoSource": "Video Source (iOS)",
128
+ "drawer.videoSourceDesc": "Live-mirror video source. Control (tap/swipe) always goes via the host WDA, unaffected by this.",
129
+ "drawer.video.replaykit": "ReplayKit stream",
130
+ "drawer.video.usbadapter": "Host screenshot",
131
+ "drawer.shortcuts": "Shortcuts",
132
+ "drawer.controls": "Controls",
133
+ "drawer.location": "Simulated location",
134
+ "drawer.locationDesc": "Inject a mock GPS fix into the device.",
135
+ "drawer.locationAuto": "Follow IP",
136
+ "drawer.locationManual": "Pick on map",
137
+ "drawer.locationAutoHint": "The node resolves its current egress IP and applies the matching approximate coordinates.",
138
+ "drawer.locationApply": "Apply location",
139
+ "drawer.locationSaved": "Location applied",
140
+ "drawer.locationInvalid": "Please enter valid latitude/longitude.",
141
+ "drawer.locationStartSimulation": "Start gentle movement",
142
+ "drawer.locationStopSimulation": "Stop movement",
143
+ "drawer.locationSimulationActive": "moving",
144
+ "drawer.reboot": "Reboot",
145
+ "drawer.lease": "Lease",
146
+ "drawer.release": "Release",
147
+ "drawer.claim": "Claim",
148
+ "drawer.factoryReset": "Factory Reset",
149
+ "drawer.factoryResetConfirm": "Factory reset? This clears /data and rebuilds from the original image. Irreversible.",
150
+ "drawer.reinstallImage": "Reinstall (change image)",
151
+ "drawer.gpuMode": "GPU Mode",
152
+ "drawer.gpuPrompt": "GPU mode auto / host / guest\nhost=hardware GPU (node needs /dev/dri), guest=software render, auto=node decides.\nSwitching rebuilds the container on the same node (keeps /data, device data preserved).",
153
+ "drawer.gpuConfirm": "Switch to GPU={mode}? Rebuilds the container (keeps /data).",
154
+ "drawer.install": "Install",
155
+ "drawer.apkUrlPlaceholder": "APK URL to install\u2026",
156
+ "drawer.installApp": "Install app",
157
+ "drawer.searchInstall": "Search & install",
158
+ "drawer.fileUpload": "File upload",
159
+ "drawer.appSearchPlaceholder": "Search app name or package\u2026",
160
+ "drawer.search": "Search",
161
+ "drawer.noResults": "No results",
162
+ "drawer.chooseFile": "Choose file\u2026",
163
+ "drawer.proxy": "Proxy",
164
+ "drawer.proxyDesc": "TCP-only HTTP CONNECT: http://[user:percent-encoded-password@]host:port. Credentials are optional. UDP, QUIC, and DNS stay direct.",
165
+ "drawer.proxyPlaceholder": "http://[user:password@]host:8080 (empty to clear)",
166
+ "drawer.proxySaved": "Proxy saved; apply status updated below",
167
+ "drawer.proxyCleared": "Proxy cleared",
168
+ "drawer.proxyAndroidOnly": "Transparent proxy applies only to virtual Android Redroid devices; other platforms show the capability reason.",
169
+ "drawer.proxyConfigured": "Configured proxy (masked)",
170
+ "drawer.proxyInvalid": "Use canonical http://[user:password@]host:port; credentials are optional. socks/https are not supported.",
171
+ "drawer.proxyStatusLabel": "Apply status",
172
+ "drawer.proxyStatus.pending": "pending \u2014 waiting for the node",
173
+ "drawer.proxyStatus.applied": "applied",
174
+ "drawer.proxyStatus.failed": "failed",
175
+ "drawer.proxyStatus.unsupported": "unsupported",
176
+ "drawer.proxyProbe": "Egress IP connectivity",
177
+ "drawer.proxyProbeCheck": "Check",
178
+ "drawer.proxyProbeRetry": "Retry",
179
+ "drawer.proxyProbeLoading": "Checking proxy\u2026",
180
+ "drawer.proxyProbeNotChecked": "Not checked yet.",
181
+ "drawer.proxyProbeConnected": "Connected \xB7 IP {ip} \xB7 {latency} ms",
182
+ "drawer.proxyProbeFailed": "Connection failed: {error}",
183
+ "drawer.proxyProbeNoConnection": "no connection",
184
+ "drawer.proxyProbeUnsupported": "Proxy probing is not supported for this device.",
185
+ "drawer.proxyProbeError": "Probe failed: {error}",
186
+ "drawer.proxyPasswordRequired": "The saved proxy password is hidden. Enter the full proxy URL to change it.",
187
+ "drawer.autoSleep": "Auto Sleep",
188
+ "drawer.autoSleepDescVirtual": "Auto-sleep after the mirror is idle (VM: snapshot+stop, no billing). Default 30 min.",
189
+ "drawer.autoSleepDescReal": "Auto-sleep after the mirror is idle (real device: lock screen). Default 30 min.",
190
+ "drawer.sleep.never": "Never",
191
+ "drawer.sleep.1m": "1 minute",
192
+ "drawer.sleep.5m": "5 minutes",
193
+ "drawer.sleep.10m": "10 minutes",
194
+ "drawer.sleep.30m": "30 minutes",
195
+ "drawer.sleep.1h": "1 hour",
196
+ "drawer.sleepTimeoutLabel": "Sleep timeout",
197
+ "drawer.sleepSaved": "Sleep timeout saved",
198
+ "drawer.sleepNow": "Sleep now",
199
+ "drawer.wakeDevice": "Wake device",
200
+ "drawer.reinstallTitle": "Reinstall (change image)",
201
+ "drawer.reinstallDesc": "Pick a new system image; clears /data and rebuilds with the chosen image (same device id). Irreversible.",
202
+ "drawer.loadingImages": "Loading image catalog\u2026",
203
+ "drawer.noImages": "No images available",
204
+ "drawer.confirmReinstall": "Confirm reinstall",
205
+ "drawer.reinstallConfirmText": 'Reinstall with "{label} v{version}"? This clears /data, irreversible.',
206
+ "drawer.hardware": "Hardware",
207
+ "drawer.resolution": "Resolution",
208
+ "drawer.platform": "Platform",
209
+ "drawer.udid": "UDID",
210
+ "drawer.installedApps": "Installed Apps",
211
+ "drawer.noApps": "No apps found",
212
+ "drawer.installTemplate": "Template",
213
+ // GPU mode dialog
214
+ "drawer.gpuSelectLabel": "GPU mode",
215
+ "drawer.gpu.auto": "auto \u2014 node decides",
216
+ "drawer.gpu.host": "host \u2014 hardware GPU (node needs /dev/dri)",
217
+ "drawer.gpu.guest": "guest \u2014 software render",
218
+ "drawer.gpuApply": "Apply",
219
+ // ── App templates (TemplateInstallPanel + AppTemplateManager) ──
220
+ "tpl.empty": "No templates yet",
221
+ "tpl.appCount": "{n} apps",
222
+ "tpl.installAll": "Install all",
223
+ "tpl.dispatched": "Dispatched {n} app(s)",
224
+ "tpl.installFailed": "Install failed: {err}",
225
+ "tpl.manage": "Manage templates",
226
+ "tpl.managerTitle": "App Templates",
227
+ "tpl.appleIdEmail": "Apple ID email",
228
+ "tpl.password": "Password",
229
+ "tpl.twoFa": "2FA code",
230
+ "tpl.login": "Log in",
231
+ "tpl.loginSection": "Apple ID",
232
+ "tpl.newTemplate": "New template",
233
+ "tpl.name": "Template name",
234
+ "tpl.searchApp": "Search apps",
235
+ "tpl.searchResults": "Results",
236
+ "tpl.noResults": "No results",
237
+ "tpl.library": "Library",
238
+ "tpl.downloadAdd": "Download & add",
239
+ "tpl.add": "Add",
240
+ "tpl.added": "Added",
241
+ "tpl.selectedCount": "{n} selected",
242
+ "tpl.save": "Save template",
243
+ "tpl.existing": "Existing templates",
244
+ "tpl.none": "No templates yet.",
245
+ // ── DeleteDeviceDialog ──
246
+ "delete.title": "Delete Device",
247
+ "delete.desc": "This moves the device to the recycle bin (auto-purged after 3 days), where you can restore it; online virtual devices destroy their container immediately. Type the device name {name} to confirm.",
248
+ "delete.inputPlaceholder": 'Type "{name}" to confirm',
249
+ "delete.confirm": "Confirm delete",
250
+ "delete.deleting": "Deleting\u2026",
251
+ // ── DeviceEditDialog ──
252
+ "editDevice.title": "Edit \u2014 {name}",
253
+ "editDevice.desc": "Device note and group tags.",
254
+ "editDevice.note": "Note",
255
+ "editDevice.notePlaceholder": "e.g. Main test device / Client A only",
256
+ "editDevice.group": "Group",
257
+ // ── TagEditor ──
258
+ "tags.noGroup": "No group",
259
+ "tags.removeTag": "Remove tag {tag}",
260
+ "tags.addPlaceholder": "Add a tag, press Enter\u2026",
261
+ // ── RecycleBinPanel ──
262
+ "recycle.loading": "Loading recycle bin\u2026",
263
+ "recycle.empty": "Recycle bin is empty.",
264
+ "recycle.clear": "Empty recycle bin",
265
+ "recycle.clearConfirm": "Confirm empty all? This cannot be undone.",
266
+ "recycle.clearing": "Clearing\u2026",
267
+ "recycle.confirmClear": "Confirm clear",
268
+ "recycle.daysLeft": "{n} days until purge",
269
+ "recycle.pendingDestroy": "\xB7 awaiting node to destroy",
270
+ "recycle.restore": "Restore",
271
+ "recycle.confirmQ": "Confirm?",
272
+ "recycle.purge": "Purge",
273
+ "recycle.purging": "Deleting\u2026",
274
+ // ── ShortcutBar ──
275
+ "shortcutBar.edit": "Edit",
276
+ "shortcutBar.newCustom": "New custom shortcut",
277
+ "shortcutBar.more": "More",
278
+ "shortcutBar.running": "{name}\u2026",
279
+ "shortcutBar.ok": "{name}: {ok}/{total} OK",
280
+ "shortcutBar.okOutput": "{name}: {ok}/{total} OK \xB7 {output}",
281
+ "shortcutBar.failed": "{name} failed: {error}",
282
+ "shortcutBar.partial": "{name}: {ok}/{total} OK, {failed} failed \xB7 {error}",
283
+ "shortcutBar.timedOut": "{name}: still running ({ok}/{total} done so far)",
284
+ // ── ShortcutRunDialog ──
285
+ "shortcutRun.deviceCount": "{n} devices",
286
+ "shortcutRun.required": '"{label}" is required',
287
+ "shortcutRun.willRun": 'Will run "{name}" on {n} devices.',
288
+ "shortcutRun.selectPlaceholder": "Select\u2026",
289
+ // ── ShortcutEditDialog ──
290
+ "shortcutEdit.editTitle": "Edit Shortcut",
291
+ "shortcutEdit.newTitle": "New Shortcut",
292
+ "shortcutEdit.name": "Name",
293
+ "shortcutEdit.icon": "Icon (lucide name)",
294
+ "shortcutEdit.platform": "Platform",
295
+ "shortcutEdit.kind": "Kind",
296
+ "shortcutEdit.kind.script": "Script",
297
+ "shortcutEdit.kind.power": "Power",
298
+ "shortcutEdit.powerAction": "Power action",
299
+ "shortcutEdit.power.start": "Start",
300
+ "shortcutEdit.power.stop": "Stop",
301
+ "shortcutEdit.power.reboot": "Reboot",
302
+ "shortcutEdit.powerHint": "Calls the backend power API per device (not a script).",
303
+ "shortcutEdit.params": "Parameters",
304
+ "shortcutEdit.addParam": "\uFF0B Parameter",
305
+ "shortcutEdit.paramReadHint": "Read in the script with vars.<key>.",
306
+ "shortcutEdit.required": "Required",
307
+ "shortcutEdit.scriptLabel": "Script (device.* snippet)",
308
+ "shortcutEdit.confirmBefore": "Confirm before running",
309
+ "shortcutEdit.nameBodyRequired": "Name and script cannot be empty",
310
+ "shortcutEdit.saveFailed": "Save failed",
311
+ "shortcutEdit.deleteConfirm": 'Delete shortcut "{name}"?',
312
+ // ── RunScriptDialog ──
313
+ "runScript.title": "Run script ({n} devices)",
314
+ "runScript.desc": "Pick a script and parameters, dispatch to the selected devices in bulk.",
315
+ "runScript.script": "Script",
316
+ "runScript.selectScript": "Select a script\u2026",
317
+ "runScript.paramsLabel": "Parameters (override flow.variables)",
318
+ "runScript.mode": "Mode",
319
+ "runScript.mode.broadcast": "Broadcast (each device runs in parallel)",
320
+ "runScript.mode.mirror": "Mirror (leader-driven, others follow)",
321
+ "runScript.leader": "Leader",
322
+ "runScript.selectLeader": "Select leader device\u2026",
323
+ "runScript.saveAsCluster": "Save as cluster (optional)",
324
+ "runScript.clusterPlaceholder": "Cluster name, leave empty to skip",
325
+ "runScript.clusterSaveFailed": "Cluster save failed (run continues): {err}",
326
+ "runScript.starting": "Starting\u2026",
327
+ "runScript.cancelRun": "Cancel run",
328
+ "runScript.done": "Done",
329
+ "runScript.closeBackground": "Close (keeps running in background)",
330
+ // ── BulkInstallAppsDialog ──
331
+ "bulkInstall.title": "Install apps to {n} devices",
332
+ "bulkInstall.desc": "Pick an app template to install on the selected devices.",
333
+ "bulkInstall.template": "Template",
334
+ "bulkInstall.selectTemplate": "Select a template\u2026",
335
+ "bulkInstall.applying": "Applying\u2026",
336
+ "bulkInstall.apply": "Apply",
337
+ "bulkInstall.done": "{matched} matched, {skipped} skipped (platform mismatch)",
338
+ // ── ScriptParamForm ──
339
+ "scriptParam.empty": "This script defines no parameters.",
340
+ // ── SharedParamsForm ──
341
+ "sharedParams.empty": "This script has no shared parameters.",
342
+ "sharedParams.random": "Random value",
343
+ "sharedParams.randomPlaceholder": "One candidate per line; each target row picks one at random when run",
344
+ "sharedParams.select": "Select\u2026",
345
+ "sharedParams.on": "On",
346
+ "sharedParams.off": "Off",
347
+ // ── RefreshParamsButton ──
348
+ "refreshParams.button": "Refresh device params",
349
+ "refreshParams.desc": "Regenerate the device fingerprint (model/brand can be kept, IMEI/serial/MAC and other unique IDs are re-rolled).",
350
+ "refreshParams.restoreOption": "Also restore the device (factory reset: clear /data, rebuild from original image, so the new params take effect cleanly)",
351
+ "refreshParams.realHint": "Real device: saving pushes live via the LSPosed identity layer, no restore needed.",
352
+ "refreshParams.doRefreshRestore": "Refresh & restore",
353
+ "refreshParams.doRefresh": "Refresh params",
354
+ "refreshParams.doneRestore": "Params re-rolled and device restored (factory reset, rebuilding, takes effect shortly).",
355
+ "refreshParams.doneVirtual": 'Params re-rolled (takes effect after device reboot; check "Restore device" for immediate clean effect).',
356
+ "refreshParams.doneReal": "Params re-rolled (pushed to the real device via LSPosed).",
357
+ "refreshParams.failed": "Failed: {err}",
358
+ // ── FingerprintPanel ──
359
+ "fp.loading": "Loading fingerprint\u2026",
360
+ "fp.noProfile": 'This device has no fingerprint profile yet. Click "Refresh device params" to generate one.',
361
+ "fp.current": "Current Fingerprint",
362
+ "fp.model": "Model",
363
+ "fp.androidVersion": "Android version",
364
+ "fp.locale": "Region/Language",
365
+ "fp.carrier": "Carrier",
366
+ "fp.resolution": "Resolution",
367
+ "fp.imei": "IMEI",
368
+ "fp.serial": "Serial",
369
+ "fp.mac": "MAC",
370
+ "fp.androidId": "Android ID",
371
+ "fp.manualAdjust": "Manual Adjust",
372
+ "fp.modelField": "Device model (switching recomputes brand/version/CPU etc.)",
373
+ "fp.localeField": "Region/Language locale (links carrier/timezone/number)",
374
+ "fp.resolutionField": "Resolution",
375
+ "fp.selectResolution": "Select a resolution",
376
+ "fp.currentResolution": "Current {width}\xD7{height} @{dpi}dpi",
377
+ "fp.applyRebootVM": "VM: fingerprint changes take effect after a device reboot.",
378
+ "fp.rebootNow": "Reboot now",
379
+ "fp.applyRealDevice": "Real device: saving pushes to the device via the LSPosed identity layer (rewrites spoof.json + resetprop identity, force-restarts target apps for instant effect, no full reboot). Running non-target apps take effect on next launch.",
380
+ "fp.uniqueIdHint": 'Unique IDs (IMEI/serial/MAC etc.) are only re-rolled on "Generate", not editable.',
381
+ "fp.antiDetect": "Anti-detect (LSPosed)",
382
+ "fp.installing": "\u25CC Installing\u2026",
383
+ "fp.installed": "\u25CF Installed",
384
+ "fp.installFailed": "\u2715 Install failed",
385
+ "fp.notInstalled": "\u25CB Not installed",
386
+ "fp.installingDesc": "The node is installing (two reboots, ~3\u20135 min, keep the device online). Status auto-refreshes every 5s\u2026",
387
+ "fp.installDesc": "Install the LSPosed identity layer + spoof module on the real device (needs Magisk root). Two reboots, ~3\u20135 min. After install, fingerprint changes push live, no reinstall.",
388
+ "fp.lastInstallFailed": "Last install failed: {err}",
389
+ "fp.retryInstall": "Retry install",
390
+ "fp.enableOnReal": "Enable anti-detect on real device",
391
+ "fp.noRoot": "No root (Magisk) detected; cannot install.",
392
+ "fp.disguiseSwitch": "Disguise switch",
393
+ "fp.disguiseEnabled": "Enabled: the identity layer is disguising apps in scope.",
394
+ "fp.disguiseDisabled": "Disabled: apps in scope read the genuine identity (model/hardware and other resetprop items fully revert only after a device reboot).",
395
+ "fp.scopeLabel": "Disguise app scope (one package per line; empty = all third-party apps)",
396
+ "fp.scopeHint": "Apps in scope are force-restarted on identity change for instant effect; out-of-scope running apps take effect on next launch.",
397
+ "fp.saveScope": "Save scope & push",
398
+ "fp.advancedRaw": "Advanced: raw device_profile",
399
+ "fp.installStarted": "Install started: runs in the background on the node (two reboots, ~3\u20135 min, keep the device online). Status auto-refreshes.",
400
+ "fp.installStartFailed": "Install start failed: {err}",
401
+ "fp.disguiseToggled": "Disguise {state} ({tail})",
402
+ "fp.enabled": "enabled",
403
+ "fp.disabled": "disabled",
404
+ "fp.pushedRepush": "Re-pushed fingerprint to the device, apps in scope restarted to take effect.",
405
+ "fp.pushedReal": "Apps in scope restarted and read the genuine identity. Model/hardware and other resetprop items need a device reboot to fully revert.",
406
+ "fp.savedPushFailed": "Saved; push failed: {err}, will retry when the node comes online.",
407
+ "fp.nodeOffline": "node offline",
408
+ "fp.saved": "Saved",
409
+ "fp.savedScope": "Disguise scope updated",
410
+ "fp.rebootTriggered": "Reboot triggered",
411
+ "fp.pushedLive": "{label} (pushed to the real device, target apps restarted to take effect)",
412
+ "fp.savedPushFailedLabel": "{label} (saved; push failed: {err}, will retry when the node comes online)",
413
+ "fp.loadFailed": "Load failed: {err}",
414
+ "fp.failed": "Failed: {err}",
415
+ // ── FileManager ──
416
+ "fm.sessionFailed": "Failed to open device session: {err}",
417
+ "fm.openingSession": "Opening device session\u2026",
418
+ "fm.refresh": "Refresh",
419
+ "fm.mkdir": "New folder",
420
+ "fm.uploadHere": "Upload to current folder",
421
+ "fm.colName": "Name",
422
+ "fm.colSize": "Size",
423
+ "fm.colPerms": "Permissions",
424
+ "fm.colActions": "Actions",
425
+ "fm.emptyDir": "Empty folder",
426
+ "fm.loading": "Loading\u2026",
427
+ "fm.listFailed": "List failed: {err}",
428
+ "fm.download": "Download",
429
+ "fm.rename": "Rename",
430
+ "fm.delete": "Delete",
431
+ "fm.mkdirTitle": "New folder",
432
+ "fm.mkdirDesc": "Create under {cwd}",
433
+ "fm.dirNamePlaceholder": "Folder name",
434
+ "fm.renameTitle": "Rename",
435
+ "fm.newNamePlaceholder": "New name",
436
+ "fm.confirmDeleteTitle": "Confirm delete",
437
+ "fm.confirmDeleteDesc": 'Delete {kind} "{name}"? This cannot be undone.',
438
+ "fm.kindDir": "folder (with contents)",
439
+ "fm.kindFile": "file",
440
+ "fm.opMkdir": "New folder",
441
+ "fm.opRename": "Rename",
442
+ "fm.opDelete": "Delete",
443
+ "fm.opOk": "{label}: success",
444
+ "fm.opErr": "{label}: {err}",
445
+ "fm.uploadDoneRelay": "Upload complete (relay)",
446
+ "fm.uploadDoneDirect": "Upload complete (direct)",
447
+ "fm.uploadFailed": "Upload failed: {err}",
448
+ "fm.downloadDoneRelay": "Download complete (relay)",
449
+ "fm.downloadDoneDirect": "Download complete (direct)",
450
+ "fm.downloadFailed": "Download failed: {err}",
451
+ "fm.confirmOk": "OK",
452
+ // ── DeviceTerminal ──
453
+ "term.openingSession": "Opening device session\u2026",
454
+ "term.interactiveReady": "Interactive terminal ready.",
455
+ "term.pseudoMode": "Pseudo-terminal mode: type a command below and press Enter.",
456
+ "term.pseudoHint": "(SDK has no interactive shell() yet; once it lands this auto-upgrades to a real interactive terminal)",
457
+ "term.sessionFailed": "Failed to open session: {err}",
458
+ "term.error": "Terminal error: {err}",
459
+ "term.running": "Running\u2026",
460
+ "term.inputPlaceholder": "Type a command, Enter to run (\u2191\u2193 history)",
461
+ "term.execFailed": "Exec failed: {err}",
462
+ // ── DeviceSelector ──
463
+ "deviceSelector.placeholder": "Select a device to run\u2026",
464
+ // ── DeviceConnect ──
465
+ "connect.title": "Connect Device",
466
+ "connect.desc": "Add a new test device",
467
+ "connect.platform": "Platform",
468
+ "connect.iosAddr": "UDID / WDA URL",
469
+ "connect.androidAddr": "ADB Serial",
470
+ "connect.hostAddr": "Hostname",
471
+ "connect.iosPlaceholder": "e.g. http://localhost:8100",
472
+ "connect.androidPlaceholder": "e.g. emulator-5554",
473
+ "connect.connect": "Connect",
474
+ // ── GroupMirrorModal ──
475
+ "groupMirror.header": "Group control \xB7 {n} devices ({connected} connected) \u2014 click a tile to set master",
476
+ "groupMirror.truncated": "Exceeds the {max} soft limit, truncated",
477
+ "groupMirror.quality": "Quality (all devices)",
478
+ "groupMirror.profile.balanced": "Balanced",
479
+ "groupMirror.profile.lowLatency": "Low latency",
480
+ "groupMirror.profile.highQuality": "High quality",
481
+ "groupMirror.master": "Master",
482
+ "groupMirror.connectFailed": "Connection failed: {err}",
483
+ "groupMirror.connecting": "Establishing stream\u2026",
484
+ // ── NodeRegister ──
485
+ "nodeRegister.registeredTitle": "Node registered",
486
+ "nodeRegister.title": "Register a node",
487
+ "nodeRegister.desc": "Mint a node token, then run the agent on the host. Its devices appear here automatically.",
488
+ "nodeRegister.saveTokenWarn": "Save this token now \u2014 it is not shown again.",
489
+ "nodeRegister.runOnHost": "Run on the node host:",
490
+ "nodeRegister.done": "Done",
491
+ "nodeRegister.nodeName": "node name",
492
+ "nodeRegister.nodeNamePlaceholder": "my-laptop",
493
+ "nodeRegister.nodeNameRequired": "node name required",
494
+ "nodeRegister.register": "Register",
495
+ // ── Scripts page (scripts.tsx) ──
496
+ "scripts.count": "{n} scripts",
497
+ "scripts.newScript": "New Script",
498
+ "scripts.searchPlaceholder": "Search scripts by name or tag...",
499
+ "scripts.allPlatforms": "All Platforms",
500
+ "scripts.loading": "Loading scripts...",
501
+ "scripts.noMatch": "No scripts match your filters",
502
+ "scripts.createFromTemplate": "Create from template",
503
+ "scripts.createFromTemplateDesc": "Start faster with pre-configured script structures.",
504
+ "scripts.ready": "Ready",
505
+ "scripts.duplicate": "Duplicate",
506
+ // ── Inspector page (inspector.tsx) ──
507
+ "inspector.selectDevice": "Select a device",
508
+ "inspector.export": "Export",
509
+ // ── Tasks page (tasks.tsx) ──
510
+ "tasks.newTask": "New Task",
511
+ "tasks.script": "Script",
512
+ "tasks.selectScript": "Select a script\u2026",
513
+ "tasks.targetLabel": "{label} (targets, one per line \xB7 {n} total)",
514
+ "tasks.targetPlaceholder": "One target per line; paste multiple lines directly",
515
+ "tasks.sharedParams": "Shared parameters (shared by all target rows)",
516
+ "tasks.noTargetHint": "This script declares no target field; it will run once per device & mode below.",
517
+ "tasks.targetDevices": "Target devices ({n} selected)",
518
+ "tasks.noDevices": "No devices.",
519
+ "tasks.selectAll": "Select all",
520
+ "tasks.onlyOnline": "Online only ({n})",
521
+ "tasks.offlineWarn": "{n} offline device(s) selected: their tasks wait until the device comes online or expires.",
522
+ "tasks.dispatchMode": "Dispatch mode",
523
+ "tasks.enqueuing": "Enqueuing\u2026",
524
+ "tasks.enqueueRun": "Enqueue ({n} targets)",
525
+ "tasks.mode.roundRobin": "Round-robin (each row to one device)",
526
+ "tasks.mode.broadcast": "Broadcast (each row runs on every device)",
527
+ "tasks.tab.all": "All",
528
+ "tasks.tab.pending": "Pending",
529
+ "tasks.tab.running": "Running",
530
+ "tasks.tab.paused": "Paused",
531
+ "tasks.tab.succeeded": "Done",
532
+ "tasks.tab.failed": "Failed",
533
+ "tasks.tab.cancelled": "Cancelled",
534
+ "tasks.empty": "No tasks yet.",
535
+ "tasks.batchLabel": "Batch \xB7 {n} targets",
536
+ "tasks.progress": "{finished}/{total} done \xB7 {percent}%",
537
+ "tasks.expandTargets": "Expand targets",
538
+ "tasks.collapseTargets": "Collapse targets",
539
+ "tasks.resumeAll": "Resume all",
540
+ "tasks.pauseAll": "Pause all (pending targets only)",
541
+ "tasks.cancelAll": "Cancel all",
542
+ "tasks.loadingDetail": "Loading details\u2026",
543
+ "tasks.noDeviceDetail": "No device details.",
544
+ "tasks.expandDetail": "Expand details",
545
+ "tasks.collapseDetail": "Collapse details",
546
+ "tasks.source": "Source {source}",
547
+ "tasks.sourceTip.webhook": "Source: external webhook (PAT)",
548
+ "tasks.sourceTip.local": "Source: local / manual",
549
+ "tasks.resume": "Resume",
550
+ "tasks.pause": "Pause (pending targets only)",
551
+ "tasks.cancel": "Cancel",
552
+ "tasks.noTargetDetail": "No target details.",
553
+ "tasks.recentEvents": "Recent events",
554
+ "tasks.status.pending": "Pending",
555
+ "tasks.status.notified": "Dispatching",
556
+ "tasks.status.running": "Running",
557
+ "tasks.status.paused": "Paused",
558
+ "tasks.status.succeeded": "Done",
559
+ "tasks.status.failed": "Failed",
560
+ "tasks.status.cancelled": "Cancelled",
561
+ "tasks.status.expired": "Expired",
562
+ // ── Integration panel (tasks) ──
563
+ "integration.openForTask": "Integration snippet for this task",
564
+ "integration.open": "Integration",
565
+ "integration.title": "Integration / API access",
566
+ "integration.tokens": "Access tokens (PAT)",
567
+ "integration.tokens.empty": "No tokens yet.",
568
+ "integration.create": "New token",
569
+ "integration.creating": "Creating\u2026",
570
+ "integration.tokenName": "Token name",
571
+ "integration.tokenNamePlaceholder": "e.g. n8n workflow",
572
+ "integration.scope": "Project scope",
573
+ "integration.scope.all": "All my projects (send X-Project-Id per request)",
574
+ "integration.scope.pin": "Pin to one project",
575
+ "integration.projectId": "Project ID",
576
+ "integration.projectIdPlaceholder": "project UUID",
577
+ "integration.created.once": "Copy this token now \u2014 it is shown only once.",
578
+ "integration.copy": "Copy",
579
+ "integration.copied": "Copied",
580
+ "integration.revoke": "Revoke",
581
+ "integration.revokeConfirm": "Revoke this token? Calls using it will fail immediately.",
582
+ "integration.revoked": "revoked",
583
+ "integration.expires": "expires",
584
+ "integration.lastUsed": "last used",
585
+ "integration.never": "never",
586
+ "integration.selectToken": "Token for snippets",
587
+ "integration.selectTokenHint": "Pick a token to fill the snippets below. Plaintext is only available right after creation; otherwise the prefix placeholder is used.",
588
+ "integration.snippets": "Callable endpoints",
589
+ "integration.snippet.listScripts": "List scripts (with params)",
590
+ "integration.snippet.listDevices": "List devices",
591
+ "integration.snippet.execTask": "Execute task",
592
+ "integration.snippet.listTasks": "List / query tasks",
593
+ "integration.unpinnedHint": "This token is unpinned \u2014 replace <PROJECT_ID> in the X-Project-Id header.",
594
+ // ── Billing page (billing) ──
595
+ "billing.title": "Billing",
596
+ "billing.balance": "Balance",
597
+ "billing.currency": "Currency",
598
+ "billing.refresh": "Refresh",
599
+ "billing.loading": "Loading billing\u2026",
600
+ "billing.ledger": "Consumption ledger",
601
+ "billing.ledger.empty": "No ledger entries yet.",
602
+ "billing.col.kind": "Type",
603
+ "billing.col.amount": "Amount",
604
+ "billing.col.balanceAfter": "Balance after",
605
+ "billing.col.ref": "Ref",
606
+ "billing.col.time": "Time",
607
+ "billing.kind.topup": "Top-up",
608
+ "billing.kind.platform_session": "Device usage",
609
+ "billing.kind.subscription": "Subscription",
610
+ "billing.kind.own_node_daily": "Own-node daily",
611
+ "billing.kind.adjust": "Adjustment",
612
+ "billing.subs": "Subscriptions",
613
+ "billing.subs.empty": "No subscriptions.",
614
+ "billing.subs.tier": "Tier",
615
+ "billing.subs.status": "Status",
616
+ "billing.subs.remaining": "Remaining (h)",
617
+ "billing.subs.expires": "Expires",
618
+ "billing.subs.boundDevice": "Bound device",
619
+ "billing.ownNode": "Own-node daily charges",
620
+ "billing.ownNode.empty": "No own-node charges.",
621
+ "billing.ownNode.day": "Day",
622
+ "billing.ownNode.devices": "Devices",
623
+ "billing.ownNode.billable": "Billable",
624
+ "billing.ownNode.amount": "Amount",
625
+ // ── Recharge section (user-facing, inside BillingPage) ──
626
+ "recharge.title": "Top up",
627
+ "recharge.methods": "Payment methods",
628
+ "recharge.desc": "Choose a payment method to add credits to your wallet.",
629
+ "recharge.wechat": "WeChat Pay",
630
+ "recharge.alipay": "Alipay",
631
+ "recharge.contact": "Contact support",
632
+ "recharge.contactDesc": "Reach out to us for manual top-up or bulk credits.",
633
+ "recharge.comingSoon": "Payment coming soon",
634
+ // ── TopupDrawer ──
635
+ "topup.title": "Top up wallet",
636
+ "topup.target": "Target user",
637
+ "topup.amount": "Amount",
638
+ "topup.amountPlaceholder": "e.g. 100",
639
+ "topup.note": "Note (optional)",
640
+ "topup.notePlaceholder": "Reason / reference",
641
+ "topup.submit": "Top up",
642
+ "topup.submitting": "Topping up\u2026",
643
+ "topup.amountInvalid": "Enter an amount greater than 0",
644
+ "topup.done": "Done \xB7 new balance {balance}",
645
+ "topup.failed": "Top-up failed: {err}",
646
+ // ── Market page (market.tsx) ──
647
+ "market.empty": "No public scripts yet",
648
+ "market.editAria": "Edit {name}",
649
+ // ── Editor AgentPanel ──
650
+ "agent.unavailable": "The local agent (claude/codex) is only available in the desktop app.",
651
+ "agent.emptyHint": "Describe the script you want \u2014 the agent queries the live device via MCP and edits script.ts directly.",
652
+ "agent.needDevice": "Select a device in the top bar first; the agent needs to connect to it for live values.",
653
+ "agent.selectDeviceFirst": "Select a device first",
654
+ "agent.startFailed": "Failed to start agent: {err}",
655
+ "agent.noApps": "No apps to list.",
656
+ "agent.useInspector": "\u{1F50D} Pick element with Inspector",
657
+ "agent.loading": "Loading\u2026",
658
+ "agent.closeApps": "\u2715 Close app list",
659
+ "agent.pickApp": "\u{1F4F1} @app pick app",
660
+ "agent.placeholder": "Ask the agent / describe the script you want\u2026 (Enter to send, Shift+Enter for newline)",
661
+ "agent.stop": "Stop",
662
+ "agent.send": "Send"
663
+ };
664
+ var zh = {
665
+ "common.refresh": "\u5237\u65B0",
666
+ "common.create": "\u521B\u5EFA",
667
+ "common.cancel": "\u53D6\u6D88",
668
+ "common.confirm": "\u786E\u8BA4",
669
+ "common.delete": "\u5220\u9664",
670
+ "common.edit": "\u7F16\u8F91",
671
+ "common.save": "\u4FDD\u5B58",
672
+ "common.close": "\u5173\u95ED",
673
+ "common.search": "\u641C\u7D22",
674
+ "common.loading": "\u52A0\u8F7D\u4E2D\u2026",
675
+ "common.empty": "\u6682\u65E0\u5185\u5BB9",
676
+ "common.online": "\u5728\u7EBF",
677
+ "common.offline": "\u79BB\u7EBF",
678
+ "common.saving": "\u4FDD\u5B58\u4E2D\u2026",
679
+ "common.run": "\u8FD0\u884C",
680
+ "common.back": "\u4E0A\u4E00\u6B65",
681
+ // ── Devices page (devices.tsx) ──
682
+ "devices.title": "\u8BBE\u5907",
683
+ "devices.refreshing": "\u5237\u65B0\u4E2D...",
684
+ "devices.connectedCount": "{n} \u53F0\u5728\u7EBF",
685
+ "devices.addDevice": "\u6DFB\u52A0\u8BBE\u5907",
686
+ "devices.searchPlaceholder": "\u641C\u7D22 UDID \u6216\u540D\u79F0...",
687
+ "devices.allPlatforms": "\u5168\u90E8\u5E73\u53F0",
688
+ "devices.allNodes": "\u5168\u90E8\u8282\u70B9",
689
+ "devices.unassignedNode": "\u672A\u5206\u914D / \u2014",
690
+ "devices.allGroups": "\u5168\u90E8\u5206\u7EC4",
691
+ "devices.sort.online": "\u6392\u5E8F\uFF1A\u5728\u7EBF\u4F18\u5148",
692
+ "devices.sort.name": "\u6392\u5E8F\uFF1A\u540D\u79F0",
693
+ "devices.sort.created": "\u6392\u5E8F\uFF1A\u521B\u5EFA\u65F6\u95F4",
694
+ "devices.gridView": "\u7F51\u683C\u89C6\u56FE",
695
+ "devices.listView": "\u5217\u8868\u89C6\u56FE",
696
+ "devices.recycleBin": "\u56DE\u6536\u7AD9",
697
+ "devices.backToList": "\u8FD4\u56DE\u8BBE\u5907\u5217\u8868",
698
+ "devices.selectAll": "\u5168\u9009",
699
+ "devices.bulk.connect": "\u8FDE\u63A5",
700
+ "devices.bulk.disconnect": "\u65AD\u5F00",
701
+ "devices.bulk.runScript": "\u8FD0\u884C\u811A\u672C",
702
+ "devices.bulk.installApps": "\u88C5\u5E94\u7528",
703
+ "devices.bulk.power": "\u7535\u6E90",
704
+ "devices.bulk.powerOn": "\u5F00\u673A",
705
+ "devices.bulk.powerOff": "\u5173\u673A",
706
+ "devices.bulk.reboot": "\u91CD\u542F",
707
+ "devices.bulk.powerDone": "{n} \u53F0\u5DF2\u4E0B\u53D1",
708
+ "devices.bulk.powerPartial": "{ok} \u53F0\u6210\u529F\uFF0C{failed} \u53F0\u5931\u8D25",
709
+ "devices.groupControl": "\u7FA4\u63A7\uFF08{n}\uFF09",
710
+ "devices.stat.total": "\u5171 {n}",
711
+ "devices.stat.online": "\u5728\u7EBF {n}",
712
+ "devices.stat.booting": "\u542F\u52A8\u4E2D {n}",
713
+ "devices.stat.offline": "\u79BB\u7EBF {n}",
714
+ "devices.stat.selected": "\xB7 \u5DF2\u9009 {n}",
715
+ "devices.loadingDevices": "\u52A0\u8F7D\u8BBE\u5907\u4E2D...",
716
+ "devices.addPhysicalTitle": "\u6DFB\u52A0\u771F\u673A\u8BBE\u5907",
717
+ "devices.addPhysicalDesc": "\u901A\u8FC7 USB \u6216 WiFi \u8C03\u8BD5\u8FDE\u63A5",
718
+ "devices.ungrouped": "\u672A\u5206\u7EC4",
719
+ "devices.expandGroup": "\u5C55\u5F00\u5206\u7EC4",
720
+ "devices.collapseGroup": "\u6536\u8D77\u5206\u7EC4",
721
+ "devices.checkGroup": "\u52FE\u9009\u5206\u7EC4 {group}",
722
+ "devices.checkGroupTitle": "\u52FE\u9009\u6574\u7EC4",
723
+ "devices.selectToPreview": "\u9009\u62E9\u5DE6\u4FA7\u8BBE\u5907\u67E5\u770B\u5B9E\u65F6\u753B\u9762",
724
+ "devices.offlineNoPreview": "\u8BBE\u5907\u79BB\u7EBF\uFF0C\u65E0\u6CD5\u9884\u89C8",
725
+ "devices.maximize": "\u653E\u5927\u5230\u5F39\u7A97",
726
+ "devices.checkDevice": "\u52FE\u9009\u8BBE\u5907\uFF08\u7FA4\u63A7\uFF09",
727
+ "devices.deviceDetail": "\u8BBE\u5907\u8BE6\u60C5",
728
+ "devices.enlargeMirror": "\u653E\u5927\u955C\u50CF",
729
+ "devices.poweredOff": "\u8BBE\u5907\u5DF2\u5173\u673A",
730
+ "devices.powerOn": "\u5F00\u673A",
731
+ "devices.poweringOn": "\u5F00\u673A\u4E2D\u2026",
732
+ "devices.nodeOfflineCannotPower": "\u8BE5\u8BBE\u5907\u6240\u5C5E\u8282\u70B9\u5F53\u524D\u79BB\u7EBF\uFF0C\u65E0\u6CD5\u5F00\u673A\u3002\u8BBE\u5907\u53EA\u80FD\u5728\u5176\u8282\u70B9\u5728\u7EBF\u65F6\u6062\u590D\uFF0C\u8BF7\u5148\u8BA9\u8282\u70B9\u4E0A\u7EBF\u3002",
733
+ "devices.ctx.editNote": "\u7F16\u8F91\u5907\u6CE8/\u5206\u7EC4",
734
+ "devices.ctx.runScript": "\u8FD0\u884C\u811A\u672C",
735
+ "devices.ctx.enlargeMirror": "\u653E\u5927\u955C\u50CF",
736
+ "devices.ctx.detail": "\u8BBE\u5907\u8BE6\u60C5",
737
+ "devices.ctx.delete": "\u5220\u9664\u8BBE\u5907",
738
+ "devices.page.summary": "\u5171 {total} \u53F0 \xB7 \u7B2C {start}\u2013{end} \u53F0",
739
+ "devices.page.first": "\u9996\u9875",
740
+ "devices.page.prev": "\u4E0A\u4E00\u9875",
741
+ "devices.page.next": "\u4E0B\u4E00\u9875",
742
+ "devices.page.last": "\u672B\u9875",
743
+ "devices.page.size": "{n} / \u9875",
744
+ // ── DeviceCard ──
745
+ "card.openMirror": "\u6253\u5F00\u5B9E\u65F6\u955C\u50CF",
746
+ "card.booting": "\u8BBE\u5907\u542F\u52A8\u4E2D \u2014 \u70B9\u51FB\u67E5\u770B\u542F\u52A8\u8FDB\u5EA6",
747
+ "card.offline": "\u8BBE\u5907\u79BB\u7EBF",
748
+ "card.bootingShort": "\u542F\u52A8\u4E2D\u2026",
749
+ "card.liveMirror": "\u5B9E\u65F6\u955C\u50CF",
750
+ "card.sleeping": "\u4F11\u7720\u4E2D",
751
+ "card.restore": "\u6062\u590D",
752
+ "card.waking": "\u5524\u9192\u4E2D\u2026",
753
+ "card.wakeFailed": "\u5524\u9192\u5931\u8D25",
754
+ "card.poweredOff": "\u8BBE\u5907\u5DF2\u5173\u673A",
755
+ "card.refreshShot": "\u5237\u65B0\u622A\u56FE",
756
+ "card.editNote": "\u7F16\u8F91\u5907\u6CE8/\u5206\u7EC4",
757
+ "card.deleteDevice": "\u5220\u9664\u8BBE\u5907",
758
+ "card.noNote": "\u65E0\u5907\u6CE8/\u5206\u7EC4",
759
+ "card.status.connected": "CONNECTED",
760
+ "card.status.connecting": "CONNECTING",
761
+ "card.status.disconnected": "DISCONNECTED",
762
+ // ── DeviceDetailDrawer ──
763
+ "drawer.deviceDetails": "\u8BBE\u5907\u8BE6\u60C5",
764
+ "drawer.tab.overview": "\u6982\u89C8",
765
+ "drawer.tab.files": "\u6587\u4EF6",
766
+ "drawer.tab.terminal": "\u7EC8\u7AEF",
767
+ "drawer.tab.fingerprint": "\u6307\u7EB9",
768
+ "drawer.capability": "Capability",
769
+ "drawer.deviceId": "Device ID",
770
+ "drawer.status": "Status",
771
+ "drawer.osVersion": "OS version",
772
+ "drawer.model": "Model",
773
+ "drawer.manufacturer": "Manufacturer",
774
+ "drawer.rooted": "Rooted",
775
+ "drawer.tier": "Tier",
776
+ "drawer.transport": "Transport",
777
+ "drawer.kind": "Kind",
778
+ "drawer.kind.virtual": "virtual (redroid)",
779
+ "drawer.kind.real": "real",
780
+ "drawer.node": "Node",
781
+ "drawer.nodeOffline": "\uFF08\u79BB\u7EBF\uFF09",
782
+ "drawer.yes": "yes",
783
+ "drawer.no": "no",
784
+ "drawer.videoSource": "\u89C6\u9891\u6E90 (iOS)",
785
+ "drawer.videoSourceDesc": "\u5B9E\u65F6\u955C\u50CF\u7684\u753B\u9762\u6765\u6E90\u3002\u63A7\u5236\uFF08\u70B9\u51FB/\u6ED1\u52A8\uFF09\u59CB\u7EC8\u7ECF\u5BBF\u4E3B WDA\uFF0C\u4E0D\u53D7\u6B64\u5F71\u54CD\u3002",
786
+ "drawer.video.replaykit": "ReplayKit \u63A8\u6D41",
787
+ "drawer.video.usbadapter": "\u5BBF\u4E3B\u622A\u56FE",
788
+ "drawer.shortcuts": "Shortcuts",
789
+ "drawer.controls": "Controls",
790
+ "drawer.location": "\u6A21\u62DF\u5B9A\u4F4D",
791
+ "drawer.locationDesc": "\u5411\u8BBE\u5907\u6CE8\u5165\u6A21\u62DF GPS \u4F4D\u7F6E\u3002",
792
+ "drawer.locationAuto": "\u8DDF\u968F IP",
793
+ "drawer.locationManual": "\u5730\u56FE\u9009\u70B9",
794
+ "drawer.locationAutoHint": "\u8282\u70B9\u4F1A\u89E3\u6790\u5F53\u524D\u51FA\u53E3 IP\uFF0C\u5E76\u5E94\u7528\u5BF9\u5E94\u7684\u5927\u81F4\u7ECF\u7EAC\u5EA6\u3002",
795
+ "drawer.locationApply": "\u5E94\u7528\u5B9A\u4F4D",
796
+ "drawer.locationSaved": "\u5B9A\u4F4D\u5DF2\u5E94\u7528",
797
+ "drawer.locationInvalid": "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u7ECF\u7EAC\u5EA6\u3002",
798
+ "drawer.locationStartSimulation": "\u5F00\u59CB\u8F7B\u5FAE\u79FB\u52A8",
799
+ "drawer.locationStopSimulation": "\u505C\u6B62\u79FB\u52A8",
800
+ "drawer.locationSimulationActive": "\u79FB\u52A8\u4E2D",
801
+ "drawer.reboot": "Reboot",
802
+ "drawer.lease": "Lease",
803
+ "drawer.release": "Release",
804
+ "drawer.claim": "Claim",
805
+ "drawer.factoryReset": "\u6062\u590D\u51FA\u5382",
806
+ "drawer.factoryResetConfirm": "\u6062\u590D\u51FA\u5382\uFF1F\u5C06\u6E05\u7A7A /data\uFF0C\u4F7F\u7528\u539F\u955C\u50CF\u91CD\u5EFA\uFF0C\u4E0D\u53EF\u6062\u590D\u3002",
807
+ "drawer.reinstallImage": "\u6362\u955C\u50CF\u91CD\u88C5",
808
+ "drawer.gpuMode": "GPU \u6A21\u5F0F",
809
+ "drawer.gpuPrompt": "GPU \u6A21\u5F0F auto / host / guest\nhost=\u786C\u4EF6GPU(\u8282\u70B9\u9700/dev/dri),guest=\u8F6F\u4EF6\u6E32\u67D3,auto=\u8282\u70B9\u81EA\u52A8\u3002\n\u5207\u6362\u4F1A\u5728\u539F\u8282\u70B9\u91CD\u5EFA\u5BB9\u5668(\u4FDD\u7559 /data,\u8BBE\u5907\u6570\u636E\u4E0D\u4E22)\u3002",
810
+ "drawer.gpuConfirm": "\u5207\u5230 GPU={mode}\uFF1F\u5C06\u91CD\u5EFA\u5BB9\u5668(\u4FDD\u7559 /data)\u3002",
811
+ "drawer.install": "\u5B89\u88C5",
812
+ "drawer.apkUrlPlaceholder": "APK URL to install\u2026",
813
+ "drawer.installApp": "\u5B89\u88C5\u5E94\u7528",
814
+ "drawer.searchInstall": "\u641C\u7D22\u5B89\u88C5",
815
+ "drawer.fileUpload": "\u6587\u4EF6\u4E0A\u4F20",
816
+ "drawer.appSearchPlaceholder": "\u641C\u7D22\u5E94\u7528\u540D\u6216\u5305\u540D\u2026",
817
+ "drawer.search": "\u641C\u7D22",
818
+ "drawer.noResults": "\u65E0\u7ED3\u679C",
819
+ "drawer.chooseFile": "\u9009\u62E9\u6587\u4EF6\u2026",
820
+ "drawer.proxy": "\u4EE3\u7406 Proxy",
821
+ "drawer.proxyDesc": "\u4EC5 TCP \u7684 HTTP CONNECT\uFF1Ahttp://[\u7528\u6237:\u5BC6\u7801@]\u4E3B\u673A:\u7AEF\u53E3\u3002\u7528\u6237\u540D\u548C\u5BC6\u7801\u53EF\u4E0D\u586B\u3002UDP\u3001QUIC\u3001DNS \u4FDD\u6301\u76F4\u8FDE\u3002",
822
+ "drawer.proxyPlaceholder": "http://[\u7528\u6237\u540D:\u5BC6\u7801@]\u4E3B\u673A:8080\uFF08\u7559\u7A7A\u6E05\u9664\uFF09",
823
+ "drawer.proxySaved": "\u4EE3\u7406\u5DF2\u4FDD\u5B58\uFF1B\u4E0B\u65B9\u663E\u793A\u4E0B\u53D1\u72B6\u6001",
824
+ "drawer.proxyCleared": "\u4EE3\u7406\u5DF2\u6E05\u9664",
825
+ "drawer.proxyAndroidOnly": "\u900F\u660E\u4EE3\u7406\u4EC5\u652F\u6301\u865A\u62DF Android Redroid\uFF1B\u5176\u5B83\u5E73\u53F0\u4F1A\u663E\u793A\u80FD\u529B\u539F\u56E0\u3002",
826
+ "drawer.proxyConfigured": "\u5DF2\u914D\u7F6E\u4EE3\u7406\uFF08\u5DF2\u63A9\u7801\uFF09",
827
+ "drawer.proxyInvalid": "\u8BF7\u8F93\u5165\u6807\u51C6 http://[\u7528\u6237\u540D:\u5BC6\u7801@]\u4E3B\u673A:\u7AEF\u53E3\uFF1B\u7528\u6237\u540D\u548C\u5BC6\u7801\u53EF\u4E0D\u586B\uFF0C\u4E0D\u652F\u6301 socks/https\u3002",
828
+ "drawer.proxyStatusLabel": "\u4E0B\u53D1\u72B6\u6001",
829
+ "drawer.proxyStatus.pending": "\u7B49\u5F85\u8282\u70B9\u4E0B\u53D1",
830
+ "drawer.proxyStatus.applied": "\u5DF2\u751F\u6548",
831
+ "drawer.proxyStatus.failed": "\u5931\u8D25",
832
+ "drawer.proxyStatus.unsupported": "\u4E0D\u652F\u6301",
833
+ "drawer.proxyProbe": "\u51FA\u53E3 IP \u8FDE\u901A\u6027",
834
+ "drawer.proxyProbeCheck": "\u68C0\u6D4B",
835
+ "drawer.proxyProbeRetry": "\u91CD\u8BD5",
836
+ "drawer.proxyProbeLoading": "\u6B63\u5728\u68C0\u6D4B\u4EE3\u7406\u2026",
837
+ "drawer.proxyProbeNotChecked": "\u5C1A\u672A\u68C0\u6D4B\u3002",
838
+ "drawer.proxyProbeConnected": "\u5DF2\u8FDE\u63A5 \xB7 IP {ip} \xB7 {latency} ms",
839
+ "drawer.proxyProbeFailed": "\u8FDE\u63A5\u5931\u8D25\uFF1A{error}",
840
+ "drawer.proxyProbeNoConnection": "\u65E0\u6CD5\u8FDE\u63A5",
841
+ "drawer.proxyProbeUnsupported": "\u6B64\u8BBE\u5907\u4E0D\u652F\u6301\u4EE3\u7406\u68C0\u6D4B\u3002",
842
+ "drawer.proxyProbeError": "\u68C0\u6D4B\u5931\u8D25\uFF1A{error}",
843
+ "drawer.proxyPasswordRequired": "\u5DF2\u9690\u85CF\u4EE3\u7406\u5BC6\u7801\u3002\u82E5\u8981\u4FEE\u6539\uFF0C\u8BF7\u8F93\u5165\u5B8C\u6574\u4EE3\u7406 URL\u3002",
844
+ "drawer.autoSleep": "\u81EA\u52A8\u4F11\u7720",
845
+ "drawer.autoSleepDescVirtual": "\u955C\u50CF\u65E0\u64CD\u4F5C\u8D85\u65F6\u540E\u81EA\u52A8\u4F11\u7720\uFF08\u865A\u62DF\u673A\uFF1A\u5FEB\u7167+\u505C\u673A\uFF0C\u4E0D\u8BA1\u8D39\uFF09\u3002\u9ED8\u8BA4 30 \u5206\u949F\u3002",
846
+ "drawer.autoSleepDescReal": "\u955C\u50CF\u65E0\u64CD\u4F5C\u8D85\u65F6\u540E\u81EA\u52A8\u4F11\u7720\uFF08\u771F\u673A\uFF1A\u9501\u5C4F\uFF09\u3002\u9ED8\u8BA4 30 \u5206\u949F\u3002",
847
+ "drawer.sleep.never": "\u4ECE\u4E0D",
848
+ "drawer.sleep.1m": "1 \u5206\u949F",
849
+ "drawer.sleep.5m": "5 \u5206\u949F",
850
+ "drawer.sleep.10m": "10 \u5206\u949F",
851
+ "drawer.sleep.30m": "30 \u5206\u949F",
852
+ "drawer.sleep.1h": "1 \u5C0F\u65F6",
853
+ "drawer.sleepTimeoutLabel": "\u4F11\u7720\u8D85\u65F6",
854
+ "drawer.sleepSaved": "\u4F11\u7720\u65F6\u957F\u5DF2\u4FDD\u5B58",
855
+ "drawer.sleepNow": "\u7ACB\u5373\u4F11\u7720",
856
+ "drawer.wakeDevice": "\u5524\u9192\u8BBE\u5907",
857
+ "drawer.reinstallTitle": "\u6362\u955C\u50CF\u91CD\u88C5",
858
+ "drawer.reinstallDesc": "\u9009\u62E9\u65B0\u7CFB\u7EDF\u955C\u50CF\uFF1B\u5C06\u6E05\u7A7A /data \u5E76\u7528\u6240\u9009\u955C\u50CF\u91CD\u5EFA\uFF08\u540C\u4E00\u8BBE\u5907 id\uFF09\u3002\u4E0D\u53EF\u6062\u590D\u3002",
859
+ "drawer.loadingImages": "\u52A0\u8F7D\u955C\u50CF\u76EE\u5F55\u2026",
860
+ "drawer.noImages": "\u65E0\u53EF\u7528\u955C\u50CF",
861
+ "drawer.confirmReinstall": "\u786E\u8BA4\u91CD\u88C5",
862
+ "drawer.reinstallConfirmText": "\u7528\u300C{label} v{version}\u300D\u91CD\u88C5\uFF1F\u5C06\u6E05\u7A7A /data\uFF0C\u4E0D\u53EF\u6062\u590D\u3002",
863
+ "drawer.hardware": "Hardware",
864
+ "drawer.resolution": "Resolution",
865
+ "drawer.platform": "Platform",
866
+ "drawer.udid": "UDID",
867
+ "drawer.installedApps": "Installed Apps",
868
+ "drawer.noApps": "No apps found",
869
+ "drawer.installTemplate": "\u6A21\u7248",
870
+ // GPU 模式对话框
871
+ "drawer.gpuSelectLabel": "GPU \u6A21\u5F0F",
872
+ "drawer.gpu.auto": "auto \u2014 \u8282\u70B9\u81EA\u52A8\u51B3\u5B9A",
873
+ "drawer.gpu.host": "host \u2014 \u786C\u4EF6 GPU\uFF08\u8282\u70B9\u9700 /dev/dri\uFF09",
874
+ "drawer.gpu.guest": "guest \u2014 \u8F6F\u4EF6\u6E32\u67D3",
875
+ "drawer.gpuApply": "\u5E94\u7528",
876
+ // ── 应用模版(TemplateInstallPanel + AppTemplateManager) ──
877
+ "tpl.empty": "\u6682\u65E0\u6A21\u7248",
878
+ "tpl.appCount": "{n} \u4E2A\u5E94\u7528",
879
+ "tpl.installAll": "\u5168\u90E8\u5B89\u88C5",
880
+ "tpl.dispatched": "\u5DF2\u4E0B\u53D1 {n} \u4E2A\u5E94\u7528",
881
+ "tpl.installFailed": "\u5B89\u88C5\u5931\u8D25\uFF1A{err}",
882
+ "tpl.manage": "\u7BA1\u7406\u6A21\u7248",
883
+ "tpl.managerTitle": "\u5E94\u7528\u6A21\u7248",
884
+ "tpl.appleIdEmail": "Apple ID \u90AE\u7BB1",
885
+ "tpl.password": "\u5BC6\u7801",
886
+ "tpl.twoFa": "2FA \u9A8C\u8BC1\u7801",
887
+ "tpl.login": "\u767B\u5F55",
888
+ "tpl.loginSection": "Apple ID",
889
+ "tpl.newTemplate": "\u65B0\u5EFA\u6A21\u7248",
890
+ "tpl.name": "\u6A21\u7248\u540D\u79F0",
891
+ "tpl.searchApp": "\u641C\u7D22\u5E94\u7528",
892
+ "tpl.searchResults": "\u641C\u7D22\u7ED3\u679C",
893
+ "tpl.noResults": "\u65E0\u7ED3\u679C",
894
+ "tpl.library": "\u5E94\u7528\u5E93",
895
+ "tpl.downloadAdd": "\u4E0B\u8F7D\u5E76\u52A0\u5165",
896
+ "tpl.add": "\u52A0\u5165",
897
+ "tpl.added": "\u5DF2\u52A0\u5165",
898
+ "tpl.selectedCount": "\u5DF2\u9009 {n}",
899
+ "tpl.save": "\u4FDD\u5B58\u6A21\u7248",
900
+ "tpl.existing": "\u5DF2\u6709\u6A21\u7248",
901
+ "tpl.none": "\u6682\u65E0\u6A21\u7248\u3002",
902
+ // ── DeleteDeviceDialog ──
903
+ "delete.title": "\u5220\u9664\u8BBE\u5907",
904
+ "delete.desc": "\u5C06\u628A\u8BBE\u5907\u79FB\u5165\u56DE\u6536\u7AD9\uFF083 \u5929\u540E\u81EA\u52A8\u5F7B\u5E95\u6E05\u9664\uFF09\uFF0C\u5176\u95F4\u53EF\u5728\u56DE\u6536\u7AD9\u6062\u590D\uFF1B\u5728\u7EBF\u7684\u865A\u62DF\u8BBE\u5907\u4F1A\u7ACB\u5373\u9500\u6BC1\u5BB9\u5668\u3002\u8BF7\u8F93\u5165\u8BBE\u5907\u540D {name} \u4EE5\u786E\u8BA4\u3002",
905
+ "delete.inputPlaceholder": "\u8F93\u5165\u300C{name}\u300D\u786E\u8BA4",
906
+ "delete.confirm": "\u786E\u8BA4\u5220\u9664",
907
+ "delete.deleting": "\u5220\u9664\u4E2D\u2026",
908
+ // ── DeviceEditDialog ──
909
+ "editDevice.title": "\u7F16\u8F91 \u2014 {name}",
910
+ "editDevice.desc": "\u8BBE\u5907\u5907\u6CE8\u4E0E\u5206\u7EC4\u6807\u7B7E\u3002",
911
+ "editDevice.note": "\u5907\u6CE8",
912
+ "editDevice.notePlaceholder": "\u4F8B\u5982\uFF1A\u4E3B\u529B\u6D4B\u8BD5\u673A / \u5BA2\u6237A \u4E13\u7528",
913
+ "editDevice.group": "\u5206\u7EC4",
914
+ // ── TagEditor ──
915
+ "tags.noGroup": "\u65E0\u5206\u7EC4",
916
+ "tags.removeTag": "\u79FB\u9664\u6807\u7B7E {tag}",
917
+ "tags.addPlaceholder": "\u6DFB\u52A0\u6807\u7B7E\u540E\u6309\u56DE\u8F66\u2026",
918
+ // ── RecycleBinPanel ──
919
+ "recycle.loading": "\u52A0\u8F7D\u56DE\u6536\u7AD9\u2026",
920
+ "recycle.empty": "\u56DE\u6536\u7AD9\u4E3A\u7A7A\u3002",
921
+ "recycle.clear": "\u6E05\u7A7A\u56DE\u6536\u7AD9",
922
+ "recycle.clearConfirm": "\u786E\u8BA4\u6E05\u7A7A\u5168\u90E8\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\u3002",
923
+ "recycle.clearing": "\u6E05\u7A7A\u4E2D\u2026",
924
+ "recycle.confirmClear": "\u786E\u8BA4\u6E05\u7A7A",
925
+ "recycle.daysLeft": "{n} \u5929\u540E\u6E05\u9664",
926
+ "recycle.pendingDestroy": "\xB7 \u5F85\u8282\u70B9\u4E0A\u7EBF\u9500\u6BC1",
927
+ "recycle.restore": "\u6062\u590D",
928
+ "recycle.confirmQ": "\u786E\u8BA4\uFF1F",
929
+ "recycle.purge": "\u5F7B\u5E95\u5220\u9664",
930
+ "recycle.purging": "\u5220\u9664\u4E2D\u2026",
931
+ // ── ShortcutBar ──
932
+ "shortcutBar.edit": "\u7F16\u8F91",
933
+ "shortcutBar.newCustom": "\u65B0\u5EFA\u81EA\u5B9A\u4E49\u5FEB\u6377",
934
+ "shortcutBar.more": "\u66F4\u591A",
935
+ "shortcutBar.running": "{name}\u2026",
936
+ "shortcutBar.ok": "{name}\uFF1A{ok}/{total} \u6210\u529F",
937
+ "shortcutBar.okOutput": "{name}\uFF1A{ok}/{total} \u6210\u529F \xB7 {output}",
938
+ "shortcutBar.failed": "{name} \u5931\u8D25\uFF1A{error}",
939
+ "shortcutBar.partial": "{name}\uFF1A{ok}/{total} \u6210\u529F\uFF0C{failed} \u5931\u8D25 \xB7 {error}",
940
+ "shortcutBar.timedOut": "{name}\uFF1A\u4ECD\u5728\u6267\u884C\uFF08\u5DF2\u5B8C\u6210 {ok}/{total}\uFF09",
941
+ // ── ShortcutRunDialog ──
942
+ "shortcutRun.deviceCount": "{n} \u53F0\u8BBE\u5907",
943
+ "shortcutRun.required": "\u300C{label}\u300D\u5FC5\u586B",
944
+ "shortcutRun.willRun": "\u5C06\u5728 {n} \u53F0\u8BBE\u5907\u4E0A\u6267\u884C\u300C{name}\u300D\u3002",
945
+ "shortcutRun.selectPlaceholder": "\u9009\u62E9\u2026",
946
+ // ── ShortcutEditDialog ──
947
+ "shortcutEdit.editTitle": "\u7F16\u8F91\u5FEB\u6377",
948
+ "shortcutEdit.newTitle": "\u65B0\u5EFA\u5FEB\u6377",
949
+ "shortcutEdit.name": "\u540D\u79F0",
950
+ "shortcutEdit.icon": "\u56FE\u6807 (lucide \u540D)",
951
+ "shortcutEdit.platform": "\u5E73\u53F0",
952
+ "shortcutEdit.kind": "\u7C7B\u578B",
953
+ "shortcutEdit.kind.script": "\u811A\u672C",
954
+ "shortcutEdit.kind.power": "\u7535\u6E90",
955
+ "shortcutEdit.powerAction": "\u7535\u6E90\u52A8\u4F5C",
956
+ "shortcutEdit.power.start": "\u5F00\u673A (start)",
957
+ "shortcutEdit.power.stop": "\u5173\u673A (stop)",
958
+ "shortcutEdit.power.reboot": "\u91CD\u542F (reboot)",
959
+ "shortcutEdit.powerHint": "\u6309\u8BBE\u5907\u8C03\u540E\u7AEF\u7535\u6E90 API\uFF08\u975E\u811A\u672C\uFF09\u3002",
960
+ "shortcutEdit.params": "\u53C2\u6570",
961
+ "shortcutEdit.addParam": "\uFF0B \u53C2\u6570",
962
+ "shortcutEdit.paramReadHint": "\u811A\u672C\u91CC\u7528 vars.<key> \u8BFB\u53D6\u3002",
963
+ "shortcutEdit.required": "\u5FC5\u586B",
964
+ "shortcutEdit.scriptLabel": "\u811A\u672C (device.* \u7247\u6BB5)",
965
+ "shortcutEdit.confirmBefore": "\u6267\u884C\u524D\u786E\u8BA4",
966
+ "shortcutEdit.nameBodyRequired": "\u540D\u79F0\u548C\u811A\u672C\u4E0D\u80FD\u4E3A\u7A7A",
967
+ "shortcutEdit.saveFailed": "\u4FDD\u5B58\u5931\u8D25",
968
+ "shortcutEdit.deleteConfirm": "\u5220\u9664\u5FEB\u6377\u300C{name}\u300D\uFF1F",
969
+ // ── RunScriptDialog ──
970
+ "runScript.title": "\u8FD0\u884C\u811A\u672C\uFF08{n} \u53F0\u8BBE\u5907\uFF09",
971
+ "runScript.desc": "\u9009\u62E9\u811A\u672C\u4E0E\u53C2\u6570\uFF0C\u6279\u91CF\u4E0B\u53D1\u5230\u52FE\u9009\u7684\u8BBE\u5907\u3002",
972
+ "runScript.script": "\u811A\u672C",
973
+ "runScript.selectScript": "\u9009\u62E9\u811A\u672C\u2026",
974
+ "runScript.paramsLabel": "\u53C2\u6570\uFF08\u8986\u5199 flow.variables\uFF09",
975
+ "runScript.mode": "\u6A21\u5F0F",
976
+ "runScript.mode.broadcast": "Broadcast\uFF08\u5404\u8BBE\u5907\u5E76\u884C\u6267\u884C\uFF09",
977
+ "runScript.mode.mirror": "Mirror\uFF08leader \u9A71\u52A8\uFF0C\u5176\u4F59\u8DDF\u968F\uFF09",
978
+ "runScript.leader": "Leader",
979
+ "runScript.selectLeader": "\u9009\u62E9 leader \u8BBE\u5907\u2026",
980
+ "runScript.saveAsCluster": "\u53E6\u5B58\u4E3A\u96C6\u7FA4\uFF08\u53EF\u9009\uFF09",
981
+ "runScript.clusterPlaceholder": "\u96C6\u7FA4\u540D\u79F0\uFF0C\u7559\u7A7A\u5219\u4E0D\u4FDD\u5B58",
982
+ "runScript.clusterSaveFailed": "\u96C6\u7FA4\u4FDD\u5B58\u5931\u8D25\uFF08\u8FD0\u884C\u7EE7\u7EED\uFF09: {err}",
983
+ "runScript.starting": "\u542F\u52A8\u4E2D\u2026",
984
+ "runScript.cancelRun": "\u53D6\u6D88\u8FD0\u884C",
985
+ "runScript.done": "\u5B8C\u6210",
986
+ "runScript.closeBackground": "\u5173\u95ED\uFF08\u540E\u53F0\u7EE7\u7EED\u8FD0\u884C\uFF09",
987
+ // ── BulkInstallAppsDialog ──
988
+ "bulkInstall.title": "\u6279\u91CF\u88C5\u5E94\u7528\uFF08{n} \u53F0\uFF09",
989
+ "bulkInstall.desc": "\u9009\u62E9\u4E00\u4E2A\u5E94\u7528\u6A21\u7248\uFF0C\u5957\u7528\u5230\u52FE\u9009\u7684\u8BBE\u5907\u3002",
990
+ "bulkInstall.template": "\u6A21\u7248",
991
+ "bulkInstall.selectTemplate": "\u8BF7\u9009\u62E9\u6A21\u7248\u2026",
992
+ "bulkInstall.applying": "\u4E0B\u53D1\u4E2D\u2026",
993
+ "bulkInstall.apply": "\u5E94\u7528",
994
+ "bulkInstall.done": "\u5339\u914D {matched} \u53F0\uFF0C\u8DF3\u8FC7 {skipped} \u53F0\uFF08\u5E73\u53F0\u4E0D\u7B26\uFF09",
995
+ // ── ScriptParamForm ──
996
+ "scriptParam.empty": "\u8BE5\u811A\u672C\u672A\u5B9A\u4E49\u53C2\u6570\u3002",
997
+ // ── SharedParamsForm ──
998
+ "sharedParams.empty": "\u8BE5\u811A\u672C\u65E0\u5171\u7528\u53C2\u6570\u3002",
999
+ "sharedParams.random": "\u968F\u673A\u53D6\u503C",
1000
+ "sharedParams.randomPlaceholder": "\u6BCF\u884C\u4E00\u4E2A\u5019\u9009\u503C\uFF0C\u6267\u884C\u65F6\u6BCF\u4E2A\u76EE\u6807\u884C\u968F\u673A\u53D6\u4E00\u6761",
1001
+ "sharedParams.select": "\u9009\u62E9\u2026",
1002
+ "sharedParams.on": "\u5F00",
1003
+ "sharedParams.off": "\u5173",
1004
+ // ── RefreshParamsButton ──
1005
+ "refreshParams.button": "\u5237\u65B0\u8BBE\u5907\u53C2\u6570",
1006
+ "refreshParams.desc": "\u91CD\u65B0\u751F\u6210\u8BBE\u5907\u6307\u7EB9\u53C2\u6570\uFF08\u578B\u53F7/\u54C1\u724C\u53EF\u4FDD\u7559\uFF0CIMEI/\u5E8F\u5217\u53F7/MAC \u7B49\u552F\u4E00\u6807\u8BC6\u91CD\u6EDA\uFF09\u3002",
1007
+ "refreshParams.restoreOption": "\u540C\u65F6\u8FD8\u539F\u8BBE\u5907\uFF08\u6062\u590D\u51FA\u5382\uFF1A\u6E05 /data\u3001\u539F\u955C\u50CF\u91CD\u5EFA\uFF0C\u4F7F\u65B0\u53C2\u6570\u5E72\u51C0\u751F\u6548\uFF09",
1008
+ "refreshParams.realHint": "\u771F\u673A\uFF1A\u4FDD\u5B58\u5373\u7ECF LSPosed \u8EAB\u4EFD\u5C42\u5B9E\u65F6\u63A8\u9001\uFF0C\u65E0\u9700\u8FD8\u539F\u3002",
1009
+ "refreshParams.doRefreshRestore": "\u5237\u65B0\u5E76\u8FD8\u539F",
1010
+ "refreshParams.doRefresh": "\u5237\u65B0\u53C2\u6570",
1011
+ "refreshParams.doneRestore": "\u5DF2\u91CD\u6EDA\u53C2\u6570\u5E76\u8FD8\u539F\u8BBE\u5907\uFF08\u6062\u590D\u51FA\u5382\u3001\u91CD\u5EFA\u4E2D\uFF0C\u7A0D\u5019\u751F\u6548\uFF09\u3002",
1012
+ "refreshParams.doneVirtual": "\u5DF2\u91CD\u6EDA\u53C2\u6570\uFF08\u91CD\u542F\u8BBE\u5907\u540E\u751F\u6548\uFF1B\u5982\u9700\u7ACB\u5373\u5E72\u51C0\u751F\u6548\u53EF\u52FE\u300C\u8FD8\u539F\u8BBE\u5907\u300D\uFF09\u3002",
1013
+ "refreshParams.doneReal": "\u5DF2\u91CD\u6EDA\u53C2\u6570\uFF08\u5DF2\u7ECF LSPosed \u63A8\u9001\u5230\u771F\u673A\uFF09\u3002",
1014
+ "refreshParams.failed": "\u5931\u8D25\uFF1A{err}",
1015
+ // ── FingerprintPanel ──
1016
+ "fp.loading": "\u52A0\u8F7D\u6307\u7EB9\u2026",
1017
+ "fp.noProfile": "\u8BE5\u8BBE\u5907\u6682\u65E0\u6307\u7EB9\u6863\u6848\u3002\u70B9\u300C\u5237\u65B0\u8BBE\u5907\u53C2\u6570\u300D\u751F\u6210\u4E00\u4EFD\u3002",
1018
+ "fp.current": "\u5F53\u524D\u6307\u7EB9",
1019
+ "fp.model": "\u578B\u53F7",
1020
+ "fp.androidVersion": "\u5B89\u5353\u7248\u672C",
1021
+ "fp.locale": "\u5730\u533A/\u8BED\u8A00",
1022
+ "fp.carrier": "\u8FD0\u8425\u5546",
1023
+ "fp.resolution": "\u5206\u8FA8\u7387",
1024
+ "fp.imei": "IMEI",
1025
+ "fp.serial": "\u5E8F\u5217\u53F7",
1026
+ "fp.mac": "MAC",
1027
+ "fp.androidId": "Android ID",
1028
+ "fp.manualAdjust": "\u624B\u52A8\u8C03\u6574",
1029
+ "fp.modelField": "\u8BBE\u5907\u578B\u53F7 model (\u5207\u6362\u5C06\u91CD\u7B97\u54C1\u724C/\u7248\u672C/CPU \u7B49)",
1030
+ "fp.localeField": "\u5730\u533A/\u8BED\u8A00 locale (\u8054\u52A8\u8FD0\u8425\u5546/\u65F6\u533A/\u53F7\u7801)",
1031
+ "fp.resolutionField": "\u5206\u8FA8\u7387 resolution",
1032
+ "fp.selectResolution": "\u8BF7\u9009\u62E9\u5206\u8FA8\u7387",
1033
+ "fp.currentResolution": "\u5F53\u524D {width}\xD7{height} @{dpi}dpi",
1034
+ "fp.applyRebootVM": "\u865A\u62DF\u673A\uFF1A\u6307\u7EB9\u6539\u52A8\u9700\u91CD\u542F\u8BBE\u5907\u540E\u751F\u6548\u3002",
1035
+ "fp.rebootNow": "\u7ACB\u5373\u91CD\u542F",
1036
+ "fp.applyRealDevice": "\u771F\u673A\uFF1A\u4FDD\u5B58\u5373\u7ECF LSPosed \u8EAB\u4EFD\u5C42\u63A8\u9001\u5230\u8BBE\u5907\uFF08\u91CD\u5199 spoof.json + resetprop \u8EAB\u4EFD\uFF0C\u5E76\u5F3A\u5236\u91CD\u542F\u76EE\u6807\u5E94\u7528\u5373\u65F6\u751F\u6548\uFF0C\u65E0\u9700\u6574\u673A\u91CD\u542F\uFF09\u3002\u5DF2\u8FD0\u884C\u7684\u975E\u76EE\u6807\u5E94\u7528\u5728\u4E0B\u6B21\u542F\u52A8\u65F6\u751F\u6548\u3002",
1037
+ "fp.uniqueIdHint": "\u552F\u4E00\u6807\u8BC6\uFF08IMEI/\u5E8F\u5217\u53F7/MAC \u7B49\uFF09\u4EC5\u5728\u300C\u4E00\u952E\u751F\u6210\u300D\u65F6\u91CD\u6EDA\uFF0C\u4E0D\u53EF\u624B\u586B\u3002",
1038
+ "fp.antiDetect": "\u53CD\u68C0\u6D4B (LSPosed)",
1039
+ "fp.installing": "\u25CC \u5B89\u88C5\u4E2D\u2026",
1040
+ "fp.installed": "\u25CF \u5DF2\u5B89\u88C5",
1041
+ "fp.installFailed": "\u2715 \u5B89\u88C5\u5931\u8D25",
1042
+ "fp.notInstalled": "\u25CB \u672A\u5B89\u88C5",
1043
+ "fp.installingDesc": "\u8282\u70B9\u6B63\u5728\u5B89\u88C5\uFF08\u542B\u4E24\u6B21\u91CD\u542F\uFF0C\u7EA6 3\u20135 \u5206\u949F\uFF0C\u8BF7\u4FDD\u6301\u8BBE\u5907\u5728\u7EBF\uFF09\u3002\u72B6\u6001\u6BCF 5 \u79D2\u81EA\u52A8\u5237\u65B0\u2026",
1044
+ "fp.installDesc": "\u5728\u771F\u673A\u4E0A\u5B89\u88C5 LSPosed \u8EAB\u4EFD\u5C42 + \u4F2A\u88C5\u6A21\u5757\uFF08\u9700 Magisk root\uFF09\u3002\u542B\u4E24\u6B21\u91CD\u542F\uFF0C\u7EA6 3\u20135 \u5206\u949F\u3002\u5B89\u88C5\u540E\u6307\u7EB9\u6539\u52A8\u5373\u5B9E\u65F6\u63A8\u9001\uFF0C\u65E0\u9700\u518D\u88C5\u3002",
1045
+ "fp.lastInstallFailed": "\u4E0A\u6B21\u5B89\u88C5\u5931\u8D25\uFF1A{err}",
1046
+ "fp.retryInstall": "\u91CD\u8BD5\u5B89\u88C5",
1047
+ "fp.enableOnReal": "\u5728\u771F\u673A\u542F\u7528\u53CD\u68C0\u6D4B",
1048
+ "fp.noRoot": "\u8BBE\u5907\u672A\u68C0\u6D4B\u5230 root\uFF08Magisk\uFF09\uFF0C\u65E0\u6CD5\u5B89\u88C5\u3002",
1049
+ "fp.disguiseSwitch": "\u4F2A\u88C5\u5F00\u5173",
1050
+ "fp.disguiseEnabled": "\u5DF2\u542F\u7528\uFF1A\u8EAB\u4EFD\u5C42\u6B63\u5728\u4F2A\u88C5\u8303\u56F4\u5185\u5E94\u7528\u3002",
1051
+ "fp.disguiseDisabled": "\u5DF2\u505C\u7528\uFF1A\u8303\u56F4\u5185\u5E94\u7528\u8BFB\u5230\u771F\u5B9E\u8EAB\u4EFD\uFF08\u578B\u53F7/hardware \u7B49 resetprop \u9879\u9700\u91CD\u542F\u8BBE\u5907\u624D\u5B8C\u5168\u8FD8\u539F\uFF09\u3002",
1052
+ "fp.scopeLabel": "\u4F2A\u88C5\u5E94\u7528\u8303\u56F4 scope (\u6BCF\u884C\u4E00\u4E2A\u5305\u540D\uFF1B\u7559\u7A7A=\u5168\u90E8\u7B2C\u4E09\u65B9\u5E94\u7528)",
1053
+ "fp.scopeHint": "\u8303\u56F4\u5185\u5E94\u7528\u4F1A\u5728\u8EAB\u4EFD\u53D8\u66F4\u540E\u88AB\u5F3A\u5236\u91CD\u542F\u4EE5\u5373\u65F6\u751F\u6548\uFF1B\u8303\u56F4\u5916\u7684\u5DF2\u8FD0\u884C\u5E94\u7528\u5728\u4E0B\u6B21\u542F\u52A8\u65F6\u751F\u6548\u3002",
1054
+ "fp.saveScope": "\u4FDD\u5B58\u8303\u56F4\u5E76\u63A8\u9001",
1055
+ "fp.advancedRaw": "\u9AD8\u7EA7\uFF1A\u539F\u59CB device_profile",
1056
+ "fp.installStarted": "\u5B89\u88C5\u5DF2\u5F00\u59CB\uFF1A\u8282\u70B9\u540E\u53F0\u8FDB\u884C\uFF08\u542B\u4E24\u6B21\u91CD\u542F\uFF0C\u7EA6 3\u20135 \u5206\u949F\uFF0C\u8BF7\u4FDD\u6301\u8BBE\u5907\u5728\u7EBF\uFF09\u3002\u72B6\u6001\u4F1A\u81EA\u52A8\u5237\u65B0\u3002",
1057
+ "fp.installStartFailed": "\u5B89\u88C5\u542F\u52A8\u5931\u8D25: {err}",
1058
+ "fp.disguiseToggled": "\u4F2A\u88C5\u5DF2{state}\uFF08{tail}\uFF09",
1059
+ "fp.enabled": "\u542F\u7528",
1060
+ "fp.disabled": "\u505C\u7528",
1061
+ "fp.pushedRepush": "\u5DF2\u91CD\u65B0\u63A8\u9001\u6307\u7EB9\u5230\u8BBE\u5907\uFF0C\u8303\u56F4\u5185\u5E94\u7528\u5DF2\u91CD\u542F\u751F\u6548\u3002",
1062
+ "fp.pushedReal": "\u8303\u56F4\u5185\u5E94\u7528\u5DF2\u91CD\u542F\u8BFB\u5230\u771F\u5B9E\u8EAB\u4EFD\u3002\u578B\u53F7/hardware \u7B49 resetprop \u9879\u9700\u91CD\u542F\u8BBE\u5907\u624D\u5B8C\u5168\u8FD8\u539F\u3002",
1063
+ "fp.savedPushFailed": "\u5DF2\u4FDD\u5B58\uFF1B\u63A8\u9001\u5931\u8D25\uFF1A{err}\uFF0C\u8282\u70B9\u4E0A\u7EBF\u540E\u91CD\u8BD5\u3002",
1064
+ "fp.nodeOffline": "\u8282\u70B9\u79BB\u7EBF",
1065
+ "fp.saved": "\u5DF2\u4FDD\u5B58",
1066
+ "fp.savedScope": "\u5DF2\u66F4\u65B0\u4F2A\u88C5\u8303\u56F4",
1067
+ "fp.rebootTriggered": "\u5DF2\u89E6\u53D1\u91CD\u542F",
1068
+ "fp.pushedLive": "{label}\uFF08\u5DF2\u63A8\u9001\u5230\u771F\u673A\uFF0C\u76EE\u6807\u5E94\u7528\u5DF2\u91CD\u542F\u751F\u6548\uFF09",
1069
+ "fp.savedPushFailedLabel": "{label}\uFF08\u5DF2\u4FDD\u5B58\uFF1B\u63A8\u9001\u5931\u8D25\uFF1A{err}\uFF0C\u8282\u70B9\u4E0A\u7EBF\u540E\u91CD\u8BD5\uFF09",
1070
+ "fp.loadFailed": "\u52A0\u8F7D\u5931\u8D25: {err}",
1071
+ "fp.failed": "\u5931\u8D25: {err}",
1072
+ // ── FileManager ──
1073
+ "fm.sessionFailed": "\u5EFA\u7ACB\u8BBE\u5907\u4F1A\u8BDD\u5931\u8D25:{err}",
1074
+ "fm.openingSession": "\u6B63\u5728\u5EFA\u7ACB\u8BBE\u5907\u4F1A\u8BDD\u2026",
1075
+ "fm.refresh": "\u5237\u65B0",
1076
+ "fm.mkdir": "\u65B0\u5EFA\u76EE\u5F55",
1077
+ "fm.uploadHere": "\u4E0A\u4F20\u5230\u5F53\u524D\u76EE\u5F55",
1078
+ "fm.colName": "\u540D\u79F0",
1079
+ "fm.colSize": "\u5927\u5C0F",
1080
+ "fm.colPerms": "\u6743\u9650",
1081
+ "fm.colActions": "\u64CD\u4F5C",
1082
+ "fm.emptyDir": "\u7A7A\u76EE\u5F55",
1083
+ "fm.loading": "\u52A0\u8F7D\u4E2D\u2026",
1084
+ "fm.listFailed": "\u5217\u76EE\u5F55\u5931\u8D25:{err}",
1085
+ "fm.download": "\u4E0B\u8F7D",
1086
+ "fm.rename": "\u91CD\u547D\u540D",
1087
+ "fm.delete": "\u5220\u9664",
1088
+ "fm.mkdirTitle": "\u65B0\u5EFA\u76EE\u5F55",
1089
+ "fm.mkdirDesc": "\u5728 {cwd} \u4E0B\u521B\u5EFA",
1090
+ "fm.dirNamePlaceholder": "\u76EE\u5F55\u540D",
1091
+ "fm.renameTitle": "\u91CD\u547D\u540D",
1092
+ "fm.newNamePlaceholder": "\u65B0\u540D\u79F0",
1093
+ "fm.confirmDeleteTitle": "\u786E\u8BA4\u5220\u9664",
1094
+ "fm.confirmDeleteDesc": "\u5C06\u5220\u9664{kind}\u300C{name}\u300D,\u6B64\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002",
1095
+ "fm.kindDir": "\u76EE\u5F55(\u542B\u5185\u5BB9)",
1096
+ "fm.kindFile": "\u6587\u4EF6",
1097
+ "fm.opMkdir": "\u65B0\u5EFA\u76EE\u5F55",
1098
+ "fm.opRename": "\u91CD\u547D\u540D",
1099
+ "fm.opDelete": "\u5220\u9664",
1100
+ "fm.opOk": "{label}:\u6210\u529F",
1101
+ "fm.opErr": "{label}:{err}",
1102
+ "fm.uploadDoneRelay": "\u4E0A\u4F20\u5B8C\u6210(\u4E2D\u8F6C\u901A\u9053)",
1103
+ "fm.uploadDoneDirect": "\u4E0A\u4F20\u5B8C\u6210(\u76F4\u8FDE)",
1104
+ "fm.uploadFailed": "\u4E0A\u4F20\u5931\u8D25:{err}",
1105
+ "fm.downloadDoneRelay": "\u4E0B\u8F7D\u5B8C\u6210(\u4E2D\u8F6C\u901A\u9053)",
1106
+ "fm.downloadDoneDirect": "\u4E0B\u8F7D\u5B8C\u6210(\u76F4\u8FDE)",
1107
+ "fm.downloadFailed": "\u4E0B\u8F7D\u5931\u8D25:{err}",
1108
+ "fm.confirmOk": "\u786E\u5B9A",
1109
+ // ── DeviceTerminal ──
1110
+ "term.openingSession": "\u6B63\u5728\u5EFA\u7ACB\u8BBE\u5907\u4F1A\u8BDD\u2026",
1111
+ "term.interactiveReady": "\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u5DF2\u5C31\u7EEA\u3002",
1112
+ "term.pseudoMode": "\u51C6\u7EC8\u7AEF\u6A21\u5F0F:\u5728\u4E0B\u65B9\u8F93\u5165\u547D\u4EE4\u5E76\u56DE\u8F66\u6267\u884C\u3002",
1113
+ "term.pseudoHint": "(SDK \u6682\u672A\u63D0\u4F9B\u4EA4\u4E92\u5F0F shell();\u843D\u5730\u540E\u5C06\u81EA\u52A8\u5347\u7EA7\u4E3A\u771F\u4EA4\u4E92\u5F0F\u7EC8\u7AEF)",
1114
+ "term.sessionFailed": "\u5EFA\u7ACB\u4F1A\u8BDD\u5931\u8D25:{err}",
1115
+ "term.error": "\u7EC8\u7AEF\u9519\u8BEF:{err}",
1116
+ "term.running": "\u6267\u884C\u4E2D\u2026",
1117
+ "term.inputPlaceholder": "\u8F93\u5165\u547D\u4EE4,\u56DE\u8F66\u6267\u884C(\u2191\u2193 \u5386\u53F2)",
1118
+ "term.execFailed": "\u6267\u884C\u5931\u8D25:{err}",
1119
+ // ── DeviceSelector ──
1120
+ "deviceSelector.placeholder": "\u9009\u62E9\u8FD0\u884C\u8BBE\u5907\u2026",
1121
+ // ── DeviceConnect ──
1122
+ "connect.title": "Connect Device",
1123
+ "connect.desc": "Add a new test device",
1124
+ "connect.platform": "Platform",
1125
+ "connect.iosAddr": "UDID / WDA URL",
1126
+ "connect.androidAddr": "ADB Serial",
1127
+ "connect.hostAddr": "Hostname",
1128
+ "connect.iosPlaceholder": "e.g. http://localhost:8100",
1129
+ "connect.androidPlaceholder": "e.g. emulator-5554",
1130
+ "connect.connect": "Connect",
1131
+ // ── GroupMirrorModal ──
1132
+ "groupMirror.header": "\u7FA4\u63A7 \xB7 {n} \u53F0\uFF08{connected} \u5DF2\u8FDE\u63A5\uFF09\u2014 \u70B9\u51FB\u753B\u9762\u5207\u6362\u4E3B\u63A7",
1133
+ "groupMirror.truncated": "\u8D85\u51FA {max} \u53F0\u8F6F\u4E0A\u9650\uFF0C\u5DF2\u622A\u65AD",
1134
+ "groupMirror.quality": "\u753B\u8D28\u6863\u4F4D\uFF08\u5168\u90E8\u8BBE\u5907\uFF09",
1135
+ "groupMirror.profile.balanced": "\u5E73\u8861",
1136
+ "groupMirror.profile.lowLatency": "\u4F4E\u5EF6\u8FDF",
1137
+ "groupMirror.profile.highQuality": "\u9AD8\u753B\u8D28",
1138
+ "groupMirror.master": "\u4E3B\u63A7",
1139
+ "groupMirror.connectFailed": "\u8FDE\u63A5\u5931\u8D25\uFF1A{err}",
1140
+ "groupMirror.connecting": "\u6B63\u5728\u5EFA\u7ACB\u753B\u9762\u2026",
1141
+ // ── NodeRegister ──
1142
+ "nodeRegister.registeredTitle": "\u8282\u70B9\u5DF2\u6CE8\u518C",
1143
+ "nodeRegister.title": "\u6CE8\u518C\u8282\u70B9",
1144
+ "nodeRegister.desc": "\u751F\u6210\u8282\u70B9\u4EE4\u724C\uFF0C\u7136\u540E\u5728\u4E3B\u673A\u4E0A\u8FD0\u884C agent\u3002\u5176\u8BBE\u5907\u4F1A\u81EA\u52A8\u51FA\u73B0\u5728\u8FD9\u91CC\u3002",
1145
+ "nodeRegister.saveTokenWarn": "\u8BF7\u7ACB\u5373\u4FDD\u5B58\u6B64\u4EE4\u724C \u2014\u2014 \u4E0D\u4F1A\u518D\u6B21\u663E\u793A\u3002",
1146
+ "nodeRegister.runOnHost": "\u5728\u8282\u70B9\u4E3B\u673A\u4E0A\u8FD0\u884C\uFF1A",
1147
+ "nodeRegister.done": "\u5B8C\u6210",
1148
+ "nodeRegister.nodeName": "\u8282\u70B9\u540D\u79F0",
1149
+ "nodeRegister.nodeNamePlaceholder": "my-laptop",
1150
+ "nodeRegister.nodeNameRequired": "\u8282\u70B9\u540D\u79F0\u5FC5\u586B",
1151
+ "nodeRegister.register": "\u6CE8\u518C",
1152
+ // ── Scripts page (scripts.tsx) ──
1153
+ "scripts.count": "{n} \u4E2A\u811A\u672C",
1154
+ "scripts.newScript": "\u65B0\u5EFA\u811A\u672C",
1155
+ "scripts.searchPlaceholder": "\u6309\u540D\u79F0\u6216\u6807\u7B7E\u641C\u7D22\u811A\u672C\u2026",
1156
+ "scripts.allPlatforms": "\u5168\u90E8\u5E73\u53F0",
1157
+ "scripts.loading": "\u52A0\u8F7D\u811A\u672C\u2026",
1158
+ "scripts.noMatch": "\u6CA1\u6709\u5339\u914D\u7B5B\u9009\u6761\u4EF6\u7684\u811A\u672C",
1159
+ "scripts.createFromTemplate": "\u4ECE\u6A21\u677F\u521B\u5EFA",
1160
+ "scripts.createFromTemplateDesc": "\u4F7F\u7528\u9884\u8BBE\u811A\u672C\u7ED3\u6784\u66F4\u5FEB\u8D77\u6B65\u3002",
1161
+ "scripts.ready": "\u5C31\u7EEA",
1162
+ "scripts.duplicate": "\u590D\u5236",
1163
+ // ── Inspector page (inspector.tsx) ──
1164
+ "inspector.selectDevice": "\u9009\u62E9\u8BBE\u5907",
1165
+ "inspector.export": "\u5BFC\u51FA",
1166
+ // ── Tasks page (tasks.tsx) ──
1167
+ "tasks.newTask": "\u65B0\u5EFA\u4EFB\u52A1",
1168
+ "tasks.script": "\u811A\u672C",
1169
+ "tasks.selectScript": "\u9009\u62E9\u811A\u672C\u2026",
1170
+ "tasks.targetLabel": "{label}\uFF08\u76EE\u6807\uFF0C\u6BCF\u884C\u4E00\u4E2A \xB7 \u5171 {n}\uFF09",
1171
+ "tasks.targetPlaceholder": "\u6BCF\u884C\u4E00\u4E2A\u76EE\u6807\uFF0C\u53EF\u76F4\u63A5\u7C98\u8D34\u591A\u884C",
1172
+ "tasks.sharedParams": "\u5171\u7528\u53C2\u6570\uFF08\u6240\u6709\u76EE\u6807\u884C\u5171\u4EAB\uFF09",
1173
+ "tasks.noTargetHint": "\u8BE5\u811A\u672C\u672A\u58F0\u660E\u76EE\u6807\u5B57\u6BB5\uFF1B\u5C06\u6309\u4E0B\u65B9\u8BBE\u5907\u4E0E\u6A21\u5F0F\u5404\u8DD1\u4E00\u6B21\u3002",
1174
+ "tasks.targetDevices": "\u76EE\u6807\u8BBE\u5907\uFF08\u5DF2\u9009 {n}\uFF09",
1175
+ "tasks.noDevices": "\u6682\u65E0\u8BBE\u5907\u3002",
1176
+ "tasks.selectAll": "\u5168\u9009",
1177
+ "tasks.onlyOnline": "\u4EC5\u9009\u5728\u7EBF\uFF08{n}\uFF09",
1178
+ "tasks.offlineWarn": "\u5DF2\u9009 {n} \u53F0\u79BB\u7EBF\u8BBE\u5907\uFF1A\u5176\u4EFB\u52A1\u4F1A\u4E00\u76F4\u7B49\u5F85\u76F4\u5230\u8BBE\u5907\u4E0A\u7EBF\u6216\u8FC7\u671F\u3002",
1179
+ "tasks.dispatchMode": "\u6D3E\u53D1\u6A21\u5F0F",
1180
+ "tasks.enqueuing": "\u5165\u961F\u4E2D\u2026",
1181
+ "tasks.enqueueRun": "\u5165\u961F\u6267\u884C\uFF08{n} \u4E2A\u76EE\u6807\uFF09",
1182
+ "tasks.mode.roundRobin": "\u8F6E\u8BE2\uFF08\u6BCF\u884C\u5206\u644A\u5230\u4E00\u53F0\u8BBE\u5907\uFF09",
1183
+ "tasks.mode.broadcast": "\u5168\u91CF\uFF08\u6BCF\u884C\u5728\u6240\u6709\u8BBE\u5907\u5404\u8DD1\u4E00\u904D\uFF09",
1184
+ "tasks.tab.all": "\u5168\u90E8",
1185
+ "tasks.tab.pending": "\u7B49\u5F85",
1186
+ "tasks.tab.running": "\u8FDB\u884C\u4E2D",
1187
+ "tasks.tab.paused": "\u5DF2\u6682\u505C",
1188
+ "tasks.tab.succeeded": "\u5B8C\u6210",
1189
+ "tasks.tab.failed": "\u5931\u8D25",
1190
+ "tasks.tab.cancelled": "\u5DF2\u53D6\u6D88",
1191
+ "tasks.empty": "\u6682\u65E0\u4EFB\u52A1\u3002",
1192
+ "tasks.batchLabel": "\u6279\u6B21 \xB7 {n} \u4E2A\u76EE\u6807",
1193
+ "tasks.progress": "{finished}/{total} \u5B8C\u6210 \xB7 {percent}%",
1194
+ "tasks.expandTargets": "\u5C55\u5F00\u76EE\u6807",
1195
+ "tasks.collapseTargets": "\u6536\u8D77\u76EE\u6807",
1196
+ "tasks.resumeAll": "\u6062\u590D\u5168\u90E8",
1197
+ "tasks.pauseAll": "\u6682\u505C\u5168\u90E8\uFF08\u4EC5 pending \u76EE\u6807\uFF09",
1198
+ "tasks.cancelAll": "\u7EC8\u6B62\u5168\u90E8",
1199
+ "tasks.loadingDetail": "\u52A0\u8F7D\u660E\u7EC6\u2026",
1200
+ "tasks.noDeviceDetail": "\u65E0\u8BBE\u5907\u660E\u7EC6\u3002",
1201
+ "tasks.expandDetail": "\u5C55\u5F00\u660E\u7EC6",
1202
+ "tasks.collapseDetail": "\u6536\u8D77\u660E\u7EC6",
1203
+ "tasks.source": "\u6765\u6E90 {source}",
1204
+ "tasks.sourceTip.webhook": "\u6765\u6E90\uFF1A\u5916\u90E8 webhook\uFF08PAT\uFF09",
1205
+ "tasks.sourceTip.local": "\u6765\u6E90\uFF1A\u672C\u5730 / \u624B\u52A8",
1206
+ "tasks.resume": "\u6062\u590D",
1207
+ "tasks.pause": "\u6682\u505C\uFF08\u4EC5 pending \u76EE\u6807\uFF09",
1208
+ "tasks.cancel": "\u7EC8\u6B62",
1209
+ "tasks.noTargetDetail": "\u65E0\u76EE\u6807\u660E\u7EC6\u3002",
1210
+ "tasks.recentEvents": "\u6700\u8FD1\u4E8B\u4EF6",
1211
+ "tasks.status.pending": "\u7B49\u5F85",
1212
+ "tasks.status.notified": "\u6D3E\u53D1\u4E2D",
1213
+ "tasks.status.running": "\u8FDB\u884C\u4E2D",
1214
+ "tasks.status.paused": "\u5DF2\u6682\u505C",
1215
+ "tasks.status.succeeded": "\u5B8C\u6210",
1216
+ "tasks.status.failed": "\u5931\u8D25",
1217
+ "tasks.status.cancelled": "\u5DF2\u53D6\u6D88",
1218
+ "tasks.status.expired": "\u5DF2\u8FC7\u671F",
1219
+ // ── Integration panel (tasks) ──
1220
+ "integration.openForTask": "\u4E3A\u6B64\u4EFB\u52A1\u751F\u6210\u63A5\u5165 snippet",
1221
+ "integration.open": "\u63A5\u5165",
1222
+ "integration.title": "\u63A5\u5165 / API \u8C03\u7528",
1223
+ "integration.tokens": "\u8BBF\u95EE\u4EE4\u724C (PAT)",
1224
+ "integration.tokens.empty": "\u6682\u65E0\u4EE4\u724C\u3002",
1225
+ "integration.create": "\u65B0\u5EFA\u4EE4\u724C",
1226
+ "integration.creating": "\u521B\u5EFA\u4E2D\u2026",
1227
+ "integration.tokenName": "\u4EE4\u724C\u540D\u79F0",
1228
+ "integration.tokenNamePlaceholder": "\u4F8B\u5982 n8n \u5DE5\u4F5C\u6D41",
1229
+ "integration.scope": "\u9879\u76EE\u4F5C\u7528\u57DF",
1230
+ "integration.scope.all": "\u540D\u4E0B\u6240\u6709\u9879\u76EE\uFF08\u6BCF\u6B21\u8BF7\u6C42\u5E26 X-Project-Id\uFF09",
1231
+ "integration.scope.pin": "\u9650\u5B9A\u5230\u5355\u4E2A\u9879\u76EE",
1232
+ "integration.projectId": "\u9879\u76EE ID",
1233
+ "integration.projectIdPlaceholder": "\u9879\u76EE UUID",
1234
+ "integration.created.once": "\u8BF7\u7ACB\u5373\u590D\u5236\u6B64\u4EE4\u724C \u2014\u2014 \u4EC5\u6B64\u4E00\u6B21\u663E\u793A\u3002",
1235
+ "integration.copy": "\u590D\u5236",
1236
+ "integration.copied": "\u5DF2\u590D\u5236",
1237
+ "integration.revoke": "\u64A4\u9500",
1238
+ "integration.revokeConfirm": "\u64A4\u9500\u6B64\u4EE4\u724C\uFF1F\u4F7F\u7528\u5B83\u7684\u8C03\u7528\u5C06\u7ACB\u5373\u5931\u8D25\u3002",
1239
+ "integration.revoked": "\u5DF2\u64A4\u9500",
1240
+ "integration.expires": "\u5230\u671F",
1241
+ "integration.lastUsed": "\u6700\u8FD1\u4F7F\u7528",
1242
+ "integration.never": "\u4ECE\u672A",
1243
+ "integration.selectToken": "snippet \u4F7F\u7528\u7684\u4EE4\u724C",
1244
+ "integration.selectTokenHint": "\u9009\u4E00\u4E2A\u4EE4\u724C\u586B\u5165\u4E0B\u65B9 snippet\u3002\u660E\u6587\u4EC5\u5728\u521B\u5EFA\u540E\u7ACB\u5373\u53EF\u7528\uFF1B\u5426\u5219\u7528\u524D\u7F00\u5360\u4F4D\u3002",
1245
+ "integration.snippets": "\u53EF\u8C03\u7528\u63A5\u53E3",
1246
+ "integration.snippet.listScripts": "\u811A\u672C\u5217\u8868\uFF08\u542B\u53C2\u6570\uFF09",
1247
+ "integration.snippet.listDevices": "\u8BBE\u5907\u5217\u8868",
1248
+ "integration.snippet.execTask": "\u6267\u884C\u4EFB\u52A1",
1249
+ "integration.snippet.listTasks": "\u4EFB\u52A1\u5217\u8868 / \u67E5\u8BE2",
1250
+ "integration.unpinnedHint": "\u8BE5\u4EE4\u724C\u672A\u9650\u5B9A\u9879\u76EE \u2014\u2014 \u8BF7\u66FF\u6362 X-Project-Id \u5934\u91CC\u7684 <PROJECT_ID>\u3002",
1251
+ // ── Billing page (billing) ──
1252
+ "billing.title": "\u8D26\u5355",
1253
+ "billing.balance": "\u4F59\u989D",
1254
+ "billing.currency": "\u8D27\u5E01",
1255
+ "billing.refresh": "\u5237\u65B0",
1256
+ "billing.loading": "\u52A0\u8F7D\u8D26\u5355\u2026",
1257
+ "billing.ledger": "\u6D88\u8017\u6D41\u6C34",
1258
+ "billing.ledger.empty": "\u6682\u65E0\u6D41\u6C34\u8BB0\u5F55\u3002",
1259
+ "billing.col.kind": "\u7C7B\u578B",
1260
+ "billing.col.amount": "\u91D1\u989D",
1261
+ "billing.col.balanceAfter": "\u4F59\u989D",
1262
+ "billing.col.ref": "\u5173\u8054",
1263
+ "billing.col.time": "\u65F6\u95F4",
1264
+ "billing.kind.topup": "\u5145\u503C",
1265
+ "billing.kind.platform_session": "\u8BBE\u5907\u65F6\u957F",
1266
+ "billing.kind.subscription": "\u8BA2\u9605",
1267
+ "billing.kind.own_node_daily": "\u81EA\u6709\u8282\u70B9\u65E5\u8D39",
1268
+ "billing.kind.adjust": "\u8C03\u6574",
1269
+ "billing.subs": "\u8BA2\u9605",
1270
+ "billing.subs.empty": "\u6682\u65E0\u8BA2\u9605\u3002",
1271
+ "billing.subs.tier": "\u6863\u4F4D",
1272
+ "billing.subs.status": "\u72B6\u6001",
1273
+ "billing.subs.remaining": "\u5269\u4F59\uFF08\u5C0F\u65F6\uFF09",
1274
+ "billing.subs.expires": "\u5230\u671F",
1275
+ "billing.subs.boundDevice": "\u7ED1\u5B9A\u8BBE\u5907",
1276
+ "billing.ownNode": "\u81EA\u6709\u8282\u70B9\u65E5\u8D39",
1277
+ "billing.ownNode.empty": "\u6682\u65E0\u81EA\u6709\u8282\u70B9\u65E5\u8D39\u3002",
1278
+ "billing.ownNode.day": "\u65E5\u671F",
1279
+ "billing.ownNode.devices": "\u8BBE\u5907\u6570",
1280
+ "billing.ownNode.billable": "\u8BA1\u8D39\u6570",
1281
+ "billing.ownNode.amount": "\u91D1\u989D",
1282
+ // ── Recharge section ──
1283
+ "recharge.title": "\u5145\u503C",
1284
+ "recharge.methods": "\u652F\u4ED8\u65B9\u5F0F",
1285
+ "recharge.desc": "\u9009\u62E9\u652F\u4ED8\u65B9\u5F0F\u5411\u94B1\u5305\u5145\u503C\u3002",
1286
+ "recharge.wechat": "\u5FAE\u4FE1\u652F\u4ED8",
1287
+ "recharge.alipay": "\u652F\u4ED8\u5B9D",
1288
+ "recharge.contact": "\u8054\u7CFB\u5BA2\u670D",
1289
+ "recharge.contactDesc": "\u8054\u7CFB\u6211\u4EEC\u8FDB\u884C\u4EBA\u5DE5\u5145\u503C\u6216\u6279\u91CF\u8D2D\u4E70\u3002",
1290
+ "recharge.comingSoon": "\u652F\u4ED8\u63A5\u5165\u4E2D",
1291
+ // ── TopupDrawer ──
1292
+ "topup.title": "\u94B1\u5305\u5145\u503C",
1293
+ "topup.target": "\u76EE\u6807\u7528\u6237",
1294
+ "topup.amount": "\u91D1\u989D",
1295
+ "topup.amountPlaceholder": "\u4F8B\u5982 100",
1296
+ "topup.note": "\u5907\u6CE8\uFF08\u53EF\u9009\uFF09",
1297
+ "topup.notePlaceholder": "\u539F\u56E0 / \u5173\u8054\u5355\u53F7",
1298
+ "topup.submit": "\u5145\u503C",
1299
+ "topup.submitting": "\u5145\u503C\u4E2D\u2026",
1300
+ "topup.amountInvalid": "\u8BF7\u8F93\u5165\u5927\u4E8E 0 \u7684\u91D1\u989D",
1301
+ "topup.done": "\u5B8C\u6210 \xB7 \u65B0\u4F59\u989D {balance}",
1302
+ "topup.failed": "\u5145\u503C\u5931\u8D25\uFF1A{err}",
1303
+ // ── Market page (market.tsx) ──
1304
+ "market.empty": "\u8FD8\u6CA1\u6709\u516C\u5F00\u811A\u672C",
1305
+ "market.editAria": "\u7F16\u8F91 {name}",
1306
+ // ── Editor AgentPanel ──
1307
+ "agent.unavailable": "\u672C\u5730 agent\uFF08claude/codex\uFF09\u9700\u5728\u684C\u9762\u5E94\u7528\u4E2D\u4F7F\u7528\u3002",
1308
+ "agent.emptyHint": "\u63CF\u8FF0\u4F60\u60F3\u8981\u7684\u811A\u672C\uFF0Cagent \u4F1A\u7528 MCP \u67E5\u5B9E\u65F6\u8BBE\u5907\u5E76\u76F4\u63A5\u6539 script.ts\u3002",
1309
+ "agent.needDevice": "\u8BF7\u5148\u5728\u9876\u680F\u9009\u62E9\u4E00\u53F0\u8BBE\u5907\uFF0Cagent \u9700\u8981\u8FDE\u5230\u8BBE\u5907\u53D6\u5B9E\u65F6\u503C\u3002",
1310
+ "agent.selectDeviceFirst": "\u8BF7\u5148\u9009\u62E9\u8BBE\u5907",
1311
+ "agent.startFailed": "\u542F\u52A8 agent \u5931\u8D25\uFF1A{err}",
1312
+ "agent.noApps": "\u6CA1\u6709\u53EF\u5217\u51FA\u7684 app\u3002",
1313
+ "agent.useInspector": "\u{1F50D} \u7528 Inspector \u9009\u5143\u7D20",
1314
+ "agent.loading": "\u52A0\u8F7D\u4E2D\u2026",
1315
+ "agent.closeApps": "\u2715 \u5173\u95ED app \u5217\u8868",
1316
+ "agent.pickApp": "\u{1F4F1} @app \u9009\u5E94\u7528",
1317
+ "agent.placeholder": "\u95EE agent / \u63CF\u8FF0\u4F60\u60F3\u8981\u7684\u811A\u672C\u2026\uFF08Enter \u53D1\u9001\uFF0CShift+Enter \u6362\u884C\uFF09",
1318
+ "agent.stop": "\u505C\u6B62",
1319
+ "agent.send": "\u53D1\u9001"
1320
+ };
1321
+ var dictionaries = { en, zh };
1322
+
1323
+ // src/ui/i18n/index.ts
1324
+ var I18nContext = createContext(null);
1325
+ function interpolate(tpl, vars) {
1326
+ if (!vars) return tpl;
1327
+ return tpl.replace(/\{(\w+)\}/g, (_, k) => k in vars ? String(vars[k]) : `{${k}}`);
1328
+ }
1329
+ function AixI18nProvider({ lang = "en", overrides, children }) {
1330
+ const value = useMemo(() => {
1331
+ const base = dictionaries[lang] ?? dictionaries.en;
1332
+ const merged = overrides?.[lang] ? { ...base, ...overrides[lang] } : base;
1333
+ const t = (key, vars) => interpolate(merged[key] ?? key, vars);
1334
+ return { lang, t };
1335
+ }, [lang, overrides]);
1336
+ return createElement(I18nContext.Provider, { value }, children);
1337
+ }
1338
+ function useAixT() {
1339
+ const ctx = useContext(I18nContext);
1340
+ if (ctx) return ctx.t;
1341
+ return (key, vars) => interpolate(dictionaries.en[key] ?? key, vars);
1342
+ }
1343
+ function useAixLang() {
1344
+ return useContext(I18nContext)?.lang ?? "en";
1345
+ }
1346
+
1347
+ export { AixI18nProvider, useAixLang, useAixT };