mirror of
https://github.com/LisherSong/ps5-web-file-manager.git
synced 2026-10-06 07:00:28 +02:00
User reported FTP-upload-and-extract use case: after uploading .part01.rar..part05.rar via FTP, the extract button should appear on selection just like .zip already does, alongside the existing delete / copy / move / rename buttons in the toolbar. Changes are purely client-side. The extractBtn sits next to deleteBtn in the toolbar (HTML layout unchanged). New helpers: - isRarMainVolume(item): matches bare .rar AND .part01.rar/.part1.rar / .part001.rar, but excludes any .partNN.rar (those are sub-volumes) - isRarSubVolume(item): matches .part02+.rar (the non-first parts) - isExtractableArchive(item): .zip OR rar main volume renderExtractButton rewiring: - archives.length + subs.length both zero -> hidden (unchanged default) - only subs selected -> button visible but disabled with tooltip 'select the main volume (.rar or .part01.rar)' (new i18n keys) - exactly one archive selected -> button enabled with hover target actionExtract wired to the new isExtractableArchive filter, keeping the existing 'must be exactly 1 selected' guard so multi-select still no-ops cleanly. Caught a real bug while wiring: the first version of isRarMainVolume matched /\.rar$/ before the part01 check, so part02+ was incorrectly classified as a main volume. Fixed by reordering + adding negative sub-pattern so .partNN. never trips the bare-.rar rule. Two new i18n keys (zh + en): - extractSelectMainVolume: friendly message when user picks only sub-volumes - extractArchivePending: reserved for the future RAR password prompt 10/10 unit cases pass for the detection regexes (zip / .rar / .part01.rar / .part02.rar / .tar.gz / .bin). Backend support for RAR is not part of this commit; clicking Extract on a .rar on v1.7 still returns err_extract_unsupported from the existing zip_extract pipeline. That will swap to the unrar engine in v1.8 without any further frontend work. Also sets local repo core.autocrlf=false to keep JS files LF on this Windows dev machine (global autocrlf was rewriting them as CRLF on write and causing the whole file to show up as changed on every edit).
198 lines
9.0 KiB
JavaScript
198 lines
9.0 KiB
JavaScript
window.WFM_LANG = {
|
||
appTitle: "PS5 Web File Manager",
|
||
copy: "复制",
|
||
move: "移动",
|
||
delete: "删除",
|
||
download: "下载",
|
||
upload: "上传",
|
||
uploadFolder: "上传文件夹",
|
||
copying: "复制",
|
||
moving: "移动",
|
||
deleting: "删除",
|
||
changingPermissions: "修改权限",
|
||
rename: "重命名",
|
||
paste: "粘贴",
|
||
cancel: "取消",
|
||
close: "关闭",
|
||
save: "保存",
|
||
apply: "应用",
|
||
exit: "退出",
|
||
exitConfirm: "是否退出并关闭文件管理器进程?",
|
||
exiting: "正在退出...",
|
||
refresh: "刷新",
|
||
mkdir: "新建目录",
|
||
newText: "新建文本",
|
||
install: "安装",
|
||
installPackage: "安装 PKG",
|
||
pkgInfoTitle: "PKG 信息",
|
||
pkgInfoLoading: "正在读取 PKG 信息...",
|
||
pkgInstalling: "正在提交安装:{name}...",
|
||
pkgInstallStarted: "已开始安装:{name}",
|
||
file: "文件",
|
||
textFile: "文本",
|
||
image: "图片",
|
||
dir: "目录",
|
||
parent: "上级目录",
|
||
name: "名称",
|
||
type: "类型",
|
||
size: "大小",
|
||
mtime: "修改时间",
|
||
mode: "权限",
|
||
permissionsTitle: "修改权限",
|
||
permissionOwner: "拥有者",
|
||
permissionGroup: "组",
|
||
permissionOther: "其他",
|
||
permissionOctal: "八进制表示",
|
||
permissionRecursive: "应用于文件夹内的所有内容",
|
||
permissionObjectCount: "等 {count} 个对象",
|
||
permissionChangeTitle: "修改 {name} 的权限",
|
||
permissionInvalid: "请输入以 0 开头、由 0 到 7 组成的四位八进制权限。已恢复为原权限值。",
|
||
unsavedPermissionConfirm: "权限有未保存的修改,是否不保存并关闭?\n\n取消将留在权限弹窗中。",
|
||
permissionChanging: "正在修改权限...",
|
||
permissionChanged: "权限已修改:{name} → {mode}",
|
||
permissionChangeFailed: "修改权限失败:{error}",
|
||
empty: "目录为空",
|
||
ready: "就绪",
|
||
freeSpace: "可用空间",
|
||
queued: "等待中",
|
||
running: "进行中",
|
||
done: "完成",
|
||
failed: "失败",
|
||
canceled: "已取消",
|
||
finishing: "完成中",
|
||
preparing: "准备中",
|
||
checking: "检查中",
|
||
pleaseWait: "请稍候",
|
||
speedLabel: "速度",
|
||
itemsPerSecond: "{count} 个对象/秒",
|
||
progressLabel: "进度",
|
||
permissionProgress: "{done} / {total} 个对象",
|
||
etaLabel: "剩余",
|
||
elapsedLabel: "耗时",
|
||
durationHours: "{hours}小时 {minutes}分",
|
||
durationMinutes: "{minutes}分 {seconds}秒",
|
||
durationSeconds: "{seconds}秒",
|
||
preparingTask: "正在准备任务",
|
||
canceling: "正在取消...",
|
||
selectedItems: "{name} 等 {count} 项",
|
||
countItems: "{count} 项",
|
||
totalItems: "共 {count} 项",
|
||
readDir: "读取目录中...",
|
||
processing: "处理中...",
|
||
actionBusy: "{label}...",
|
||
taskCreated: "{label}任务已创建",
|
||
actionDone: "{label}完成",
|
||
actionFailed: "{label}失败: {error}",
|
||
taskFailed: "{label}失败: {error}",
|
||
taskCanceled: "{label}已取消",
|
||
selectedForPaste: "已选择 {name},进入目标目录后点击{verb}",
|
||
clipboardCleared: "已清除待操作项目",
|
||
downloadStarted: "已开始下载 {name}",
|
||
uploadFolderChoice: "选择要上传的目录?\n\n确定:选择目录\n取消:选择文件",
|
||
uploadOverwriteConfirm: "目标中已存在同名项目: {names}\n\n是否覆盖同名文件并合并同名目录?",
|
||
uploadingStatus: "正在上传 {index}/{count}: {name}",
|
||
uploadDone: "上传完成,共 {count} 项",
|
||
uploadFailed: "上传失败: {error}",
|
||
downloading: "下载中",
|
||
uploading: "上传中",
|
||
extract: "解压",
|
||
extractToCurrent: "解压到当前目录",
|
||
extractUpload: "上传并解压",
|
||
extracting: "解压",
|
||
extractConfirm: "解压 {name} 到 {path}?",
|
||
extractOverwriteAsk: "若目标已存在同名文件或目录:\n\n确定 = 覆盖同名文件(目录仍会合并)\n取消 = 若目标已存在则失败",
|
||
extractUploadConfirm: "上传并解压 {name}?\n\n目标目录:{path}\n成功后将删除上传的 ZIP。",
|
||
extractStarted: "已开始解压 {name}",
|
||
extractDone: "解压完成:{name}",
|
||
extractProgress: "{done} / {total} 个文件",
|
||
extractLargeAsk: "ZIP 体积较大({size}),是否启用「大文件模式」?\n\n确定 = 启用(单文件最大 1 TiB / 总解压最大 2 TiB)\n取消 = 默认限制(单文件 64 GiB / 总解压 512 GiB),可能拒绝此压缩包",
|
||
extractLargeActive: "此任务已启用大文件模式",
|
||
extractSelectMainVolume: "请改选主卷(如 .rar 或 .part01.rar)",
|
||
extractArchivePending: "正在准备解压 {name}",
|
||
sameSourceTarget: "源和目标相同,不能{label} {name}",
|
||
removeConflictFirst: "目标中已存在同名{existingType} {name}。要{label}{sourceType},请先删除该{existingType}才能继续。",
|
||
overwriteFiles: "同名文件将被覆盖: {names}",
|
||
mergeDirs: "同名目录将被合并,内部同名文件会被覆盖: {names}",
|
||
continueConfirm: "继续?",
|
||
preparingPaste: "准备中:正在计算大小并检查目标空间...",
|
||
preparingStatus: "{label}准备中...",
|
||
cancelPrepare: "已取消准备操作",
|
||
cancelTaskConfirm: "取消当前{label}任务?",
|
||
cancelFailed: "取消失败: {error}",
|
||
tasksPollFailed: "任务状态读取失败: {error}",
|
||
transferComplete: "{label}完成:{name}\n\n总耗时:{duration}\n传输大小:{size}",
|
||
transferCompleteFiles: "{label}完成:{name}\n\n总耗时:{duration}\n传输大小:{size}\n文件数量:{count}",
|
||
renamePrompt: "新名称",
|
||
mkdirPrompt: "目录名",
|
||
newTextPrompt: "文本文件名",
|
||
creatingText: "正在新建文本...",
|
||
createTextFailed: "新建文本失败: {error}",
|
||
unsavedSaveConfirm: "文本有未保存的修改,是否不保存并关闭?\n\n取消将留在编辑器中。",
|
||
loadingText: "正在读取文本...",
|
||
savingText: "正在保存...",
|
||
textSaved: "文本已保存",
|
||
openTextFailed: "打开文本失败: {error}",
|
||
saveTextFailed: "保存文本失败: {error}",
|
||
deleteConfirm: "确认删除 {name}?",
|
||
deleteConfirmRecursive: "确认删除 {name}?\n\n目录会递归删除。",
|
||
activeTask: "有任务正在执行",
|
||
pasteTitle: "{label} {name} 到 {path}",
|
||
pasteToCurrent: "到当前目录",
|
||
backendError: "操作失败",
|
||
storageInternal: "内部存储",
|
||
storageUsb: "USB存储",
|
||
storageM2: "M2扩充存储",
|
||
storageExtended: "扩展存储",
|
||
storageRoot: "根分区",
|
||
storageCurrent: "当前目录",
|
||
err_target_dir_not_writable: "目标目录不可写: {path}",
|
||
err_target_file_not_writable: "目标文件不可写: {path}",
|
||
err_target_parent_not_writable: "当前目录不可写: {path}",
|
||
err_target_check_failed: "无法检查目标路径: {path}",
|
||
err_target_path_too_long: "目标路径过长",
|
||
err_path_too_long: "路径过长",
|
||
err_space_check_failed: "无法读取目标剩余空间: {path}",
|
||
err_no_space: "目标空间不足,需要 {required},可用 {available}",
|
||
err_active_task: "有任务正在执行",
|
||
err_active_task_not_found: "没有找到正在执行的任务",
|
||
err_source_destination_same: "源和目标相同",
|
||
err_destination_inside_source: "不能复制或移动目录到它自己的内部",
|
||
err_invalid_path: "路径无效",
|
||
err_invalid_mode: "权限格式无效",
|
||
err_chmod_failed: "无法修改权限:{path}",
|
||
err_pkg_type_invalid: "只能安装 .pkg 文件",
|
||
err_pkg_info_failed: "无法读取 PKG 信息:{path}",
|
||
err_pkg_install_failed: "PKG 安装失败({arg})",
|
||
err_pkg_install_unsupported: "PKG 安装仅支持 PS5 环境",
|
||
err_file_not_found: "文件不存在",
|
||
err_file_already_exists: "同名文件已存在",
|
||
err_invalid_method: "请求方式无效",
|
||
err_text_type_not_editable: "该文件类型不支持文本编辑",
|
||
err_text_file_too_large: "文本文件过大,最大支持 1 MiB",
|
||
err_text_invalid_utf8: "文件不是有效的 UTF-8 文本",
|
||
err_text_file_changed: "文件在打开后已发生变化,请关闭后重新打开",
|
||
err_text_file_not_writable: "文本文件不可写",
|
||
err_request_body_too_large: "选择的项目过多,请减少选择后重试",
|
||
err_unknown_api: "未知接口",
|
||
err_out_of_memory: "内存不足",
|
||
err_no_source_paths: "没有源路径",
|
||
err_destination_must_be_directory: "多个项目的目标必须是目录",
|
||
err_extract_open_failed: "无法打开压缩包: {arg}",
|
||
err_extract_corrupt: "压缩包损坏或不完整: {arg}",
|
||
err_extract_unsupported: "不支持的压缩包(仅支持未加密的普通 ZIP): {arg}",
|
||
err_extract_unsafe_name: "压缩包包含不安全的路径: {arg}",
|
||
err_extract_special_entry: "压缩包包含不支持的特殊文件: {arg}",
|
||
err_extract_duplicate: "压缩包包含重复条目: {arg}",
|
||
err_extract_too_many_entries: "压缩包条目过多: {arg}",
|
||
err_extract_entry_too_large: "压缩包内单个文件过大: {arg}",
|
||
err_extract_too_large: "压缩包解压后总大小过大: {arg}",
|
||
err_extract_ratio: "压缩比异常(疑似压缩炸弹): {arg}",
|
||
err_extract_too_deep: "目录层级过深: {arg}",
|
||
err_extract_name_too_long: "文件名或路径过长: {arg}",
|
||
err_extract_conflict: "目标已存在同名文件或目录: {arg}",
|
||
err_extract_io: "解压读写失败: {arg}",
|
||
err_extract_crc: "CRC 校验失败: {arg}",
|
||
err_extract_failed: "解压失败: {arg}",
|
||
err_system_error: "系统错误: {arg}"
|
||
};
|