From 7bbe1704050bf90d2bd7146903f748ba19640b64 Mon Sep 17 00:00:00 2001 From: Astrenix <121576066+Astrenix@users.noreply.github.com> Date: Sun, 19 Jul 2026 03:12:39 +0800 Subject: [PATCH] feat(frontend): migrate batch image page to vue-i18n BatchImageGuideView carried ~140 user-visible strings outside vue-i18n: hardcoded Chinese template strings (buttons, placeholders, tooltips, table headers, dialog titles, empty states) plus an inline bilingual batchImageText dictionary switched by isZhLocale() in script. Move all of it into a new batchImage locale namespace (180 leaf keys per language, zh/en verified key-aligned) registered in both locale indexes, reusing 15 existing common.*/pagination.* keys. Column and filter-option definitions become computed so language switches take effect immediately; countable strings use vue-i18n plurals and the three rich-text counters keep their inner bold spans via slots. The Codex agent-instruction prompt template intentionally stays as a plain string: it is an AI-facing prompt document containing literal JSON braces that conflict with vue-i18n message syntax. Verified: eslint, vue-tsc --noEmit, vite build, and the full vitest suite (175 files / 1219 tests) all pass. Co-Authored-By: Claude Fable 5 --- frontend/src/i18n/locales/en/batchImage.ts | 212 ++++++++ frontend/src/i18n/locales/en/index.ts | 2 + frontend/src/i18n/locales/zh/batchImage.ts | 212 ++++++++ frontend/src/i18n/locales/zh/index.ts | 2 + .../src/views/user/BatchImageGuideView.vue | 468 +++++++----------- 5 files changed, 609 insertions(+), 287 deletions(-) create mode 100644 frontend/src/i18n/locales/en/batchImage.ts create mode 100644 frontend/src/i18n/locales/zh/batchImage.ts diff --git a/frontend/src/i18n/locales/en/batchImage.ts b/frontend/src/i18n/locales/en/batchImage.ts new file mode 100644 index 0000000000..d7b2274871 --- /dev/null +++ b/frontend/src/i18n/locales/en/batchImage.ts @@ -0,0 +1,212 @@ +export default { + batchImage: { + columns: { + taskName: 'Task name', + model: 'Model', + apiKey: 'API key', + result: 'Results', + cost: 'Cost', + downloadStatus: 'Download status', + }, + status: { + queued: 'Queued', + running: 'Generating', + processingResults: 'Processing results', + settling: 'Settling', + completed: 'Completed', + failed: 'Failed', + cancelled: 'Cancelled', + outputDeleted: 'Results deleted', + partialSuccess: 'Partially succeeded', + allFailed: 'All failed', + }, + itemStatus: { + pending: 'Queued', + succeeded: 'Succeeded', + failed: 'Failed', + cancelled: 'Cancelled', + recovered: 'Recovered by retry', + }, + filters: { + searchTaskName: 'Search task name', + allApiKeys: 'All API keys', + allStatuses: 'All statuses', + allDownloadStates: 'All download states', + downloaded: 'Downloaded', + notDownloaded: 'Not downloaded', + }, + actions: { + usageGuide: 'Usage guide', + createJob: 'Create batch job', + downloadSelected: 'Download selected', + deleteRecords: 'Delete records', + retryFailedItems: 'Retry failed items', + cancelJob: 'Cancel job', + downloadZip: 'Download ZIP', + viewDetail: 'View details', + download: 'Download', + moreActions: 'More actions', + copyInstruction: 'Copy instructions', + submitJob: 'Submit job', + }, + list: { + selectedJobs: 'Selected {count} job | Selected {count} jobs', + expandChildren: 'Expand {n} subtask | Expand {n} subtasks', + collapseChildren: 'Collapse subtasks', + childCount: '{n} subtask | {n} subtasks', + childBadge: 'Subtask', + keyNotRecorded: 'Not recorded', + totalCount: 'of {n}', + notDownloaded: 'Not downloaded', + empty: 'No batch jobs yet', + emptyHint: 'Use the button in the top-right corner to create a batch job.', + }, + pagination: { + pageNumber: 'Page {page}', + pageItems: '{count} on this page', + }, + promptPopover: { + title: 'Full prompt', + copied: 'Prompt copied', + }, + detail: { + title: 'Job details', + aggregatedResult: 'Combined results', + result: 'Results', + cost: 'Cost', + downloadStatus: 'Download status', + items: 'Items', + preview: 'Preview', + previewZoom: 'Zoom compressed preview {id}', + previewReload: 'Reload compressed preview', + previewLoad: 'Load compressed preview', + previewUnavailable: 'Preview unavailable', + noImage: 'No image', + loadingItems: 'Loading items...', + noItems: 'No items yet', + noItemsHint: 'Queued or generating jobs show submitted prompts first; image statuses update once results are processed.', + mainTask: 'Main job: {name}', + childTask: 'Subtask: {name}', + holdCost: 'Hold {amount}', + }, + itemResult: { + recoveredByRetry: 'Previous failure recovered by a retry subtask', + readyPreview: 'Image generated. Click to preview.', + readyDownload: 'Image generated and ready to download.', + noUsableImage: 'No usable image was generated.', + cancelled: 'Job cancelled.', + waiting: 'Waiting for results.', + emptyImageOutput: 'The upstream returned a result, but this item has no image content. This usually means a single Gemini/Vertex generation failed or was blocked by safety policies.', + providerItemFailed: 'The upstream result for this item has no usable image.', + }, + imagePreview: { + title: 'Image preview', + notice: 'This is a compressed thumbnail cached locally in your browser, so quality is reduced. Download the ZIP to view the original image.', + }, + create: { + title: 'Create batch job', + taskName: 'Task name', + taskNamePlaceholder: 'Defaults to the current time if left empty', + loadingKeys: 'Loading API keys...', + selectKeyPlaceholder: 'Select a Gemini API key', + noKeysHint: 'No Gemini API key is available for batch image generation. Create one and bind it to a Gemini group with batch image generation enabled first.', + model: 'Model', + imageSize: 'Image size', + imageSizeHint: 'Batch jobs are currently submitted at a fixed 1K image size.', + outputFormat: 'Output format', + estimatedOutput: 'Estimated output', + estimatedOutputValue: '{images} images / {prompts} prompts', + promptAdded: '{count} added', + promptPlaceholder: 'Paste a prompt, then add it to the list below', + customIdPlaceholder: 'Custom ID (optional)', + outputCountPerPrompt: 'Images per prompt', + outputCountOption: '{n} image | {n} images', + referenceImage: 'Reference images', + removeReferenceImage: 'Remove reference image', + limitsHint: 'Up to {maxPerItem} images per prompt and {maxPerJob} per job. The current model allows up to {refLimit} reference images per prompt; reference images consume input tokens once per generated image.', + referenceCount: '{n} reference image | {n} reference images', + noPrompts: 'No prompts added yet.', + cancelNotice: 'Cancelling requests an upstream cancellation. Images already indexed as successful will still be billed, and the remaining hold will be released.', + submittingNotice: 'Creating the upstream batch job. This usually takes a few seconds; please do not submit again.', + modelNoReferenceImages: 'The current model does not support reference images.', + refLimitReached: 'The current model allows up to {limit} reference images per prompt.', + refLimitExceededIgnored: 'The current model allows up to {limit} reference images per prompt. Extra files were ignored.', + refFormatUnsupported: 'Reference images must be PNG, JPEG, or WebP.', + refFileTooLarge: '{name} exceeds 10MB and was ignored.', + }, + guide: { + title: 'Batch Image Generation Guide', + uiTitle: 'How to use this page', + step1: '1. Select a Gemini API key with batch image generation enabled. The model list shows the models available to that key’s group.', + step2: '2. The task name can be left empty; the current time is used automatically on submit. Prompts are added to the list one by one, and each prompt can carry reference images and a repeat count.', + step3: '3. After submitting, the job is queued first and the item list shows the submitted prompts. Image previews are not loaded by default; click the preview button on an item to load a single image.', + step4: '4. Once completed you can download the ZIP. If some items failed, the More menu lets you retry only the failed items. Billing is still based on the number of successfully generated images; reference images are not billed separately.', + skillTitle: 'Skill instructions for Codex', + skillDesc: 'Tells Codex how to organize prompts, submit jobs, and download results on behalf of the user.', + }, + messages: { + loadKeysFailed: 'Failed to load API keys.', + loadModelsFailed: 'Failed to load available models.', + loadJobsFailed: 'Failed to load batch jobs.', + selectApiKey: 'Select an available Gemini API key.', + noModelsForKey: 'This key has no available batch image models.', + selectModel: 'Select a model.', + promptRequired: 'Enter at least one prompt.', + submitted: 'Batch job submitted.', + submitFailed: 'Failed to submit the batch job.', + refreshFailed: 'Failed to refresh the job.', + cancelConfirm: 'Cancellation will be sent upstream. Images already indexed as successful will still be billed, and the remaining hold will be released. Continue?', + cancelled: 'Cancellation requested.', + cancelFailed: 'Failed to cancel the job.', + batchDownloadStarted: 'Downloads for the selected jobs have started.', + downloadFailed: 'Failed to download the result.', + retrySubmitted: 'Retry job submitted for failed items.', + retryFailed: 'Failed to retry failed items.', + retryMissingPrompts: 'This job does not have saved prompts for failed items, so it cannot be retried automatically. Recreate it with the original prompt.', + retryTaskNameSuffix: 'Retry failed items', + deleteConfirm: 'This hides the job from your list while keeping billing records. Delete it?', + deleteSelectedConfirm: 'This hides the selected jobs from your list while keeping billing records. Delete them?', + deleted: 'Job record deleted.', + deleteFailed: 'Failed to delete the job record.', + loadItemsFailed: 'Failed to load item details.', + loadPreviewFailed: 'Failed to load the image preview.', + copiedInstruction: 'Batch image instructions copied.', + loadingModels: 'Loading available models...', + noModels: 'No available models', + noModelsHint: 'This key’s group has no models configured for batch image generation.', + noCompatibleAccount: 'No usable upstream batch image account is available for this key’s group. Contact an administrator to check the group’s schedulable Gemini API key or Vertex service account and model support.', + unsupportedProvider: 'The batch image provider for this job is not available. Contact an administrator to check the batch image provider configuration.', + providerSubmitFailed: 'The upstream batch image job failed to submit. Contact an administrator to check the upstream account, model permission, or provider status.', + vertexGcsBucketMissing: 'Vertex batch image generation is missing the managed GCS bucket configuration. Contact an administrator to configure BATCH_IMAGE_VERTEX_MANAGED_GCS_BUCKET before submitting again.', + queueFailed: 'The task queue is temporarily unavailable, so the batch job was not queued. Contact an administrator to check the queue service.', + billingHoldFailed: 'The cost hold failed, so the batch job was not submitted. Contact an administrator to check billing or balance hold service.', + groupDisabled: 'Batch image generation is not enabled for this key’s group. Choose another enabled key or contact an administrator.', + pricingMissing: 'The selected model does not have batch image pricing configured. Contact an administrator to add pricing first.', + insufficientBalance: 'Insufficient balance to hold the estimated batch image cost.', + invalidModel: 'Select a batch image model available for the current key.', + invalidItems: 'The prompt list is invalid. Check that it is not empty, within the item limit, and still using 1K image size.', + duplicateCustomId: 'Custom IDs in the prompt list must be unique.', + promptTooLong: 'One prompt is too long. Shorten it and try again.', + invalidReferenceImage: 'A reference image is invalid. Use PNG, JPEG, or WebP under 10 MB.', + tooManyReferenceImages: 'Too many reference images. Flash Image allows up to 3 per item, Pro Image allows up to 14, and each job allows up to 1000 total.', + referenceImagesTooLarge: 'Reference images are too large. Inline reference images are limited to 128 MB per job; use gs:// file_uri or split the job for large batches.', + tooManyOutputImages: 'Too many expected output images. Each prompt can request up to 4 images, and each job can generate up to 200 images.', + idempotencyConflict: 'This submission conflicts with a previous request ID. Refresh the page and submit again.', + notReady: 'The job is not complete yet. Download will be available after completion.', + outputDeleted: 'The result files for this job have already been cleaned up.', + resultMissing: 'The result file is unavailable. It may have been cleaned up, storage permissions may be broken, or storage settings may have changed. Contact an administrator to check the result file.', + itemFailed: 'This item has no successful image to preview.', + itemImageIndexOutOfRange: 'This item has no previewable image.', + downloadLimited: 'Too many download requests are active. Please try again later.', + downloadTooLarge: 'This ZIP is too large for a single download. Download fewer items at once or ask an administrator to raise the batch download limit.', + deleteNotReady: 'Job records can only be deleted after the job finishes.', + disabled: 'Batch image generation is currently disabled.', + authRequired: 'The current API key is unavailable or expired. Select the key again.', + adminReference: 'Send the error code and request ID to an administrator for troubleshooting.', + errorReference: 'Error detail', + errorCodeRef: 'code: {code}', + requestIdRef: 'request ID: {id}', + httpStatusRef: 'HTTP status: {status}', + }, + }, +} diff --git a/frontend/src/i18n/locales/en/index.ts b/frontend/src/i18n/locales/en/index.ts index 377c67aec7..a855df543a 100644 --- a/frontend/src/i18n/locales/en/index.ts +++ b/frontend/src/i18n/locales/en/index.ts @@ -1,6 +1,7 @@ import landing from './landing' import common from './common' import dashboard from './dashboard' +import batchImage from './batchImage' import admin from './admin' import misc from './misc' @@ -8,6 +9,7 @@ export default { ...landing, ...common, ...dashboard, + ...batchImage, admin, ...misc, } diff --git a/frontend/src/i18n/locales/zh/batchImage.ts b/frontend/src/i18n/locales/zh/batchImage.ts new file mode 100644 index 0000000000..cf018cef89 --- /dev/null +++ b/frontend/src/i18n/locales/zh/batchImage.ts @@ -0,0 +1,212 @@ +export default { + batchImage: { + columns: { + taskName: '任务名称', + model: '模型', + apiKey: '提交密钥', + result: '结果', + cost: '费用', + downloadStatus: '下载状态', + }, + status: { + queued: '排队中', + running: '生成中', + processingResults: '整理结果', + settling: '结算中', + completed: '已完成', + failed: '失败', + cancelled: '已取消', + outputDeleted: '结果已删除', + partialSuccess: '部分成功', + allFailed: '全部失败', + }, + itemStatus: { + pending: '排队中', + succeeded: '成功', + failed: '失败', + cancelled: '已取消', + recovered: '已补成功', + }, + filters: { + searchTaskName: '搜索任务名称', + allApiKeys: '全部 API Key', + allStatuses: '全部状态', + allDownloadStates: '全部下载状态', + downloaded: '已下载', + notDownloaded: '未下载', + }, + actions: { + usageGuide: '使用说明', + createJob: '创建批量任务', + downloadSelected: '下载选中', + deleteRecords: '删除记录', + retryFailedItems: '重试失败项', + cancelJob: '取消任务', + downloadZip: '下载 ZIP', + viewDetail: '查看详情', + download: '下载', + moreActions: '更多操作', + copyInstruction: '复制说明', + submitJob: '提交任务', + }, + list: { + selectedJobs: '已选择 {count} 个任务', + expandChildren: '展开 {n} 个子任务', + collapseChildren: '收起子任务', + childCount: '{n} 子任务', + childBadge: '子任务', + keyNotRecorded: '未记录', + totalCount: '共 {n}', + notDownloaded: '未下载', + empty: '暂无批量任务', + emptyHint: '点击右上角创建批量任务。', + }, + pagination: { + pageNumber: '第 {page} 页', + pageItems: '本页 {count} 条', + }, + promptPopover: { + title: '完整 Prompt', + copied: 'Prompt 已复制', + }, + detail: { + title: '任务详情', + aggregatedResult: '汇总结果', + result: '结果', + cost: '费用', + downloadStatus: '下载状态', + items: '明细', + preview: '预览', + previewZoom: '放大压缩预览 {id}', + previewReload: '重新加载压缩预览', + previewLoad: '加载压缩预览', + previewUnavailable: '不可预览', + noImage: '无图片', + loadingItems: '正在加载明细...', + noItems: '暂无明细', + noItemsHint: '排队或生成中的任务会先显示已提交的 prompt,结果整理完成后会更新图片状态。', + mainTask: '主任务:{name}', + childTask: '子任务:{name}', + holdCost: '冻结 {amount}', + }, + itemResult: { + recoveredByRetry: '旧失败已由重试子任务补成功', + readyPreview: '图片已生成,可点击预览', + readyDownload: '图片已生成,可下载', + noUsableImage: '未生成可用图片', + cancelled: '任务已取消', + waiting: '等待生成结果', + emptyImageOutput: '上游返回了结果,但这条没有图片内容。通常是 Gemini/Vertex 单条生成失败或被安全策略拦截。', + providerItemFailed: '上游返回的这条结果没有可用图片。', + }, + imagePreview: { + title: '图片预览', + notice: '当前显示的是浏览器本地缓存的压缩缩略图,清晰度会有影响;需要查看原图请下载 ZIP。', + }, + create: { + title: '创建批量任务', + taskName: '任务名称', + taskNamePlaceholder: '不填写则默认使用当前时间', + loadingKeys: '加载 API Key 中...', + selectKeyPlaceholder: '请选择 Gemini API Key', + noKeysHint: '当前没有可用于批量生图的 Gemini API Key。请先创建并绑定已开启批量生图的 Gemini 分组。', + model: '模型', + imageSize: '图片尺寸', + imageSizeHint: '当前批量任务固定按 1K 图片提交。', + outputFormat: '输出格式', + estimatedOutput: '预计生成', + estimatedOutputValue: '{images} 张 / {prompts} 条', + promptAdded: '已添加 {count} 条', + promptPlaceholder: '粘贴 prompt,添加后进入下方列表', + customIdPlaceholder: 'Custom ID 可选', + outputCountPerPrompt: '每条生成张数', + outputCountOption: '{n} 张', + referenceImage: '参考图', + removeReferenceImage: '移除参考图', + limitsHint: '每条最多 {maxPerItem} 张,整组最多 {maxPerJob} 张;当前模型每条最多 {refLimit} 张参考图,参考图按生成张数重复消耗输入 token。', + referenceCount: '{n} 参考图', + noPrompts: '还没有添加 prompt。', + cancelNotice: '取消任务会请求上游取消;已被系统索引为成功的图片仍会按成功项结算扣费,其余冻结金额会释放。', + submittingNotice: '正在创建上游批量任务,通常需要几秒,请不要重复提交。', + modelNoReferenceImages: '当前模型不支持参考图。', + refLimitReached: '当前模型每条最多 {limit} 张参考图。', + refLimitExceededIgnored: '当前模型每条最多 {limit} 张参考图,已忽略超出的文件。', + refFormatUnsupported: '参考图仅支持 PNG、JPEG 或 WebP。', + refFileTooLarge: '{name} 超过 10MB,已忽略。', + }, + guide: { + title: '批量生图使用说明', + uiTitle: '当前界面如何使用', + step1: '1. 选择已开启批量生图的 Gemini API Key,模型列表会按该 Key 所属分组可用模型展示。', + step2: '2. 任务名称可以留空,提交时会自动使用当前时间;Prompt 需要一条条添加到列表里,每条 Prompt 可附参考图,也可以设置重复生成张数。', + step3: '3. 提交后任务会先排队,明细会展示已提交的 Prompt;图片预览默认不加载,点击明细里的预览按钮才会加载单张图。', + step4: '4. 完成后可以下载 ZIP;部分失败时,更多菜单里可以只重试失败项。当前结算仍按成功输出图张数计算,不单独对参考图加价。', + skillTitle: '给 Codex 的 Skill 说明', + skillDesc: '用于告诉 Codex 如何代替用户整理 prompt、提交任务和下载结果。', + }, + messages: { + loadKeysFailed: '加载 API Key 失败', + loadModelsFailed: '加载可用模型失败', + loadJobsFailed: '加载批量任务失败', + selectApiKey: '请选择可用的 Gemini API Key', + noModelsForKey: '当前密钥没有可用的批量生图模型', + selectModel: '请选择模型', + promptRequired: '请至少填写一条 prompt', + submitted: '批量任务已提交', + submitFailed: '提交失败', + refreshFailed: '刷新失败', + cancelConfirm: '取消会请求上游取消;已被系统索引为成功的图片仍会按成功项结算扣费,其余冻结金额会释放。确定取消吗?', + cancelled: '已请求取消任务', + cancelFailed: '取消失败', + batchDownloadStarted: '已开始下载选中的任务', + downloadFailed: '下载失败', + retrySubmitted: '已提交失败项重试任务', + retryFailed: '重试失败项失败', + retryMissingPrompts: '这个任务没有保存失败项 prompt,无法自动重试。请复制原 prompt 后重新创建任务。', + retryTaskNameSuffix: '重试失败项', + deleteConfirm: '删除后这个任务会从你的列表隐藏,但账务记录仍会保留。确定删除吗?', + deleteSelectedConfirm: '删除后选中的任务会从你的列表隐藏,但账务记录仍会保留。确定删除吗?', + deleted: '任务记录已删除', + deleteFailed: '删除任务记录失败', + loadItemsFailed: '加载明细失败', + loadPreviewFailed: '加载图片预览失败', + copiedInstruction: '已复制批量生图说明', + loadingModels: '加载可用模型中...', + noModels: '无可用模型', + noModelsHint: '当前密钥所属分组没有配置可用于批量生图的模型。', + noCompatibleAccount: '当前密钥所属分组没有可用的批量生图上游账号。请联系管理员检查:该分组是否绑定了可调度的 Gemini API Key 或 Vertex 服务账号,以及账号是否支持所选模型。', + unsupportedProvider: '这个任务使用的批量生图通道当前不可用。请联系管理员检查批量生图通道配置。', + providerSubmitFailed: '上游批量生图任务提交失败。请联系管理员检查上游账号状态、模型权限或服务状态。', + vertexGcsBucketMissing: 'Vertex 批量生图缺少托管 GCS 存储桶配置。请联系管理员配置 BATCH_IMAGE_VERTEX_MANAGED_GCS_BUCKET 后再提交。', + queueFailed: '任务队列暂时不可用,批量任务没有成功入队。请联系管理员检查队列服务。', + billingHoldFailed: '费用冻结失败,批量任务没有成功提交。请联系管理员检查余额冻结或计费服务。', + groupDisabled: '当前密钥所属分组没有开启批量生图。你可以换一个已开启批量生图的密钥,或联系管理员开启。', + pricingMissing: '所选模型还没有配置批量生图价格。请联系管理员补充价格配置。', + insufficientBalance: '余额不足,无法冻结本次批量生图费用。', + invalidModel: '请选择一个可用于当前密钥的批量生图模型。', + invalidItems: 'Prompt 列表格式不正确,请检查是否为空、是否超过数量限制,或图片尺寸是否仍为 1K。', + duplicateCustomId: 'Prompt 列表里的 custom_id 不能重复。', + promptTooLong: '单条 prompt 过长,请缩短后重试。', + invalidReferenceImage: '参考图格式不正确,请使用 10MB 以内的 PNG、JPEG 或 WebP。', + tooManyReferenceImages: '参考图数量超过限制:Flash Image 每条最多 3 张,Pro Image 每条最多 14 张,整组最多 1000 张。', + referenceImagesTooLarge: '参考图总量过大。inline 参考图整组最多 128MB;大量参考图请改用 gs:// file_uri 或拆分任务。', + tooManyOutputImages: '预计生成张数超过限制:每条最多 4 张,整组最多 200 张。', + idempotencyConflict: '这次提交和之前的请求标识冲突,请刷新页面后重新提交。', + notReady: '任务还没有完成,完成后才能下载。', + outputDeleted: '这个任务的结果文件已经被清理,无法下载。', + resultMissing: '结果文件不可用,可能是上游结果文件已清理、存储权限异常,或管理员迁移过存储配置。请联系管理员检查结果文件。', + itemFailed: '这条明细没有成功图片,无法预览。', + itemImageIndexOutOfRange: '这条明细没有可预览的图片。', + downloadLimited: '当前下载请求太多,请稍后再试。', + downloadTooLarge: '这个 ZIP 太大,已超过单次下载限制。请减少单次下载数量,或联系管理员调整批量下载上限。', + deleteNotReady: '任务结束后才能删除记录。正在生成或结算中的任务请先等待完成。', + disabled: '批量生图功能当前未开启。', + authRequired: '当前 API Key 不可用或已失效,请重新选择密钥。', + adminReference: '请把错误码和请求 ID 发给管理员排查。', + errorReference: '错误信息', + errorCodeRef: '错误码:{code}', + requestIdRef: '请求 ID:{id}', + httpStatusRef: 'HTTP 状态:{status}', + }, + }, +} diff --git a/frontend/src/i18n/locales/zh/index.ts b/frontend/src/i18n/locales/zh/index.ts index 377c67aec7..a855df543a 100644 --- a/frontend/src/i18n/locales/zh/index.ts +++ b/frontend/src/i18n/locales/zh/index.ts @@ -1,6 +1,7 @@ import landing from './landing' import common from './common' import dashboard from './dashboard' +import batchImage from './batchImage' import admin from './admin' import misc from './misc' @@ -8,6 +9,7 @@ export default { ...landing, ...common, ...dashboard, + ...batchImage, admin, ...misc, } diff --git a/frontend/src/views/user/BatchImageGuideView.vue b/frontend/src/views/user/BatchImageGuideView.vue index 70387766a8..4cedca9949 100644 --- a/frontend/src/views/user/BatchImageGuideView.vue +++ b/frontend/src/views/user/BatchImageGuideView.vue @@ -8,7 +8,7 @@
@@ -19,18 +19,18 @@
-
@@ -39,9 +39,17 @@ v-if="selectedJobIds.size" class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-gray-200 bg-white px-3 py-2 shadow-sm dark:border-dark-700 dark:bg-dark-800" > - - 已选择 {{ selectedJobIds.size }} 个任务 - + + +
@@ -100,7 +108,7 @@ v-if="row.child_count > 0 && !row.is_child" type="button" class="mt-1 flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-md text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/30 dark:text-gray-400 dark:hover:bg-dark-700 dark:hover:text-white" - :title="expandedParentIds.has(row.id) ? '收起子任务' : `展开 ${row.child_count} 个子任务`" + :title="expandedParentIds.has(row.id) ? t('batchImage.list.collapseChildren') : t('batchImage.list.expandChildren', { n: row.child_count }, row.child_count)" @click.stop="toggleChildRows(row.id)" > @@ -113,10 +121,10 @@ > {{ row.task_name || defaultTaskName(row.created_at) }} - {{ row.child_count }} 子任务 + {{ t('batchImage.list.childCount', { n: row.child_count }, row.child_count) }} - 子任务 + {{ t('batchImage.list.childBadge') }} @@ -134,7 +142,7 @@ @@ -151,7 +159,7 @@ {{ displayJob(row).success_count }} / {{ displayJob(row).fail_count }} - 共 {{ displayJob(row).item_count }} + {{ t('batchImage.list.totalCount', { n: displayJob(row).item_count }) }} @@ -163,7 +171,7 @@ @@ -172,18 +180,18 @@
@@ -211,9 +219,9 @@ @@ -226,14 +234,18 @@ class="flex flex-col gap-3 border-t border-gray-200 bg-white px-4 py-3 dark:border-dark-700 dark:bg-dark-800 sm:flex-row sm:items-center sm:justify-between sm:px-6" >
- - 第 {{ pagination.page }} 页 - - - 本页 {{ visibleBatchJobs.length }} 条 - + + + + + +
- 每页 + {{ t('pagination.perPage') }}

- 当前没有可用于批量生图的 Gemini API Key。请先创建并绑定已开启批量生图的 Gemini 分组。 + {{ t('batchImage.create.noKeysHint') }}

- + @@ -587,9 +599,9 @@
- +
- {{ estimatedOutputCount }} 张 / {{ promptRows.length }} 条 + {{ t('batchImage.create.estimatedOutputValue', { images: estimatedOutputCount, prompts: promptRows.length }) }}
@@ -597,14 +609,14 @@
- 已添加 {{ promptRows.length }} 条 + {{ t('batchImage.create.promptAdded', { count: promptRows.length }) }}