diff --git a/apps/web/app/components/files/dialogs.tsx b/apps/web/app/components/files/dialogs.tsx index 643bca6b..f9398f73 100644 --- a/apps/web/app/components/files/dialogs.tsx +++ b/apps/web/app/components/files/dialogs.tsx @@ -145,7 +145,7 @@ export function UploadButton({ root, path }: { root: FileRoot; path: string }) { setBusy(null); if (input.current) input.current.value = ""; if (ok) toast.success(`已上传 ${ok} 个文件到 ${displayPath(root, path)}`); - if (failed.length) toast.error(`${failed.length} 个文件没有上传`, { description: failed.join("\n") }); + if (failed.length) toast.error(`${failed.length} 个文件上传失败`, { description: failed.join("\n") }); }; return ( @@ -190,7 +190,7 @@ export function MkdirDialog({ onOpenChange(false); onCreated?.(r.path); }, - onError: (e) => setErr(filesErrorReason(e) === "exists" ? "同名项已存在" : filesErrorText(e)), + onError: (e) => setErr(filesErrorReason(e) === "exists" ? "同名已存在" : filesErrorText(e)), }, ); }; @@ -232,13 +232,13 @@ export function MkdirDialog({ // ─── 重命名 ───────────────────────────────────────────────────── const SUGGEST_REASON: Record = { - identity_uncertain: "识别不可信,没有推荐名", + identity_uncertain: "认不准作品,没有推荐名", }; const TEMPLATE_SOURCE: Record = { entity: "作品覆盖", root: "媒体库目录", global: "全局", - override: "覆写", + override: "保留原文件名", }; function suggestReasonText(r: string | null): string { @@ -418,17 +418,17 @@ export function RenameDialog({
{suggest.isPending && !suggested ? (

- 取推荐名… + 生成推荐名…

) : suggest.error ? ( -

{filesErrorText(suggest.error, "取推荐名失败")}

+

{filesErrorText(suggest.error, "生成推荐名失败")}

) : suggested && !suggested.name ? (

{suggestReasonText(suggested.reason)}

) : suggested?.name ? ( <>

{suggested.name === entry.name - ? "当前名称与命名模板一致" + ? "已符合命名模板" : usingSuggestion ? "已填入推荐名" : `推荐名:${suggested.name}`} @@ -460,17 +460,17 @@ export function RenameDialog({ {!isDir ? (

- 同名附属文件一起改 - 主干相同的字幕、NFO、图片 + 附属文件一起改名 + 主文件名相同的字幕、NFO 和图片 - +
) : null} {changed && !problem && !taken ? (
{preview.isPending ? (

- 预览中 + 预览中…

) : null} {pvErr ? {filesErrorText(pvErr)} : null} @@ -487,10 +487,10 @@ export function RenameDialog({ ))} {pv.assetLocations > 0 ? ( - 同步 {pv.assetLocations} 条文件位置,写一条整理记录。 + 会同步 {pv.assetLocations} 条文件位置,并写一条整理记录。 ) : null} {pv.bindings.length > 0 ? ( - 这是作品或季的绑定目录。改名后旧绑定标为已替代,之后整理用新目录。 + 这是作品或季的绑定目录。改名后旧绑定作废,之后整理用新目录。 ) : null} ) : null} @@ -570,11 +570,11 @@ export function TransferDialog({ const ok = r.executed?.succeeded.length ?? 0; const failed = r.executed?.failed ?? []; const recordId = r.executed?.recordId; - const msg = `${mode === "move" ? "已移动" : "已复制"} ${ok} 项${failed.length ? `,${failed.length} 项失败` : ""}`; + const msg = `${mode === "move" ? "已移动" : "已复制"} ${ok} 项${failed.length ? `,${failed.length} 项失败` : ""}`; (failed.length ? toast.warning : toast.success)(msg, { description: failed .slice(0, 3) - .map((f) => `${baseName(f.path)}:${f.message}`) + .map((f) => `${baseName(f.path)}:${f.message}`) .join("\n"), action: recordId ? { label: "整理记录", onClick: () => window.location.assign(`/console/organize/${recordId}`) } @@ -599,8 +599,7 @@ export function TransferDialog({ {mode === "move" ? "移动到…" : "复制到…"} - {items.length === 1 ? baseName(items[0]?.path ?? "") : `${items.length} 项`} · 先预览再执行。跨卷移动按复制 - → 核验 → 删源。 + {items.length === 1 ? baseName(items[0]?.path ?? "") : `${items.length} 项`} · 跨卷移动会先复制、核验,再删除源文件。
@@ -637,7 +636,7 @@ export function TransferDialog({ className="max-h-72 md:max-h-96" />

- 可选可写的媒体库目录或 Alist 存储;本机和 Alist 之间会上传或下载。 + 目标可以是可写的媒体库目录或 Alist 存储。

@@ -647,11 +646,11 @@ export function TransferDialog({

{displayPath(targetRoot, target.path)}

) : ( - 在左侧选择目标目录。 + 选择目标目录。 )} {dry.isPending ? (

- 生成预览 + 生成预览…

) : null} {err ? {err} : null} @@ -666,15 +665,15 @@ export function TransferDialog({ {plan.freeBytes != null ? ` · 剩余 ${formatBytes(plan.freeBytes, 1)}` : ""} {conflicts.length ? ( - 冲突 {conflicts.length}(跳过,不覆盖) + {conflicts.length} 项目标已存在 · 跳过 ) : null} {plan.pairs.some((p) => p.registered) ? ( - 含已整理文件,执行后同步位置 + 含已整理文件,会同步文件位置 ) : null}
{hnrBlocked ? ( - {plan.hnrBlocks.length} 项做种中且 H&R 未满足,不能移动。改用复制,或等 H&R 满足后再移动。 + {plan.hnrBlocks.length} 项做种中且 H&R 未满足,不能移动。可以改用复制。
) : ( @@ -1230,7 +1229,7 @@ export function ScrapeDialog({ items, root, onClose }: { items: FileEntry[]; roo

认不准作品,选一个再刮削

{choices.map((c) => (
-

{c.path || "目录根"}

+

{c.path || "(登记目录本身)"}

加载中…

: null} {skipped.map((x) => (

- {x.path || "目录根"}:{SKIP_TEXT[x.reason] ?? x.reason} + {x.path || "(登记目录本身)"}:{SKIP_TEXT[x.reason] ?? x.reason}

))}
diff --git a/apps/web/app/components/files/dir-tree.tsx b/apps/web/app/components/files/dir-tree.tsx index 2e35ec99..180e9977 100644 --- a/apps/web/app/components/files/dir-tree.tsx +++ b/apps/web/app/components/files/dir-tree.tsx @@ -230,7 +230,7 @@ function Children({ className="flex items-center gap-2 py-1 text-meta text-muted-foreground" style={{ paddingLeft: depth * 14 + 28 }} > - 加载中 + 加载中… ); if (q.error) @@ -242,7 +242,7 @@ function Children({ if (!dirs.length) return (

- 无子目录 + 没有子目录

); return ( @@ -293,7 +293,7 @@ function Children({ {paginate && dirs.length > 0 ? : null} {q.data?.truncated ? (

- 子目录过多,只列出一部分 + 子目录太多,只列出一部分

) : null} diff --git a/apps/web/app/components/files/directory-dialog.tsx b/apps/web/app/components/files/directory-dialog.tsx index 779269a7..74cfdb66 100644 --- a/apps/web/app/components/files/directory-dialog.tsx +++ b/apps/web/app/components/files/directory-dialog.tsx @@ -43,8 +43,10 @@ export function DirectoryDialog({ !open && onClose()}> - {mode === "source" ? "选择来源目录" : "选择媒体库目标目录"} - 展开登记目录,选择一个目录后确认。 + {mode === "source" ? "选择来源目录" : "选择目标目录"} + + {mode === "source" ? "可以选下载目录或媒体库目录。" : "只能选可写的媒体库目录。"} +
@@ -56,7 +58,7 @@ export function DirectoryDialog({ paginate /> {roots.isPending ? ( -

读取目录中…

+

加载中…

) : !available.length ? (

没有可用的登记目录。

) : null} @@ -75,12 +77,12 @@ export function DirectoryDialog({ ) : null} {!selected ? ( -

在左侧选择登记目录,查看内容。

+

选择一个登记目录。

) : listing.isPending ? ( -

读取目录中…

+

加载中…

) : listing.error ? (

- {filesErrorText(listing.error, "目录不可读取")} + {filesErrorText(listing.error, "读取目录失败")}

) : !entries.length ? (

目录为空。

@@ -110,15 +112,15 @@ export function DirectoryDialog({
{roots.error ? (

- 读取目录失败,请关闭后重试。 + 读取登记目录失败。关闭后重试

) : null}

- {root ? `${root.name} / ${selected?.path || "(根目录)"}` : "尚未选择"} + {root ? `${root.name} / ${selected?.path || "(登记目录本身)"}` : "未选择目录"}

{!allowRoot && selected && !selected.path ? (

- 请选择子目录,或先新增来源配置,再整理根目录存量。 + 不能直接整理登记目录本身。选一个子目录,或在设置里新增整理来源后用「整理存量」。

) : null}
@@ -129,7 +131,7 @@ export function DirectoryDialog({ disabled={!root || !directorySelectionAllowed(selected, allowRoot) || listing.isPending || !!listing.error} onClick={() => selected && directorySelectionAllowed(selected, allowRoot) && onSelect(selected)} > - 使用此目录 + 使用这个目录
diff --git a/apps/web/app/components/files/file-detail.tsx b/apps/web/app/components/files/file-detail.tsx index 2815b966..4edb7ec0 100644 --- a/apps/web/app/components/files/file-detail.tsx +++ b/apps/web/app/components/files/file-detail.tsx @@ -119,7 +119,7 @@ export function FileDetail({ label: "链接数", value: stat.data?.nlink != null - ? `${stat.data.nlink}${stat.data.sharedInode ? "(共享 inode,不能原地改写)" : ""}` + ? `${stat.data.nlink}${stat.data.sharedInode ? "(硬链接,不能原地改写)" : ""}` : null, }, { @@ -163,7 +163,7 @@ export function FileDetail({ {e.asset.slotStatus ? (SLOT_STATUS[e.asset.slotStatus]?.label ?? e.asset.slotStatus) : "查看"} ) : ( - "未满足任何收录版本" + "未关联收录版本" ), }, { label: "主文件", value: e.asset.isPrimary ? "是" : "否" }, @@ -215,7 +215,7 @@ export function FileDetail({ ))} -

改名或移动此目录会同步更新绑定;刷新元数据不改路径。

+

改名或移动这个目录会同步更新绑定。刷新元数据不改路径。

) : null} @@ -245,7 +245,7 @@ export function FileDetail({ { onSuccess: (r) => { const it = r.items[0]; - if (it?.status !== "ok") toast.warning(`探测不到媒体信息:${it?.reason ?? "未知原因"}`); + if (it?.status !== "ok") toast.warning(`没有探测到媒体信息${it?.reason ? `:${it.reason}` : ""}`); }, onError: (err) => toast.error(filesErrorText(err, "探测失败")), }, @@ -278,7 +278,7 @@ export function FileDetail({ /> ) : (

- 未登记文件不自动探测。点「探测」读取 ffprobe 摘要,结果缓存 24 小时。 + 未整理的文件不自动探测。点「探测」用 ffprobe 读取,结果缓存 24 小时。

)} diff --git a/apps/web/app/components/files/file-list.tsx b/apps/web/app/components/files/file-list.tsx index fe4b9f74..15df5c0f 100644 --- a/apps/web/app/components/files/file-list.tsx +++ b/apps/web/app/components/files/file-list.tsx @@ -562,7 +562,7 @@ export function FileList({
{loadingMore ? (
- 加载下一页 + 加载中…
) : null} {footer} diff --git a/apps/web/app/components/files/file-preview.tsx b/apps/web/app/components/files/file-preview.tsx index 8eeebde2..d8256789 100644 --- a/apps/web/app/components/files/file-preview.tsx +++ b/apps/web/app/components/files/file-preview.tsx @@ -190,7 +190,7 @@ function ImageView({ className="max-h-[28rem] rounded-xl border border-border object-contain" /> - 新窗口看原图 + 在新窗口打开原图 diff --git a/apps/web/app/components/files/identify-panel.tsx b/apps/web/app/components/files/identify-panel.tsx index e40b783a..339b11ac 100644 --- a/apps/web/app/components/files/identify-panel.tsx +++ b/apps/web/app/components/files/identify-panel.tsx @@ -52,12 +52,12 @@ export function IdentifyPanel({ > {identify.isPending ? (

- 匹配本地作品与 TMDB… + 匹配本地作品和 TMDB…

) : identify.error ? (

{filesErrorText(identify.error, "识别失败")}

) : !identify.data ? ( -

按文件名解析,匹配本地作品和 TMDB。只读,不改文件。

+

按文件名匹配本地作品和 TMDB,不改文件。

) : !it ? (

{isDir ? "目录里没有视频文件。" : "没有识别结果。"}

) : ( @@ -98,7 +98,7 @@ export function IdentifyPanel({

) : null} {it.skipped === "below_min_size" ? ( -

低于最小体积,没有查候选。

+

文件小于最小体积,没有查找候选。

) : null} {it.warnings.map((w) => (

@@ -132,7 +132,7 @@ export function IdentifyPanel({ {c.year ? ` (${c.year})` : ""} 置信 {LEVEL[c.level]?.label ?? c.level} - {c.mediaEntityId ? null : 仅 TMDB} + {c.mediaEntityId ? null : 不在媒体库}

{[ diff --git a/apps/web/app/components/files/mock.ts b/apps/web/app/components/files/mock.ts index 635ea41d..71711b00 100644 --- a/apps/web/app/components/files/mock.ts +++ b/apps/web/app/components/files/mock.ts @@ -951,7 +951,7 @@ function organize(body: { recordId: body.dryRun || conflict ? null : `rec-${++seq}`, taskId: body.dryRun || conflict ? null : `task-${seq}`, warnings: items.some((x) => x.season != null) && !tv ? ["电影不使用季/集覆写"] : [], - error: conflict ? { code: "plan_blocked", message: "目标已存在同名文件,按「交给待处理」整组不执行" } : null, + error: conflict ? { code: "plan_blocked", message: "目标已存在同名文件,整组不执行" } : null, action: body.action, entity: items.some((x) => x.external) ? { provider: "tmdb", externalId: "95396", created: true, title: "人生切割术" } diff --git a/apps/web/app/components/files/model.test.ts b/apps/web/app/components/files/model.test.ts index 9d7c2112..20b60395 100644 --- a/apps/web/app/components/files/model.test.ts +++ b/apps/web/app/components/files/model.test.ts @@ -312,7 +312,7 @@ describe("transferTargetProblem(U13)", () => { ]; it("目标就是源所在目录 / 在源目录之内时拦下", () => { expect(transferTargetProblem(items, { root: "dl", path: "a" })).toMatch(/已在这个目录里/); - expect(transferTargetProblem(items, { root: "dl", path: "pack/sub" })).toMatch(/目标在源目录之内/); + expect(transferTargetProblem(items, { root: "dl", path: "pack/sub" })).toMatch(/目标不能是 pack 或它的子目录/); expect(transferTargetProblem([{ root: "dl", path: "x.mkv" }], { root: "dl", path: "" })).toMatch(/已在/); }); it("其他目录、其他根放行", () => { @@ -370,7 +370,7 @@ describe("识别远端回填(S-7)", () => { { ...other, taskId: "run-1", status: "failed", error: "超时" } as never, ); expect(f.remoteTaskId).toBeNull(); - expect(f.items[0]?.warnings).toEqual(["远端查询失败,只有本地结果:超时"]); + expect(f.items[0]?.warnings).toEqual(["TMDB 查询失败,只显示本地结果:超时"]); }); }); diff --git a/apps/web/app/components/files/model.ts b/apps/web/app/components/files/model.ts index 81e44dd6..0e44c95d 100644 --- a/apps/web/app/components/files/model.ts +++ b/apps/web/app/components/files/model.ts @@ -15,11 +15,11 @@ export type StatusView = { label: string; tone: StatusTone; hint?: string }; /** 行主状态 → 徽标(文字必带,不只靠颜色) */ export function entryStatusView(e: Pick): StatusView | null { - if (e.escape) return { label: "链接越界", tone: "destructive", hint: "符号链接指向登记目录之外,所有操作已禁用" }; + if (e.escape) return { label: "链接越界", tone: "destructive", hint: "符号链接指向登记目录之外,不能操作" }; const s: FileStatus = e.status; switch (s) { case "temp": - return { label: "未完成", tone: "neutral", hint: "下载器或整理过程中的未完成文件" }; + return { label: "未完成", tone: "neutral", hint: "下载或整理还没完成的临时文件" }; case "downloading": return { label: e.download?.progress != null ? `下载中 ${Math.floor(e.download.progress * 100)}%` : "下载中", @@ -44,7 +44,7 @@ export const FILTER_CHIPS = [ { value: "unidentified", label: "未识别" }, { value: "registered", label: "已整理" }, { value: "downloading", label: "下载中" }, - { value: "temp", label: "未完成下载" }, + { value: "temp", label: "未完成" }, ] as const; export const SORT_OPTIONS = [ @@ -100,7 +100,7 @@ export function opDisabledReason(op: FileOp, e: FileEntry, root: Pick(items: T[], max: number): (T | null)[] { export function nameProblem(name: string): string | null { const n = name.trim(); if (!n) return "名称不能为空"; - if (n === "." || n === "..") return "不能用 . 或 .."; + if (n === "." || n === "..") return "名称不能是 . 或 .."; if (/[/\\]/.test(n)) return "名称不能包含 / 或 \\"; // biome-ignore lint/suspicious/noControlCharactersInRegex: 过滤控制字符 if (/[\u0000-\u001f]/.test(n)) return "名称不能包含控制字符"; - if (n.length > 255) return "名称过长"; + if (n.length > 255) return "名称不能超过 255 个字符"; return null; } @@ -318,7 +318,7 @@ export function deleteBlockText(b: string | null): { text: string; soft: boolean case "needs_unbind": return { text: "是作品或季的绑定目录,勾选「解除路径绑定」后才能删除", soft: true }; case "root_unavailable": - return { text: "目录离线,不能确认文件状态", soft: false }; + return { text: "目录离线,无法确认文件状态", soft: false }; case "not_found": return { text: "文件已不存在", soft: false }; case "escape": @@ -557,7 +557,7 @@ const REMOTE_PENDING_WARNING = /^远端候选在后台查询/; export function applyIdentifyCompleted(r: FilesIdentifyResult, p: IdentifyCompletedPayload): FilesIdentifyResult { if (!r.remoteTaskId || p.taskId !== r.remoteTaskId) return r; if (p.status === "failed") { - const note = `远端查询失败,只有本地结果${p.error ? `:${p.error.slice(0, 200)}` : ""}`; + const note = `TMDB 查询失败,只显示本地结果${p.error ? `:${p.error.slice(0, 200)}` : ""}`; return { ...r, remoteTaskId: null, diff --git a/apps/web/app/components/files/shortcuts-dialog.tsx b/apps/web/app/components/files/shortcuts-dialog.tsx index 2d845dd7..3a19a7a8 100644 --- a/apps/web/app/components/files/shortcuts-dialog.tsx +++ b/apps/web/app/components/files/shortcuts-dialog.tsx @@ -9,11 +9,11 @@ const KEYS: [string, string][] = [ ["Ctrl/⌘+A", "全选"], ["Ctrl/⌘+Enter", "整理选中项"], ["F2", "重命名"], - ["Del", "删除(先进回收站)"], + ["Del", "删除(移入回收站)"], ["/", "搜索当前目录"], ["Esc", "清除选择"], ["Shift+F10 / 菜单键", "打开操作菜单"], - ["拖到目录", "移动到(按住 Alt 复制),先出预览"], + ["拖到目录", "移动(按住 Alt 复制),先预览"], ["?", "显示这张表"], ];