diff --git a/client/src/api/character.ts b/client/src/api/character.ts index e59a4fe05..3fd04737f 100644 --- a/client/src/api/character.ts +++ b/client/src/api/character.ts @@ -4,7 +4,7 @@ import type { BaseCharacter } from "@ai-novel/shared/types/novel"; import { apiClient } from "./client"; export interface CharacterGenerateConstraints { - storyFunction?: "主角" | "反派" | "导师" | "对照组" | "配角"; + storyFunction?: "main character" | "Villain" | "tutor" | "control group" | "supporting role"; externalGoal?: string; internalNeed?: string; coreFear?: string; @@ -12,7 +12,7 @@ export interface CharacterGenerateConstraints { secret?: string; coreFlaw?: string; relationshipHooks?: string; - growthStage?: "起点" | "受挫" | "转折" | "觉醒" | "收束"; + growthStage?: "starting point" | "frustrated" | "turning point" | "Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know." | "Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know."; toneStyle?: string; } diff --git a/client/src/api/client.ts b/client/src/api/client.ts index 4abdd0890..a8ed6dd30 100644 --- a/client/src/api/client.ts +++ b/client/src/api/client.ts @@ -31,19 +31,19 @@ apiClient.interceptors.response.use( const backendError = error.response?.data?.error; const backendMessage = error.response?.data?.message; const silentErrorStatuses = error.config?.silentErrorStatuses ?? []; - let title = backendError ?? error.message ?? "请求失败。"; + let title = backendError ?? error.message ?? "The request failed."; let description = backendMessage && backendMessage !== backendError ? backendMessage : undefined; if (!status) { - title = "网络连接失败,请检查网络后重试。"; + title = "Network connection failed, please check the network and try again."; description = undefined; } else if (status >= 500) { - title = backendError ?? "服务器错误,请稍后重试。"; + title = backendError ?? "Server error, please try again later."; description = backendMessage && backendMessage !== title ? backendMessage : undefined; } if (!status || !silentErrorStatuses.includes(status)) { - const isGenericServerErrorToast = title === "服务器错误,请稍后重试。"; + const isGenericServerErrorToast = title === "Server error, please try again later."; if (description) { toast.error( diff --git a/client/src/api/creativeHub.ts b/client/src/api/creativeHub.ts index ee6a01ecd..cf42223b2 100644 --- a/client/src/api/creativeHub.ts +++ b/client/src/api/creativeHub.ts @@ -12,7 +12,7 @@ import { apiClient } from "./client"; function ensureThreadId(threadId: string): string { const normalized = threadId.trim(); if (!normalized) { - throw new Error("创作中枢线程不存在,请先创建线程。"); + throw new Error("The creation hub thread does not exist, please create a thread first."); } return normalized; } diff --git a/client/src/api/drama.ts b/client/src/api/drama.ts index dd6b281c0..5ab38d5d3 100644 --- a/client/src/api/drama.ts +++ b/client/src/api/drama.ts @@ -570,18 +570,7 @@ export async function downloadDramaEpisodeExport(id: string, order: number, form // ───────────────────────────────────────────────────────────────────────────── export async function getDramaCharacterImageStatus(id: string, characterId: string) { - const { data } = await apiClient.get>( - `/drama/projects/${id}/characters/${characterId}/image-status`, - ); - return data; -} - -/** 生成角色设计稿(面部特写 + 三视图合图,推荐使用) */ -export async function prepareDramaCharacterSheet( - id: string, - characterId: string, - provider?: string, -): Promise> { + const { data } = await apiClient.get>( `/drama/projects/${id}/characters/${characterId}/image-status`, ); return data; } /** Generate character design drafts (facial close-up + three-view composite image, recommended) */ export async function prepareDramaCharacterSheet( id: string, characterId: string, provider?: string, ): Promise> { const { data } = await apiClient.post>( `/drama/projects/${id}/characters/${characterId}/prepare-character-sheet`, provider ? { provider } : {}, diff --git a/client/src/api/novel/production.ts b/client/src/api/novel/production.ts index 04eda36f1..16a85fbff 100644 --- a/client/src/api/novel/production.ts +++ b/client/src/api/novel/production.ts @@ -87,18 +87,7 @@ export async function deleteCreativeDecision(id: string, decisionId: string) { } export async function batchInvalidateCreativeDecisions(id: string, decisionIds: string[]) { - const { data } = await apiClient.post>( - `/novels/${id}/creative-decisions/batch-invalidate`, - { decisionIds }, - ); - return data; -} - -/** - * [开发工具] 重置指定小说的所有章节正文及相关生成数据,供测试重跑使用。 - * 清除范围:章节正文、生成状态、事实账本、章节摘要、质量报告等。 - */ -export async function devResetNovelChapters(id: string): Promise<{ resetCount: number }> { + const { data } = await apiClient.post>( `/novels/${id}/creative-decisions/batch-invalidate`, { decisionIds }, ); return data; } /** * [Development Tools] Resets all chapter text and related generated data for the specified novel for testing reruns. * Scope of clearing: chapter text, generation status, fact ledger, chapter summary, quality report, etc. */ export async function devResetNovelChapters(id: string): Promise<{ resetCount: number }> { const { data } = await apiClient.post>( `/novels/${id}/dev/reset-chapters`, {}, diff --git a/client/src/components/assetLibrary/AssetLibrarySection.tsx b/client/src/components/assetLibrary/AssetLibrarySection.tsx index 077afd413..35be21f5a 100644 --- a/client/src/components/assetLibrary/AssetLibrarySection.tsx +++ b/client/src/components/assetLibrary/AssetLibrarySection.tsx @@ -21,7 +21,7 @@ export function AssetLibraryRecommendation(props: { const Icon = props.icon; return (
= { export default function AssetLibraryStatusGrid(props: { items: AssetLibraryStatusItem[] }) { return ( -
+
{props.items.map((item) => { const Icon = item.icon; return ( diff --git a/client/src/components/autoDirector/AICockpit.tsx b/client/src/components/autoDirector/AICockpit.tsx index 72124ca74..5f9e988d6 100644 --- a/client/src/components/autoDirector/AICockpit.tsx +++ b/client/src/components/autoDirector/AICockpit.tsx @@ -36,12 +36,12 @@ export interface AICockpitProps { function displayStateLabel(state: DirectorBookAutomationDisplayState): string { const labels: Record = { - processing: "AI 正在处理", - needs_confirmation: "等你确认", - paused: "已暂停", - needs_attention: "出错需处理", - completed: "已完成", - idle: "未开启", + processing: "AI is processing", + needs_confirmation: "Waiting for your confirmation", + paused: "Suspended", + needs_attention: "Error needs to be handled", + completed: "Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know.", + idle: "Not turned on", }; return labels[state]; } @@ -137,11 +137,11 @@ function stateSoftSurfaceClassName(state: DirectorBookAutomationDisplayState): s function formatDate(value: string | null | undefined): string { if (!value) { - return "暂无"; + return "None yet"; } const date = new Date(value); if (Number.isNaN(date.getTime())) { - return "暂无"; + return "None yet"; } return date.toLocaleString(); } @@ -157,7 +157,7 @@ function formatDuration(value: number | null | undefined): string | null { } const seconds = Math.round(value / 1000); if (seconds <= 0) { - return "<1 秒"; + return "<1 second"; } if (seconds < 60) { return `${seconds} 秒`; @@ -185,7 +185,7 @@ function formatUsageLine(usage: { } function fallbackProjectionReason(props: Pick): string { - return props.fallbackSummary?.trim() || "没有需要你处理的 AI 自动推进任务。"; + return props.fallbackSummary?.trim() || "There are no AI auto-advance tasks for you to handle."; } function renderActionLabel( @@ -196,28 +196,28 @@ function renderActionLabel( displayState === "needs_confirmation" && (action.type === "continue" || action.type === "auto_execute_range") ) { - return "确认并继续"; + return "Confirm and continue"; } - return action.label || "继续处理"; + return action.label || "Continue processing"; } function artifactTypeLabel(type: string): string { const labels: Record = { - book_contract: "书级约定", - story_macro: "故事规划", - character_cast: "角色", - volume_strategy: "分卷", - chapter_task_sheet: "任务单", - chapter_draft: "正文", - audit_report: "审校", - repair_ticket: "修复", - reader_promise: "读者承诺", - character_governance_state: "角色状态", - world_skeleton: "世界框架", - source_knowledge_pack: "资料包", - chapter_retention_contract: "留存约定", - continuity_state: "连续性", - rolling_window_review: "近期复盘", + book_contract: "book level agreement", + story_macro: "story planning", + character_cast: "Role", + volume_strategy: "Divide into volumes", + chapter_task_sheet: "Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know.", + chapter_draft: "text", + audit_report: "Review", + repair_ticket: "repair", + reader_promise: "reader commitment", + character_governance_state: "character status", + world_skeleton: "world frame", + source_knowledge_pack: "Information package", + chapter_retention_contract: "retention agreement", + continuity_state: "continuity", + rolling_window_review: "Recent review", }; return labels[type] ?? type; } @@ -226,11 +226,11 @@ function recoveryActionLabel( action: NonNullable["recoveryAction"], ): string | null { const labels: Record = { - retry: "重试当前步骤", - resume_after_review: "查看原因后继续", - switch_model: "切换模型后继续", - confirm_protected_content: "确认保护内容边界", - manual_repair: "先处理章节问题", + retry: "Retry current step", + resume_after_review: "Continue after checking the reason", + switch_model: "Continue after switching models", + confirm_protected_content: "Confirm protected content boundaries", + manual_repair: "Deal with chapter issues first", }; return action ? labels[action] ?? null : null; } @@ -239,16 +239,16 @@ function workerStateLabel( state: NonNullable["derivedState"], ): string { const labels: Record["derivedState"], string> = { - idle: "未运行", - queued_waiting_worker: "等待接手", - leased_starting: "正在接手", - running_step: "自动推进中", - waiting_gate: "等待确认", - auto_recovering: "恢复中", - cancelled: "已停止", - failed_recoverable: "等待恢复", - failed_hard: "需要处理", - succeeded: "已完成", + idle: "Not running", + queued_waiting_worker: "Waiting to take over", + leased_starting: "Taking over", + running_step: "Automatically advancing", + waiting_gate: "Waiting for confirmation", + auto_recovering: "Recovering", + cancelled: "Stopped", + failed_recoverable: "Waiting for recovery", + failed_hard: "Need to be processed", + succeeded: "Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know.", }; return labels[state] ?? state; } @@ -258,15 +258,15 @@ function workerStateDetail(health: NonNullable 0) { - return "任务已排队,后台执行接手后会继续推进。"; + return "The task has been queued and will continue to advance after the background execution takes over."; } if (health.runningCommandCount > 0 || health.leasedCommandCount > 0) { - return "后台执行正在处理当前任务。"; + return "Background execution is processing the current task."; } if (health.staleCommandCount > 0) { - return "后台执行中断后会从最近进度尝试恢复。"; + return "After background execution is interrupted, it will try to resume from the latest progress."; } - return "当前没有正在排队或执行的后台动作。"; + return "There are currently no background actions queued or executed."; } function SummaryMetric(props: { @@ -330,16 +330,16 @@ export default function AICockpit(props: AICockpitProps) {
{stateIcon("idle")}
-
AI 驾驶舱
+
AI Cockpit
{fallbackProjectionReason(props)}
- {fallbackStatusLabel ?? "未开启"} + {fallbackStatusLabel ?? "Not turned on"} {onOpenFallbackDetails ? ( + Check + ) : null} ); @@ -379,9 +379,9 @@ export default function AICockpit(props: AICockpitProps) { || focusProjection.headline?.trim() || displayStateLabel(focusProjection.displayState); const statusDetail = reason === statusHeadline - ? focusProjection.progressSummary?.trim() || "AI 会在这里汇总本书自动推进的最新状态。" + ? focusProjection.progressSummary?.trim() || "AI will summarize the latest status of the book's automatic advancement here." : reason; - const latestRecordText = recentItems[0] ? formatDate(recentItems[0].occurredAt) : "暂无"; + const latestRecordText = recentItems[0] ? formatDate(recentItems[0].occurredAt) : "None yet"; const handlePrimaryAction = () => { if (primaryAction && onAction) { @@ -414,7 +414,7 @@ export default function AICockpit(props: AICockpitProps) {
{stateIcon(focusProjection.displayState)}
-
AI 驾驶舱
+
AI Cockpit
{focusProjection.userHeadline || focusProjection.headline || reason}
@@ -425,8 +425,8 @@ export default function AICockpit(props: AICockpitProps) {
+ Check +
); } @@ -453,34 +453,34 @@ export default function AICockpit(props: AICockpitProps) {
- - + +
-
下一步
+
Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know.
- {focusProjection.nextActionLabel || "打开小说查看当前内容"} + {focusProjection.nextActionLabel || "Open the novel to view the current content"}
{canOpenDetails ? ( + Execution details + ) : null}
@@ -488,9 +488,9 @@ export default function AICockpit(props: AICockpitProps) { {circuitBreaker ? (
-
自动推进已暂停
-
{circuitBreaker.message || "系统检测到继续自动推进可能反复失败。"}
- {circuitRecovery ?
建议:{circuitRecovery}。
: null} +
Auto-advance paused
+
{circuitBreaker.message || "The system has detected that continued automatic advancement may fail repeatedly."}
+ {circuitRecovery ?
suggestion:{circuitRecovery}。
: null}
) : null} @@ -499,20 +499,20 @@ export default function AICockpit(props: AICockpitProps) {
- 后台执行 -
+ background execution +
{workerStateLabel(workerHealth.derivedState)}
{workerStateDetail(workerHealth)}
- - - - + + + +
{workerHealth.oldestQueuedWaitMs ? (
- 等待接手 {formatDuration(workerHealth.oldestQueuedWaitMs) ?? "<1 秒"} + Waiting to take over {formatDuration(workerHealth.oldestQueuedWaitMs) ?? "<1 second"}
) : null}
@@ -523,8 +523,8 @@ export default function AICockpit(props: AICockpitProps) {
- 产物记录 -
+ product record +
{artifactInsightLines.length > 0 ? ( {artifactInsightLines[0]} ) : null} @@ -549,7 +549,7 @@ export default function AICockpit(props: AICockpitProps) { {usageSummary ? ( } > @@ -557,7 +557,7 @@ export default function AICockpit(props: AICockpitProps) {
{formatUsageLine(usageSummary)}
{promptUsage.length > 0 ? (
-
阶段用量
+
Phase dosage
{promptUsage.map((item) => (
@@ -572,7 +572,7 @@ export default function AICockpit(props: AICockpitProps) { ) : null} {stepUsage.length > 0 ? (
-
推进步骤
+
Advance steps
{stepUsage.map((item) => (
@@ -591,7 +591,7 @@ export default function AICockpit(props: AICockpitProps) { {recentItems.length > 0 ? ( } > @@ -602,7 +602,7 @@ export default function AICockpit(props: AICockpitProps) { {item.usage ? (
{formatUsageLine(item.usage)}
) : item.durationMs ? ( -
耗时 {formatDuration(item.durationMs)}
+
time consuming {formatDuration(item.durationMs)}
) : null}
{formatDate(item.occurredAt)}
diff --git a/client/src/components/autoDirector/AutoDirectorApprovalPointMultiSelect.tsx b/client/src/components/autoDirector/AutoDirectorApprovalPointMultiSelect.tsx index b4afaacdc..c217c2e51 100644 --- a/client/src/components/autoDirector/AutoDirectorApprovalPointMultiSelect.tsx +++ b/client/src/components/autoDirector/AutoDirectorApprovalPointMultiSelect.tsx @@ -17,9 +17,9 @@ interface AutoDirectorApprovalPointMultiSelectProps { } function riskLabel(riskLevel: DirectorAutoApprovalPoint["riskLevel"]): string { - if (riskLevel === "high") return "高风险"; - if (riskLevel === "medium") return "中风险"; - return "低风险"; + if (riskLevel === "high") return "high risk"; + if (riskLevel === "medium") return "Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know."; + return "Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know."; } function riskClassName(riskLevel: DirectorAutoApprovalPoint["riskLevel"]): string { @@ -41,7 +41,7 @@ function toggleCodes(current: string[], targetCodes: string[], checked: boolean) export function summarizeDirectorAutoApprovalPoints(codes: string[]): string { const normalized = normalizeDirectorAutoApprovalPointCodes(codes, []); if (normalized.length === 0) { - return "不会自动通过审批点"; + return "Will not automatically pass the approval point"; } const labels: string[] = normalized .map((code) => DIRECTOR_AUTO_APPROVAL_POINTS.find((item) => item.code === code)?.label) @@ -49,7 +49,7 @@ export function summarizeDirectorAutoApprovalPoints(codes: string[]): string { if (labels.length <= 2) { return labels.join("、"); } - return `${labels.slice(0, 2).join("、")} 等 ${labels.length} 项`; + return `${labels.slice(0, 2).join("、")} and ${labels.length} items`; } export default function AutoDirectorApprovalPointMultiSelect({ diff --git a/client/src/components/autoDirector/AutoDirectorApprovalStrategyPanel.tsx b/client/src/components/autoDirector/AutoDirectorApprovalStrategyPanel.tsx index ce9a29816..a557c0732 100644 --- a/client/src/components/autoDirector/AutoDirectorApprovalStrategyPanel.tsx +++ b/client/src/components/autoDirector/AutoDirectorApprovalStrategyPanel.tsx @@ -26,7 +26,7 @@ export default function AutoDirectorApprovalStrategyPanel({ }: AutoDirectorApprovalStrategyPanelProps) { return (
-
自动推进方式
+
Automatic propulsion method
{enabled - ? "自动推进:系统会在目标范围内自动确认规划、章节执行、质量修复和必要重规划。" + ? "Automatic advancement: The system will automatically confirm planning, chapter execution, quality repair and necessary re-planning within the target range." : `副驾确认边界:${summarizeDirectorAutoApprovalPoints(approvalPointCodes)}。未包含的审批点会等待你确认。`}
{!enabled ? (
- 高级审批授权 - + Advanced approval authorization +
- 项目导航 - + Project navigation + ) : null}
); diff --git a/client/src/components/autoDirector/DirectorRuntimeProjectionCard.tsx b/client/src/components/autoDirector/DirectorRuntimeProjectionCard.tsx index 436a13397..f55b97c9c 100644 --- a/client/src/components/autoDirector/DirectorRuntimeProjectionCard.tsx +++ b/client/src/components/autoDirector/DirectorRuntimeProjectionCard.tsx @@ -23,11 +23,11 @@ interface DirectorRuntimeProjectionCardProps { function formatDate(value: string | null | undefined): string { if (!value) { - return "暂无"; + return "None yet"; } const date = new Date(value); if (Number.isNaN(date.getTime())) { - return "暂无"; + return "None yet"; } return date.toLocaleString(); } @@ -43,84 +43,14 @@ function formatDuration(value: number | null | undefined): string | null { } const seconds = Math.round(value / 1000); if (seconds <= 0) { - return "<1 秒"; + return "<1 second"; } if (seconds < 60) { - return `${seconds} 秒`; + return `${seconds} seconds`; } const minutes = Math.floor(seconds / 60); const restSeconds = seconds % 60; - return restSeconds > 0 ? `${minutes} 分 ${restSeconds} 秒` : `${minutes} 分`; -} - -function formatUsageLine(usage: { - llmCallCount: number; - promptTokens: number; - completionTokens: number; - totalTokens: number; - durationMs?: number | null; -}): string { - const duration = formatDuration(usage.durationMs); - return [ - `${formatTokenCount(usage.llmCallCount)} 次调用`, - `输入 ${formatTokenCount(usage.promptTokens)}`, - `输出 ${formatTokenCount(usage.completionTokens)}`, - `总计 ${formatTokenCount(usage.totalTokens)} Tokens`, - duration ? `累计调用耗时 ${duration}` : null, - ].filter(Boolean).join(" · "); -} - -function formatPolicyMode(mode: DirectorPolicyMode): string { - if (mode === "suggest_only") { - return "只给建议"; - } - if (mode === "run_next_step") { - return "推进下一步"; - } - if (mode === "auto_safe_scope") { - return "安全范围自动推进"; - } - return "推进到检查点"; -} - -function formatStatus(status: DirectorRuntimeProjectionStatus): string { - if (status === "running") { - return "推进中"; - } - if (status === "waiting_approval") { - return "等待确认"; - } - if (status === "blocked") { - return "已暂停"; - } - if (status === "failed") { - return "失败"; - } - if (status === "completed") { - return "已完成"; - } - return "待开始"; -} - -function statusClassName(status: DirectorRuntimeProjectionStatus): string { - if (status === "running") { - return "border-sky-300 bg-sky-50 text-sky-900"; - } - if (status === "waiting_approval") { - return "border-amber-300 bg-amber-50 text-amber-900"; - } - if (status === "blocked" || status === "failed") { - return "border-destructive/30 bg-destructive/5 text-destructive"; - } - if (status === "completed") { - return "border-emerald-300 bg-emerald-50 text-emerald-900"; - } - return "border-border bg-muted/30 text-muted-foreground"; -} - -function statusIcon(status: DirectorRuntimeProjectionStatus) { - if (status === "running") { - return ; + return restSeconds > 0 ? `${minutes} minutes ${restSeconds} seconds` : `${minutes} minutes`; } function formatUsageLine(usage: { llmCallCount: number; promptTokens: number; completionTokens: number; totalTokens: number; durationMs?: number | null; }): string { const duration = formatDuration(usage.durationMs); return [ `${formatTokenCount(usage.llmCallCount)} times`, `input ${formatTokenCount(usage.promptTokens)}`, `output ${formatTokenCount(usage.completionTokens)}`, `total ${formatTokenCount(usage.totalTokens)} Tokens`, duration ? `cumulative call time ${duration}` : null, ].filter(Boolean).join(" · "); } function formatPolicyMode(mode: DirectorPolicyMode): string { if (mode === "suggest_only") { return "Only suggestions are given"; } if (mode === "run_next_step") { return "Proceed to the next step"; } if (mode === "auto_safe_scope") { return "Automatically proceed within safe scope"; } return "Proceeded to checkpoint"; } function formatStatus(status: DirectorRuntimeProjectionStatus): string { if (status === "running") { return "Proceeding"; } if (status === "waiting_approval") { return "Waiting for confirmation"; } if (status === "blocked") { return "Paused"; } if (status === "failed") { return "Failed"; } if (status === "completed") { return "Completed"; } return "Pending start"; } function statusClassName(status: DirectorRuntimeProjectionStatus): string { if (status === "running") { return "border-sky-300 bg-sky-50 text-sky-900"; } if (status === "waiting_approval") { return "border-amber-300 bg-amber-50 text-amber-900"; } if (status === "blocked" || status === "failed") { return "border-destructive/30 bg-destructive/5 text-destructive"; } if (status === "completed") { return "border-emerald-300 bg-emerald-50 text-emerald-900"; } return "border-border bg-muted/30 text-muted-foreground"; } function statusIcon(status: DirectorRuntimeProjectionStatus) { if (status === "running") { return ; } if (status === "waiting_approval") { return ; @@ -163,7 +93,7 @@ function formatQualityBudgetSummary(summary: DirectorRuntimeProjection["qualityB } const chapterText = typeof summary.currentChapterOrder === "number" ? `第 ${summary.currentChapterOrder} 章` - : "当前章节"; + : "Current Chapter"; return `${chapterText}质量预算:局部修复 ${summary.patchRepairUsed}/1,整章重写 ${summary.chapterRewriteUsed}/1,窗口重规划 ${summary.windowReplanUsed}/1。${summary.nextActionLabel}`; } @@ -172,15 +102,15 @@ function formatRootCauseSummary(projection: DirectorRuntimeProjection): string | return null; } if (projection.rootCauseCode === "replan_required") { - return "当前问题来自章节职责失配,系统需要先调整附近章节安排。"; + return "The current problem comes from the mismatch of chapter responsibilities, and the system needs to adjust the arrangement of nearby chapters first."; } if (projection.rootCauseCode === "draft_obligation_unmet") { - return "正文已经生成,但仍有本章必须完成的内容没有兑现。"; + return "The main text has been generated, but there are still things that must be completed in this chapter that have not been fulfilled."; } if (projection.rootCauseCode === "draft_repair_exhausted") { - return "正文已经生成,但自动修复后仍有阻塞问题需要继续处理。"; + return "The main text has been generated, but there are still blocking issues that need to be dealt with after automatic repair."; } - return "正文没有成功生成,需要重新执行当前章节。"; + return "The main text was not successfully generated and the current chapter needs to be re-executed."; } function formatPercent(value: number | null | undefined): string { @@ -201,13 +131,13 @@ export default function DirectorRuntimeProjectionCard({ const primaryText = projection.headline?.trim() || projection.currentLabel?.trim() || projection.lastEventSummary?.trim() - || "等待同步当前推进状态"; + || "Waiting for synchronization of current advancement status"; const detailText = projection.detail?.trim(); const attentionText = projection.requiresUserAction ? projection.blockingReason?.trim() || projection.blockedReason?.trim() || projection.lastEventSummary?.trim() - || "请先处理当前停留点。" + || "Please process the current stop first." : projection.blockingReason?.trim() || projection.blockedReason?.trim(); const progressLine = projection.progressBreakdown?.explanation?.trim() || projection.progressSummary?.trim() @@ -221,7 +151,7 @@ export default function DirectorRuntimeProjectionCard({ const activeExecutionLine = projection.activeExecution ? `后台执行:${getDirectorNodeDisplayLabel({ nodeKey: projection.activeExecution.stepType, - fallback: projection.currentAction || "自动导演任务", + fallback: projection.currentAction || "Automatic director tasks", })}${projection.activeExecution.resourceClass ? ` · ${projection.activeExecution.resourceClass}` : ""}` : null; const waitingLine = projection.waitingReason ? `等待原因:${projection.waitingReason}` : null; @@ -238,7 +168,7 @@ export default function DirectorRuntimeProjectionCard({ workerHealthLine, projection.nextActionLabel ? `下一步:${projection.nextActionLabel}` : null, projection.recommendedAction?.reason ? `推荐原因:${projection.recommendedAction.reason}` : null, - projection.isAutopilotRecoverable ? "AI 可以从当前进度继续处理。" : null, + projection.isAutopilotRecoverable ? "The AI ​​can continue processing from its current progress." : null, rootCauseLine, obligationLine, qualityBudgetLine, @@ -259,7 +189,7 @@ export default function DirectorRuntimeProjectionCard({
{statusIcon(projection.status)}
-
导演进度
+
Director's progress
{primaryText}
@@ -281,19 +211,19 @@ export default function DirectorRuntimeProjectionCard({ {progressBreakdown && !compact ? (
-
规划
+
planning
{formatPercent(progressBreakdown.planningProgress ?? progressBreakdown.planningPercent)}
-
章节
+
chapter
{progressBreakdown.continuableChapters}/{progressBreakdown.totalChapters}
-
质量
+
quality
{formatPercent(progressBreakdown.qualityProgress ?? progressBreakdown.qualityRepairPercent)}
-
当前动作
+
Current action
{formatPercent(progressBreakdown.activeJobProgress)}
@@ -301,7 +231,7 @@ export default function DirectorRuntimeProjectionCard({ {attentionText ? (
- {projection.requiresUserAction ? "需要你处理:" : "暂停原因:"}{attentionText} + {projection.requiresUserAction ? "You need to handle:" : "Reason for suspension:"}{attentionText}
) : null} @@ -323,11 +253,11 @@ export default function DirectorRuntimeProjectionCard({ {usageSummary ? (
-
AI 用量
+
AI usage
{formatUsageLine(usageSummary)}
{promptUsage.length > 0 && !compact ? (
-
阶段用量
+
Phase dosage
{promptUsage.map((item) => (
@@ -340,7 +270,7 @@ export default function DirectorRuntimeProjectionCard({ ) : null} {stepUsage.length > 0 && !compact ? (
-
推进步骤
+
Advance steps
{stepUsage.map((item) => (
@@ -355,13 +285,13 @@ export default function DirectorRuntimeProjectionCard({ ) : null}
- 推进方式:{formatPolicyMode(projection.policyMode)} - 更新时间:{formatDate(projection.updatedAt)} + Promotion method:{formatPolicyMode(projection.policyMode)} + Update time:{formatDate(projection.updatedAt)}
{recentEvents.length > 0 && !compact ? (
-
最近进展
+
Recent Progress
{recentEvents.map((event) => (
{event.summary}
diff --git a/client/src/components/characterConversation/CharacterConversationWorkbench.tsx b/client/src/components/characterConversation/CharacterConversationWorkbench.tsx index 7a7b40be7..9399e9c34 100644 --- a/client/src/components/characterConversation/CharacterConversationWorkbench.tsx +++ b/client/src/components/characterConversation/CharacterConversationWorkbench.tsx @@ -92,10 +92,10 @@ export default function CharacterConversationWorkbench(props: CharacterConversat {displayName} 的对话空间} + title={{displayName} dialogue space} description={projection?.sourceDescription ?? sourceDescriptionForPolicy(policy)} meta={{projection?.sourceLabel ?? sourceLabelForPolicy(policy)} · {policyLabel(policy)}} - actions={<>{props.headerActions}{props.chapterAnchorOptions?.length && typeof props.chapterAnchor === "number" && props.onChapterAnchorChange ? : null}{props.onClose ? : null}} + actions={<>{props.headerActions}{props.chapterAnchorOptions?.length && typeof props.chapterAnchor === "number" && props.onChapterAnchorChange ? : null}{props.onClose ? : null}} className="rounded-xl border-border/50 shadow-none" headerClassName="bg-background px-5 py-4 xl:px-6" bodyClassName={cn( @@ -141,29 +141,29 @@ export default function CharacterConversationWorkbench(props: CharacterConversat } function ChapterAnchorSelect(props: { chapterAnchor: number; options: number[]; disabled: boolean; onChange: (chapterAnchor: number) => void }) { - return ; + return ; } function ConversationContextPanel(props: { projection: CharacterSubjectProjection | undefined }) { if (!props.projection) { - return ; + return ; } const { projection } = props; return ( ); } @@ -175,17 +175,17 @@ function policyForSubject(kind: CharacterSubjectRef["kind"]): CharacterConversat } function policyLabel(policy: CharacterConversationPolicy) { - return policy === "novel_influence" ? "可带入创作" : policy === "evidence_interview" ? "证据访谈" : "只读访谈"; + return policy === "novel_influence" ? "Can be brought into creation" : policy === "evidence_interview" ? "evidence interview" : "read-only interview"; } function sourceLabelForPolicy(policy: CharacterConversationPolicy) { - return policy === "novel_influence" ? "小说角色" : policy === "evidence_interview" ? "拆书角色档案" : "基础角色库"; + return policy === "novel_influence" ? "novel character" : policy === "evidence_interview" ? "Open book character files" : "Basic character library"; } function sourceDescriptionForPolicy(policy: CharacterConversationPolicy) { return policy === "novel_influence" - ? "角色会结合当前小说处境回应;只有你确认后,谈话倾向才会带入后续创作。" + ? "The character will respond based on the current novel situation; only after you confirm it, the conversational tendencies will be carried into subsequent creations." : policy === "evidence_interview" - ? "角色只依据选定章节范围内的原文证据回应,不会改写原文。" - : "角色依据角色库中的稳定设定回应;交流只用于理解人物,不会改写设定。"; + ? "The character will only respond based on the original text evidence within the selected chapter range, and will not rewrite the original text." + : "Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know."; } diff --git a/client/src/components/comic/GeneratedImageCard.tsx b/client/src/components/comic/GeneratedImageCard.tsx index e9a8629f0..aa6929202 100644 --- a/client/src/components/comic/GeneratedImageCard.tsx +++ b/client/src/components/comic/GeneratedImageCard.tsx @@ -11,10 +11,10 @@ const STATUS_DOT: Record = { }; const STATUS_TITLE: Record = { - idle: "未生成", - generating: "生成中", - done: "已就绪", - error: "生成失败", + idle: "Error 500 (Server Error)!!1500.That’s an error.There was an error. Please try again later.That’s all we know.", + generating: "Generating", + done: "Ready", + error: "Build failed", }; const SIZE_STYLE: Record, string> = { @@ -112,7 +112,7 @@ export function GeneratedImageCard({ {onDelete && (
@@ -179,13 +179,13 @@ export function GeneratedImageCard({ onClick={onGenerate} > {isGenerating ? : } - {generateLabel ?? (hasDoneImage ? "重新生成" : "AI 生图")} + {generateLabel ?? (hasDoneImage ? "Regenerate" : "AI generated pictures")} )} {onUpload && ( + Optimize selected content only +
{preview.trim() ? (
-
优化预览
+
Optimize preview
                 {preview}
               
- - + +
) : null} diff --git a/client/src/components/common/FullscreenView.tsx b/client/src/components/common/FullscreenView.tsx index ea76ec6aa..d8977d5df 100644 --- a/client/src/components/common/FullscreenView.tsx +++ b/client/src/components/common/FullscreenView.tsx @@ -37,8 +37,8 @@ export default function FullscreenView(props: FullscreenViewProps) { headerClassName, bodyClassName, fullscreenBodyClassName, - toggleLabel = "全屏查看", - exitLabel = "退出全屏", + toggleLabel = "View full screen", + exitLabel = "Exit full screen", } = props; const headingId = useId(); const [internalFullscreen, setInternalFullscreen] = useState(defaultFullscreen); diff --git a/client/src/components/common/LLMSelector.tsx b/client/src/components/common/LLMSelector.tsx index f87725996..517b94dc8 100644 --- a/client/src/components/common/LLMSelector.tsx +++ b/client/src/components/common/LLMSelector.tsx @@ -268,20 +268,20 @@ export default function LLMSelector({ return (
- {showBadge ? 模型 : null} + {showBadge ? Model : null}
diff --git a/client/src/components/creativeHub/OpenInCreativeHubButton.tsx b/client/src/components/creativeHub/OpenInCreativeHubButton.tsx index 5fad5bfa0..7ea1ea043 100644 --- a/client/src/components/creativeHub/OpenInCreativeHubButton.tsx +++ b/client/src/components/creativeHub/OpenInCreativeHubButton.tsx @@ -8,18 +8,7 @@ interface OpenInCreativeHubButtonProps { bindings: CreativeHubResourceBinding; label?: string; variant?: ComponentProps["variant"]; - size?: ComponentProps["size"]; - className?: string; -} - -export default function OpenInCreativeHubButton({ - bindings, - label = "在创作中枢中继续", - variant = "outline", - size = "sm", - className, -}: OpenInCreativeHubButtonProps) { - return ( + size?: ComponentProps["size"]; className?: string; } export default function OpenInCreativeHubButton({ bindings, label = "Continue in the Creation Hub", variant = "outline", size = "sm", className, }: OpenInCreativeHubButtonProps) { return ( diff --git a/client/src/components/image/ImageGenerationConfirmDialog.tsx b/client/src/components/image/ImageGenerationConfirmDialog.tsx index 31ce23b64..b60afa0ec 100644 --- a/client/src/components/image/ImageGenerationConfirmDialog.tsx +++ b/client/src/components/image/ImageGenerationConfirmDialog.tsx @@ -18,18 +18,18 @@ import { toast } from "@/components/ui/toast"; import SelectControl from "@/components/common/SelectControl"; const SIZE_OPTIONS = [ - { value: "1024x1024", label: "1024×1024(方形 1:1)" }, - { value: "1024x1536", label: "1024×1536(竖版 2:3,漫画/角色)" }, - { value: "1536x1024", label: "1536×1024(横版 3:2,三视图/表情稿)" }, + { value: "1024x1024", label: "1024×1024 (square 1:1)" }, + { value: "1024x1536", label: "1024×1536 (vertical 2:3, comic/character)" }, + { value: "1536x1024", label: "1536×1024 (Landscape 3:2, Three-view drawing/Emoji)" }, ]; const REF_KIND_LABEL: Record = { - character_sheet: "三视图", - character_expression: "表情稿", - character_face: "面部裁剪", - book_analysis_character_base: "基础形象", - asset: "资产", - scene: "场景", + character_sheet: "Three views", + character_expression: "Expressions", + character_face: "face cropping", + book_analysis_character_base: "basic image", + asset: "assets", + scene: "Scene", }; const REF_KIND_COLOR: Record = { @@ -166,16 +166,16 @@ export function ImageGenerationConfirmDialog({ })), }); if (!response.data) { - throw new Error("没有收到 Prompt 处理结果。"); + throw new Error("No prompt processing result was received."); } if (action === "optimize" && response.data.optimizedPrompt?.trim()) { setPrompt(response.data.optimizedPrompt.trim()); } setPromptAssistResult(response.data); } catch (error) { - const message = error instanceof Error ? error.message : "Prompt 处理失败。"; + const message = error instanceof Error ? error.message : "Prompt processing failed."; setPromptAssistError(message); - toast.error("Prompt 处理失败", { description: message }); + toast.error("Prompt processing failed", { description: message }); } finally { setPromptAssistLoading(null); } @@ -184,15 +184,15 @@ export function ImageGenerationConfirmDialog({ const footer = preview ? (

- {anyDirty ? "本次将使用上方修改后的参数生图(仅一次性,不保存到角色)" : "点击「开始生图」按当前参数生成"} + {anyDirty ? "This time, the modified parameters above will be used to generate the picture (one-time only, not saved to the character)" : "Click \"Start Generating Picture\" to generate according to the current parameters."}

+ Cancel +
@@ -201,7 +201,7 @@ export function ImageGenerationConfirmDialog({ return ( { if (!o) onCancel(); }}> - 正在准备生图素材... -
+ Preparing photo material... +
) : !preview ? ( -
无预览数据
+
No preview data
) : (
{/* 参考图素材 */}
- 参考素材 - + Reference material + {referenceImages.length}/{preview.referenceImages.length} {referenceDirty && ( @@ -231,18 +231,18 @@ export function ImageGenerationConfirmDialog({ onClick={() => setIncludedReferenceImageUrls(preview.referenceImages.map((ref) => ref.url))} disabled={submitting || !!promptAssistLoading} > - 恢复全部 - + Restore all + )}
{preview.referenceImages.length === 0 ? (
- 本次生图不附带参考图(纯文生图) -
+ This drawing does not come with reference pictures (pure text drawings) +
) : referenceImages.length === 0 ? (
- 本次生成不会发送参考图 -
+ Reference images will not be sent during this generation. +
) : (
{referenceImages.map((ref, i) => { @@ -250,27 +250,7 @@ export function ImageGenerationConfirmDialog({ const kindLabel = REF_KIND_LABEL[ref.kind] ?? ref.kind; return ( @@ -336,7 +316,7 @@ export function ImageGenerationConfirmDialog({ }} disabled={submitting || !!promptAssistLoading} /> -

{prompt.length} 字符 · 临时修改不会改动角色/项目设置

+

{prompt.length} Characters · Temporary modifications will not change character/item settings

优化要求

@@ -350,8 +330,8 @@ export function ImageGenerationConfirmDialog({ disabled={submitting || !!promptAssistLoading || !prompt.trim()} > {promptAssistLoading === "optimize" ? : } - 优化 Prompt - + Optimize Prompt + {optimizationInstruction && ( + Clear + )}
@@ -375,10 +355,10 @@ export function ImageGenerationConfirmDialog({ setOptimizationInstruction(e.target.value); clearPromptAssistResult(); }} - placeholder="例如:更像水彩、画面更温柔、保留服装和发型" + placeholder="For example: more like watercolor, softer picture, retaining clothing and hairstyle" disabled={submitting || !!promptAssistLoading} /> -

{optimizationInstruction.length} 字符 · 仅用于「优化 Prompt」

+

{optimizationInstruction.length} Character · Only used for "Optimize Prompt"

{(promptAssistResult || promptAssistError) && (
@@ -401,7 +381,7 @@ export function ImageGenerationConfirmDialog({ {promptAssistAction === "optimize" && promptAssistResult.changes.length > 0 && (
-

已调整

+

Adjusted

    {promptAssistResult.changes.map((item, index) => (
  • {item}
  • @@ -411,7 +391,7 @@ export function ImageGenerationConfirmDialog({ )} {promptAssistResult.risks.length > 0 && (
    -

    注意事项

    +

    Things to note

      {promptAssistResult.risks.map((item, index) => (
    • {item}
    • @@ -429,8 +409,8 @@ export function ImageGenerationConfirmDialog({

      - 负面 Prompt - {negativePromptDirty && 已修改} + Negative Prompt + {negativePromptDirty && Modified}

      {negativePromptDirty && ( + Restore default + )}