diff --git a/lang/zh/common.json b/lang/zh/common.json new file mode 100644 index 00000000..c826cd1b --- /dev/null +++ b/lang/zh/common.json @@ -0,0 +1,86 @@ +{ + "actions": { + "cancel": "取消", + "close": "关闭", + "copy": "复制", + "delete": "删除", + "duplicate": "复制", + "hide": "隐藏", + "lock": "锁定", + "remove": "移除", + "rename": "重命名", + "reset": "重置", + "save": "保存", + "saveChanges": "保存更改", + "select": "选择", + "undo": "撤销", + "unlock": "解锁" + }, + "labels": { + "account": "账户", + "actions": "操作", + "dashboard": "后台", + "description": "描述", + "expires": "过期时间", + "language": "语言", + "name": "名称", + "owner": "所有者", + "profile": "个人资料", + "project": "项目", + "role": "角色", + "status": "状态", + "theme": "主题", + "title": "标题", + "type": "类型", + "view": "视图" + }, + "dataTable": { + "filterPlaceholder": "筛选{title}...", + "noResults": "未找到结果。", + "selectedCount": "已选择 {count} 项", + "clearFilters": "清除筛选" + }, + "measurementUnit": { + "metric": "公制", + "imperial": "英制", + "presetAriaLabel": "单位预设:当前 {current}。切换到 {next}。", + "presetLabel": "单位预设:{current}", + "description": "切换显示距离和兼容输入。" + }, + "themeToggle": { + "labels": { + "light": "浅色", + "dark": "深色", + "system": "跟随系统" + }, + "ariaLabel": "主题:{theme}", + "tooltip": "当前:{current}。点击切换到 {next}。" + }, + "versionTag": { + "tooltip": { + "openReleaseNotes": "在 GitHub 打开更新说明", + "openReleaseNotesForCommit": "提交 {sha} · 在 GitHub 打开更新说明" + } + }, + "runtimeError": { + "badge": "运行时错误", + "title": "TrackDraw 遇到了一个问题。", + "description": "当前页面已停止渲染。你的本地项目数据通常仍保存在这个浏览器里,但在尝试恢复前请先不要关闭这个标签页。", + "tryAgain": "重试", + "openStudio": "打开 Studio", + "nextStep": "如果问题持续出现,请重新打开 Studio,并在继续进行较大改动前先导出一份 JSON 备份。" + }, + "status": { + "active": "生效中", + "expired": "已过期", + "featured": "精选", + "hidden": "已隐藏", + "listed": "已展示", + "loading": "加载中", + "off": "关闭", + "on": "开启", + "pending": "等待中", + "published": "已发布", + "revoked": "已撤销" + } +} diff --git a/lang/zh/dialogs.json b/lang/zh/dialogs.json new file mode 100644 index 00000000..11f9236e --- /dev/null +++ b/lang/zh/dialogs.json @@ -0,0 +1,827 @@ +{ + "account": { + "dialog": { + "eyebrow": "Studio", + "mobileSubtitle": "管理你的 TrackDraw 账户。" + }, + "nav": { + "security": "安全", + "apiKeys": "API 密钥", + "danger": "危险操作" + }, + "panels": { + "profile": { + "title": "个人资料", + "description": "管理在 TrackDraw 账户中显示的名称。" + }, + "security": { + "title": "安全", + "description": "管理 TrackDraw 账户的登录方式、账户邮箱和通行密钥。" + }, + "apiKeys": { + "title": "API 密钥", + "description": "为外部 TrackDraw 集成创建和撤销带过期时间的密钥。" + }, + "danger": { + "title": "危险操作", + "description": "具有破坏性且不可逆的账户操作。" + } + }, + "profile": { + "displayName": { + "label": "显示名称", + "placeholder": "你的名字" + }, + "language": { + "description": "更改会立即生效" + }, + "status": { + "saving": "正在保存..." + }, + "fallback": { + "noAccountEmail": "TrackDraw 账户" + } + }, + "security": { + "email": { + "address": "邮箱地址", + "newEmail": "新邮箱", + "placeholder": "name@example.com", + "changeDescription": "保存后,我们会先向你当前邮箱发送确认邮件。确认后,你再去验证新的邮箱地址。", + "change": "更换邮箱", + "saving": "正在保存..." + }, + "passkeys": { + "title": "通行密钥", + "description": "在支持的设备上添加通行密钥,登录会更快。", + "add": "添加通行密钥", + "adding": "正在添加...", + "devAuthUnavailable": "在 `npm run dev` 下无法使用通行密钥。请用 `npm run preview` 在本地测试真实认证流程。", + "unsupportedBrowser": "当前浏览器这里没有暴露 WebAuthn,所以这台设备暂时不能设置通行密钥。", + "loading": "正在加载通行密钥...", + "addedDate": "添加于 {date}", + "status": { + "synced": "已同步" + }, + "reauth": { + "title": "请重新登录后继续", + "description": "设置通行密钥需要近期登录状态。平时你可以保持登录,但这类安全操作需要再次确认。", + "action": "重新登录" + }, + "empty": { + "title": "还没有通行密钥", + "description": "添加一个后,在你信任的设备上登录会更快。" + }, + "aria": { + "rename": "重命名通行密钥", + "removing": "正在移除通行密钥", + "remove": "移除通行密钥" + }, + "actions": { + "removing": "正在移除...", + "removeTooltip": "从账户移除", + "saveName": "保存名称" + }, + "fields": { + "namePlaceholder": "通行密钥名称" + }, + "fallback": { + "unnamed": "未命名通行密钥" + }, + "rename": { + "description": "这只会改变它在你 TrackDraw 账户里的显示名称。" + } + } + }, + "apiKeys": { + "form": { + "keyNameLabel": "密钥名称", + "expiryDays": "{days} 天", + "expiryYear": "1 年", + "namePlaceholder": "Overlay 集成", + "create": "创建" + }, + "created": { + "title": "API 密钥已创建", + "description": "这个密钥只显示一次。请在关闭对话框前妥善保存。" + }, + "active": { + "title": "有效的 API 密钥", + "descriptionPrefix": "供受信任集成使用的只读密钥。", + "docsLink": "API 文档", + "refreshAriaLabel": "刷新 API 密钥", + "loading": "正在加载 API 密钥..." + }, + "empty": { + "title": "还没有 API 密钥", + "description": "当受信任的集成需要只读访问你账户内的项目时,再创建一个即可。" + }, + "fallback": { + "unnamed": "未命名 API 密钥" + }, + "meta": { + "expiresOn": "过期于 {date}", + "lastUsed": "最后使用于 {date}" + }, + "revoke": { + "confirmTitle": "撤销这个 API 密钥?", + "tooltip": "撤销", + "ariaLabel": "撤销 {name}", + "warningExpiring": "撤销后这个密钥会立即失效。", + "warningNonExpiring": "使用这个密钥的集成会失去只读访问权限。", + "revoking": "正在撤销...", + "action": "撤销" + } + }, + "danger": { + "description": "删除账户会移除账户项目、已发布链接、API 密钥以及其他账户关联数据。继续前,请先导出你想保留的账户数据。", + "confirm": { + "label": "输入 DELETE 确认", + "placeholder": "DELETE" + }, + "actions": { + "deleting": "正在删除...", + "deleteAccount": "删除账户" + } + }, + "shared": { + "loading": "正在加载账户...", + "signInTitle": "登录后管理账户", + "signInBody": "使用邮箱登录,即可访问云端账户设置和个人资料。" + }, + "errors": { + "loadPasskeysFailed": "加载通行密钥失败。", + "loadApiKeysFailed": "加载 API 密钥失败。", + "profileNameRequired": "请输入希望 TrackDraw 显示的名称。", + "profileUpdateFailed": "更新个人资料失败。", + "deleteConfirmRequired": "请输入 DELETE 以确认删除账户。", + "deleteFailed": "删除账户失败。", + "emailRequired": "请输入你要用于 TrackDraw 的邮箱地址。", + "emailSame": "请输入不同的邮箱地址来完成更换。", + "emailChangeFailed": "更换邮箱失败。", + "passkeyAddFailed": "添加通行密钥失败。", + "passkeyNameRequired": "保存前请输入通行密钥名称。", + "passkeyRenameFailed": "重命名通行密钥失败。", + "passkeyRemoveFailed": "移除通行密钥失败。", + "apiKeyNameRequired": "请为这个 API 密钥命名。", + "apiKeyCreateFailed": "创建 API 密钥失败。", + "apiKeyRevokeFailed": "撤销 API 密钥失败。" + }, + "success": { + "profileUpdated": "个人资料已更新", + "passkeyAdded": "通行密钥已添加。你可以在下面继续重命名或移除它。", + "passkeyRenamed": "通行密钥已重命名", + "apiKeyCreated": "API 密钥已创建", + "apiKeyCopied": "API 密钥已复制", + "apiKeyRevoked": "API 密钥已撤销" + } + }, + "export": { + "dialog": { + "title": "导出", + "subtitle": "把当前赛道导出为视觉素材、项目交接文件或可用于模拟器的输出。" + }, + "fields": { + "filename": { + "label": "文件名" + } + }, + "theme": { + "label": "主题", + "dark": "深色", + "light": "浅色" + }, + "routeNumbers": { + "label": "路径编号", + "hint": "用于 2D 导出", + "enable": "导出中显示路径编号", + "disable": "导出中隐藏路径编号", + "enableMobile": "显示路径编号", + "disableMobile": "隐藏路径编号" + }, + "actions": { + "switchTo3dView": "切换到 3D 视图", + "openRequiredView": "打开所需视图" + }, + "aria": { + "exportFormat": "导出 {label}" + }, + "messages": { + "view3dUnavailable": "3D 视图不可用——请先打开 3D 标签页", + "canvasNotReady": "画布尚未就绪", + "exported": "已导出" + }, + "readiness": { + "open3d": "导出这个渲染前请先打开 3D 视图。", + "canvasNotReady": "2D 画布仍在加载,请稍后再试。", + "emptyRacePack": "导出布场清单前,请先在赛道里添加至少一个项目。", + "missingRaceLine": "导出 Cinematic FPV 视频前,请先画出路径。", + "emptyVelocidrone": "导出 Velocidrone 文件前,请先在赛道里添加至少一个项目。", + "experimentalSimulator": "部分 TrackDraw 物件导入 Velocidrone 后的效果可能会不同。" + }, + "webm": { + "renderingBackground": "Cinematic FPV 正在后台渲染…", + "renderingProgress": "正在渲染 cinematic FPV… {status}", + "ready": "Cinematic FPV 导出已完成", + "progressLabel": "正在渲染模拟飞行… 视频长度 {duration}" + }, + "categories": { + "visuals": { + "title": "视觉内容" + }, + "raceDay": { + "title": "比赛日" + }, + "projectData": { + "title": "项目数据" + }, + "motion": { + "title": "视频" + }, + "simulatorLab": { + "title": "实验性" + } + }, + "formats": { + "image": { + "label": "图片", + "descriptionFull": "高分辨率 2D 平面图,适合打印、讲解页、聊天或快速审阅。", + "descriptionShort": "高分辨率 2D 平面图,适合打印、聊天或审阅。" + }, + "vector": { + "label": "矢量图", + "descriptionFull": "可缩放图稿,适合在 Illustrator、Figma 或 Inkscape 中继续编辑。", + "descriptionShort": "可缩放图稿,适合在 Illustrator、Figma 或 Inkscape 中继续编辑。" + }, + "render3d": { + "label": "3D 渲染", + "descriptionFull": "截取当前 3D 相机视角,用于视觉审阅。", + "descriptionShort": "当前 3D 相机截图,用于视觉审阅。" + }, + "projectFile": { + "label": "项目文件", + "descriptionFull": "可编辑备份,方便在 TrackDraw 中再次打开、分享或归档。", + "descriptionShort": "可编辑备份,方便在 TrackDraw 中再次打开或归档。" + }, + "racePack": { + "label": "布场清单", + "descriptionFull": "比赛日布场交接 PDF,包含地图、物料、顺序和二维码。", + "descriptionShort": "包含地图、物料、顺序和二维码的布场 PDF。" + }, + "cinematicFpv": { + "label": "Cinematic FPV", + "descriptionFull": "单圈线路视频,适合检查 flow 或直接分享整圈。", + "descriptionShort": "单圈线路视频,用于检查 flow。" + }, + "velocidrone": { + "label": "Velocidrone", + "descriptionFull": "实验性的 Velocidrone 赛道文件,可作为模拟器测试起点;导入后请自行复核布局。", + "descriptionShort": "实验性模拟器文件;导入后请自行复核。" + } + }, + "errors": { + "exportFailed": "导出失败", + "exportFailedDescription": "{message} 请重试、切换导出格式,或导出项目 JSON 作为手动备份。" + } + }, + "import": { + "dialogTitle": "打开项目", + "dialogSubtitle": "从 JSON 文件导入一个 TrackDraw 项目。", + "dropFile": "把文件拖到这里", + "browseFile": "或点击浏览", + "errorInvalidJson": "无效的 JSON 文件。", + "errorInvalidJsonHelp": "无效的 JSON 文件。请选择一份完整的 TrackDraw `.json` 导出文件后重试。", + "errorInvalidProject": "这看起来不像是一个 TrackDraw 项目。", + "errorInvalidProjectHelp": "这看起来不像是一个 TrackDraw 项目。请选择一份从 TrackDraw 导出的 JSON 项目文件。", + "errorUnsupportedFile": "目前只支持 TrackDraw 的 JSON 项目文件。", + "errorUnsupportedFileHelp": "目前只支持 TrackDraw 的 JSON 项目文件。请选择一份从 TrackDraw 导出的 `.json` 备份。", + "errorReadFailed": "TrackDraw 无法读取这个文件。", + "errorReadFailedHelp": "TrackDraw 无法读取这个文件。请重试,或换一份备份文件。", + "toastImportFailed": "导入失败", + "toastUnsupportedFile": "请选择一个从 TrackDraw 导出的 .json 文件。", + "toastInvalidJson": "TrackDraw 无法读取这个 JSON 文件。请检查文件后重试。", + "toastInvalidProject": "请选择一个从 TrackDraw 导出的 JSON 项目文件。", + "toastReadFailed": "TrackDraw 无法读取所选文件。请重试或换一个备份文件。", + "untitledProject": "未命名项目", + "objectCount": "{count} 个对象", + "replaceWarning": "当前项目会被替换。如果你想保留一份不依赖本地自动保存的手动恢复点,请先导出 JSON 备份。", + "replaceProject": "替换项目", + "backupFirst": "先备份当前项目" + }, + "share": { + "dialog": { + "eyebrow": "Studio", + "existingEyebrow": "共享赛道", + "title": "分享", + "mobileSubtitle": "分享这条赛道的只读审阅链接" + }, + "nav": { + "shareLink": "共享链接", + "share": "分享", + "embed": "嵌入", + "gallery": "图库" + }, + "currentLink": { + "title": "当前共享链接", + "readOnlyReviewOn": "{hostname} 上的只读 {view} 审阅" + }, + "content": { + "share": { + "title": "共享链接", + "description": { + "existing": "复制或再次发送当前已发布的只读链接;如果你想自己继续编辑,请打开 Studio 创建可编辑副本。", + "account": "为这个账户项目创建或更新长期有效的只读链接。", + "separate": "为这个可编辑副本或本地赛道创建一条独立的长期只读链接。", + "anonymous": "创建一条临时只读快照链接,并控制它的有效时间。" + } + }, + "gallery": { + "title": "图库可见性", + "description": "管理这条已发布链接在公开图库中的展示方式。" + }, + "embed": { + "title": "嵌入", + "description": "为已通过账户发布的赛道复制 iframe 嵌入代码,链接会一直有效,直到你撤销为止。" + }, + "actions": { + "descriptionExisting": "在其他应用中使用当前已发布链接,或在新标签页里重新打开它。", + "description": "打开、再次发送、撤销或导出这个分享,不会改动赛道本身。" + } + }, + "link": { + "expiry": { + "days": "{days} 天", + "label": "链接过期时间", + "description": "临时快照会一直可用,直到过期。" + }, + "titles": { + "savedProject": "已保存项目链接", + "newShare": "新的共享链接", + "separatePublished": "独立发布链接", + "temporary": "临时链接", + "noSavedProject": "还没有已保存项目链接", + "noSeparate": "还没有独立链接", + "noTemporary": "还没有临时链接" + }, + "descriptions": { + "savedProject": "这个已保存的账户项目使用同一条长期只读链接。更新它时,URL 不变,但内容会切到最新设计。", + "newShare": "这个可编辑副本会拥有自己的长期只读链接,不会覆盖其他项目已发布的链接。", + "createSavedProject": "为这个账户项目创建一条长期 URL,后续更新可以继续沿用同一条链接。", + "createSeparate": "为当前副本创建一条长期 URL,不会改动任何之前的分享。", + "createTemporary": "选择过期时间,然后创建一个只读快照。" + }, + "lifetime": { + "published": "{hostname} 上的已发布链接,会一直有效直到撤销", + "temporary": "{hostname} 上的只读快照,将在 {days} 天后过期" + }, + "warnings": { + "outdatedDesign": "这条链接已经不是最新设计内容。", + "outdatedExpiry": "这条链接已经不是最新的过期设置。" + } + }, + "embed": { + "iframeTitle": "TrackDraw 赛道嵌入", + "code": { + "title": "嵌入代码", + "description": "通过账户发布的赛道可以嵌入到俱乐部或赛事网站中。", + "label": "Iframe 代码", + "selectedViewDescription": "会包含你选定的初始视图。", + "copy": "复制代码" + }, + "state": { + "loading": "正在加载嵌入状态…" + }, + "publish": { + "title": "先发布再嵌入", + "description": "嵌入使用的是账户发布的长期链接,所以请先创建发布链接,再复制 iframe 代码。", + "action": "创建发布链接" + }, + "temporary": { + "title": "临时链接不能嵌入", + "description": "请先登录,并通过你的账户发布这条赛道,才能创建长期有效的嵌入。" + }, + "refresh": { + "message": "请先更新已发布链接,这样嵌入内容才会切到最新设计。", + "action": "更新链接" + }, + "initialView": { + "label": "初始视图", + "options": { + "layout2d": { + "label": "2D 布局", + "description": "以平面场地图打开" + }, + "preview3d": { + "label": "3D 预览", + "description": "以环绕审阅视角打开" + } + } + } + }, + "gallery": { + "status": { + "loading": "正在加载当前状态", + "featured": "已在图库置顶推荐", + "listed": "已展示在图库中", + "hiddenByModeration": "因审核被隐藏", + "linkOnly": "仅链接可见", + "noPublishedLink": "尚未发布链接", + "checking": "检查中", + "noLink": "无链接", + "expiresInDays": "{days} 天后过期", + "noExpiry": "不过期" + }, + "descriptions": { + "checking": "正在检查当前图库状态。", + "featured": "已显示在公开图库中,并置顶推荐。", + "listed": "已显示在公开图库中。", + "hidden": "已因审核被隐藏。只要未撤销,直链仍然可以使用。", + "linkOnly": "只保留分享链接,不会出现在公开图库中。", + "noLink": "请先创建共享链接。" + }, + "labels": { + "visibility": "可见性", + "shareLink": "共享链接", + "author": "作者" + }, + "publishFirst": { + "title": "先发布再上架", + "description": "图库使用的是当前已发布快照,所以想把这条赛道展示到图库里,必须先有一条共享链接。", + "action": "先创建共享链接" + }, + "moderation": { + "title": "审核锁定", + "description": "这条赛道已因审核被隐藏,不会出现在图库中。但只要未撤销,直链仍可能可用。" + }, + "details": { + "title": "图库信息", + "description": "更新这条赛道在公开图库卡片上的标题和描述。", + "edit": "编辑信息" + }, + "list": { + "title": "添加到图库", + "description": "填写标题、描述并生成预览图后,就能公开展示给其他人浏览。", + "add": "添加到图库", + "readyTitle": "已准备好上架", + "readyDescription": "把这个已发布快照加到公开图库中,并给它设置单独的标题、描述和预览图。" + }, + "fields": { + "titlePlaceholder": "赛道标题", + "descriptionPlaceholder": "用于图库卡片的简短描述" + }, + "validation": { + "titleRequired": "保存前请先填写标题。", + "descriptionLength": "描述请控制在 {min}-{max} 个字符之间。", + "displayNameRequired": "请先设置账户显示名。", + "noMetadataChanges": "请修改标题或描述后再保存。", + "refreshShareFirst": "请先更新共享链接,这样图库使用的才是最新快照。" + }, + "preview": { + "generating": "正在生成预览图...", + "ready": "预览图已就绪" + }, + "card": { + "title": "图库卡片", + "description": "查看或更新这条赛道对外展示的标题和描述。", + "untitled": "未命名", + "noDescription": "还没有设置图库描述。", + "viewGallery": "查看图库" + }, + "remove": { + "confirm": "要从公开图库中移除吗?共享链接本身仍会继续有效,直到你主动撤销为止。", + "action": "从图库移除" + }, + "fallback": { + "noDisplayName": "未设置显示名" + } + }, + "actions": { + "primary": { + "updateLink": "更新链接", + "copyLink": "复制链接", + "createLink": "创建链接", + "createNewLink": "创建新链接" + }, + "copyLink": "复制链接", + "revoke": "撤销", + "share": { + "existingDescription": "这个页面本身已经是已发布的只读审阅链接。你可以直接分享它,或打开 Studio 创建可编辑副本。", + "withPathDescription": "共享链接会以只读审阅页打开。这个链接会直接进入 {view} 审阅,并且路径已可用于模拟飞行。", + "withoutPathDescription": "共享链接会以只读审阅页打开。如果你希望审阅者使用模拟飞行和高度审阅,请先补一条路径。" + }, + "nativeShare": { + "title": "通过以下方式分享...", + "description": "发送到应用或联系人" + }, + "openInTab": { + "label": "在标签页中打开", + "reopenCurrent": "在新标签页里重新打开当前共享链接", + "previewLink": "在新窗口里预览共享链接" + }, + "exportJson": { + "label": "改为导出 JSON", + "description": "用于在 TrackDraw 里再次打开的可编辑备份" + } + }, + "errors": { + "createFailed": "创建共享链接失败", + "createFailedDescription": "TrackDraw 无法发布当前快照。请检查网络后重试;如果现在就需要交接,请先导出 JSON。", + "copyFailed": "复制链接失败", + "copyFailedDescription": "链接可能仍显示在输入框中。如果浏览器阻止剪贴板访问,请手动选中并复制。", + "nativeShareFailed": "无法打开分享面板", + "nativeShareFailedDescription": "当前浏览器无法打开系统分享面板。请改用复制链接。", + "revokeFailed": "撤销链接失败", + "revokeFailedDescription": "链接可能仍然处于可用状态。请在这个对话框里重试,或去 Project Manager 的共享标签页操作。", + "embedCopyFailed": "复制嵌入代码失败", + "embedCopyFailedDescription": "嵌入代码仍显示在文本框中。如果浏览器阻止剪贴板访问,请手动选中并复制。", + "galleryListFailed": "添加到图库失败", + "galleryListFailedDescription": "这条共享链接未被加入公开图库。请检查标题、描述、预览图和网络连接后再试。", + "galleryRemoveFailed": "无法从图库移除", + "galleryRemoveFailedDescription": "图库条目可能仍然可见。请重试;如果必须立刻停止访问,也可以直接撤销共享链接。", + "galleryUpdateFailed": "更新图库信息失败", + "galleryUpdateFailedDescription": "已发布的图库卡片没有被更新。请检查标题和描述后重试。", + "unableToReadLink": "无法读取当前共享链接", + "unableToReadLinkDescription": "请刷新这个分享页面;如果这是通过账户发布的共享,也可以从 Project Manager 中打开该链接。" + }, + "success": { + "linkUpdated": "链接已更新", + "linkCreated": "链接已创建", + "linkCopied": "链接已复制", + "embedCopied": "嵌入代码已复制", + "galleryListed": "赛道已加入图库", + "galleryUpdated": "图库信息已更新", + "linkRevoked": "链接已撤销" + } + }, + "projectManager": { + "dialog": { + "eyebrow": "Studio", + "title": "项目", + "mobileSubtitle": "打开、重命名、同步或恢复。" + }, + "nav": { + "thisDevice": "本机", + "shares": "共享", + "snapshots": "快照" + }, + "panels": { + "device": { + "label": "保存在本机", + "description": "保存在当前设备上的本地副本。已同步项目也可能同时出现在账户里。" + }, + "account": { + "label": "账户项目", + "description": "登录设备之间可访问的账户副本。在这里打开它,即可刷新当前浏览器内容。" + }, + "snapshots": { + "label": "快照", + "description": "当前项目过去保存的状态。恢复任意一个快照即可回退改动。" + }, + "shares": { + "label": "已发布共享", + "description": "你从账户发布的有效共享链接。可在这里复制、打开或撤销。" + } + }, + "device": { + "sync": { + "resolveConflict": "处理同步冲突", + "retry": "重试同步", + "updateAccountCopy": "更新账户副本", + "syncToAccount": "同步到账户", + "conflictDescription": "这个项目在另一台设备上已被改动。继续前请先检查版本。", + "failedDescription": "上次同步失败了。请尝试重新推送最新本地版本。", + "pendingDescription": "把最新本地改动推送到你的账户。", + "updateDescription": "用当前设备上的版本覆盖账户里的同步副本。", + "description": "让这个项目能在你已登录的其他设备上继续使用。", + "localChangesWaiting": "有本地改动等待同步", + "latestLocalCopySaved": "最新本地副本保存于 {relativeTime}", + "changedOnAnotherDevice": "这个项目在另一台设备上被改过", + "couldNotSyncProject": "无法同步这个项目", + "resolveVersionConflict": "处理版本冲突" + }, + "status": { + "syncing": "同步中", + "reviewNeeded": "需复核", + "failed": "同步失败", + "synced": "已同步", + "localOnly": "仅本机" + }, + "meta": { + "localCopyWaitingToSync": "{itemLabel} · 本地副本等待同步", + "localCopySynced": "{itemLabel} · 本地副本已于 {relativeTime} 同步", + "localCopy": "{itemLabel} · 本地副本", + "onlyOnThisDevice": "{itemLabel} · 仅本机 · {relativeTime}", + "onThisDeviceSubtitle": "本机上的 {itemLabel}" + }, + "aria": { + "resolveVersionConflictFor": "处理 {title} 的版本冲突", + "retrySyncFor": "重试 {title} 的同步", + "updateAccountCopyFor": "更新 {title} 的账户副本", + "syncProjectToAccount": "把 {title} 同步到账户", + "exportProjectAsJson": "把 {title} 导出为 JSON", + "manageProject": "管理 {title}", + "renameProject": "重命名 {title}", + "deleteProject": "删除 {title}" + }, + "actions": { + "confirmRenameTitle": "确认重命名", + "more": "更多操作", + "newProject": "新建项目", + "newProjectDescription": "开始一条全新赛道,或使用起始布局。", + "cleanUpLocal": "清理本地项目", + "cleanUpLocalDescription": "只移除当前设备上的旧副本。", + "renameDescription": "给这个项目起个更清晰的名字。", + "openBadge": "已打开" + }, + "export": { + "json": "导出 JSON", + "jsonDescription": "把这个项目下载成可复用的 TrackDraw 文件。" + }, + "delete": { + "localProject": "删除本地项目?", + "localProjectDescription": "会移除这个本地副本。", + "permanently": "永久删除?", + "permanentlyDescription": "这只会把本地项目从当前浏览器中移除。", + "localProjectActionDescription": "把这个本地项目从当前设备删除。", + "removeLocalProjectsTitle": "删除 {count} 个本地项目?", + "accountSyncedCopiesStayAvailable": "已同步到账户的副本仍然会保留在你的账户中。" + }, + "groups": { + "localCopiesLinkedToAccount": "已关联到账户的本地副本", + "onlyOnThisDevice": "仅保存在本机" + }, + "empty": { + "noProjects": "还没有已保存项目", + "noProjectsDescription": "你保存的项目会显示在这里。" + }, + "fallback": { + "project": "项目", + "untitled": "未命名" + } + }, + "account": { + "status": { + "loadingProjects": "正在加载账户项目...", + "syncing": "同步中", + "reviewNeeded": "需复核", + "failed": "同步失败", + "synced": "已同步" + }, + "empty": { + "noProjects": "还没有账户项目", + "description": "先同步一个项目,才能在多设备间继续使用。" + }, + "messages": { + "loadFailed": "无法加载账户项目", + "loadFailedDescription": "当前设备上的本地项目仍然可用。请检查网络连接或重新登录,然后再打开 Project Manager。", + "conflictWarning": "你离线期间,这个项目在另一台设备上被改动过", + "syncError": "无法同步最新改动", + "localCopySavedAfterFailure": "最新本地副本保存于 {relativeTime}", + "resolveConflictFirst": "请先处理版本冲突" + }, + "actions": { + "retrySync": "重试同步", + "syncPendingChanges": "同步待处理改动" + }, + "fallback": { + "untitled": "未命名" + } + }, + "restore": { + "empty": { + "noSnapshots": "还没有快照", + "description": "打开或替换项目时,会自动创建快照。", + "desktopDescription": "按 ⌘S / Ctrl S,或使用顶部的保存按钮。" + }, + "actions": { + "restoreTitle": "恢复这个快照", + "deleteTitle": "删除快照", + "restore": "恢复" + }, + "aria": { + "restoreSnapshot": "恢复 {title}", + "deleteSnapshot": "删除 {title}" + }, + "confirm": { + "restoreSnapshot": "恢复这个快照?" + }, + "status": { + "active": "当前" + }, + "fallback": { + "snapshot": "快照", + "untitled": "未命名" + } + }, + "shares": { + "status": { + "loadingPublishedShares": "正在加载已发布共享...", + "published": "已发布", + "expired": "已过期", + "expiresInDays": "剩余 {days} 天" + }, + "messages": { + "readOnlyReviewLinks": "共享内容都是只读审阅链接。如果对方需要可编辑备份,请导出 JSON。" + }, + "empty": { + "noShares": "还没有有效共享", + "description": "账户发布链接会一直保留到你撤销为止。匿名临时链接会自动过期,也不会在这里管理。" + }, + "lifetime": { + "published": "只读发布链接,会一直有效直到撤销", + "temporary": "只读临时链接,{expiresIn}" + }, + "actions": { + "copyLink": "复制链接", + "openTab": "在新标签页打开", + "revokeLink": "撤销链接" + }, + "aria": { + "copyLink": "复制共享链接", + "openTab": "打开共享链接", + "revokeLink": "撤销共享链接" + }, + "confirm": { + "revokeLink": "撤销这条链接?" + } + }, + "projectIdCopy": { + "success": "项目 ID 已复制", + "failed": "当前浏览器无法复制项目 ID。", + "label": "API 项目 ID", + "ariaLabel": "复制 API 项目 ID", + "action": "复制" + } + }, + "completeProfile": { + "title": "补充你的名字", + "subtitle": "给账户设置一个清晰的显示名称,用于云端项目和分享。", + "displayNameLabel": "显示名称", + "namePlaceholder": "你的名字", + "description": "这是 TrackDraw 在云端项目和共享布局中显示给别人的名字。", + "descriptionWithEmail": "这是 TrackDraw 在 {email} 账户下的云端项目和共享布局中显示给别人的名字。", + "skip": "跳过", + "save": "保存名称", + "saving": "正在保存…", + "nameRequired": "请输入希望 TrackDraw 显示的名称。", + "updateFailed": "更新账户名称失败。", + "success": "个人资料已更新" + }, + "newProject": { + "title": "新建项目", + "closeAriaLabel": "关闭", + "subtitle": "从空白画布开始,或选择一个起始布局继续搭建。", + "desktopSubtitle": "从空白画布开始,或选择一个起始布局继续搭建。", + "studioLabel": "Studio", + "blankCanvasLabel": "空白画布", + "starterLayoutsLabel": "起始布局", + "saveWarningTitle": "请先保存当前赛道", + "saveWarningBody": "开始新项目会替换当前画布内容。如果你还想保留它,请先导出或备份。", + "startFreshTitle": "从空白画布开始", + "startFreshBody": "清空当前场地,从零搭建你的赛道。", + "exportFirstTitle": "先备份当前项目", + "exportFirstBody": "开始新项目之前,先把当前赛道保存一份副本。" + }, + "keyboardShortcuts": { + "dialogTitle": "快捷键", + "dialogSubtitle": "Studio 中可用的键盘与画布快捷操作。", + "sectionTools": "工具", + "sectionSelection": "选择", + "sectionPathEditing": "路径编辑", + "sectionCanvas": "画布", + "shortcuts": { + "duplicateItems": "复制当前选中项目", + "copyItems": "复制当前选中项目", + "pasteItems": "粘贴已复制项目", + "rotateLeft": "向左旋转选中项目", + "rotateRight": "向右旋转选中项目", + "mouseRotateSnap": "鼠标旋转吸附", + "mouseRotateFine": "鼠标精细旋转", + "keyRotateFine": "按键精细旋转", + "deleteItems": "删除当前选中项目", + "nudgeItems": "微移当前选中项目", + "fineNudge": "精细微移", + "finishPath": "完成路径", + "removeLastPoint": "移除最后一个草稿点", + "deletePathPoint": "删除选中的路径点", + "cancelDraft": "取消当前草稿", + "fitView": "视图适应场地", + "clearSelection": "清空选择", + "panView": "平移视图", + "bypassSnap": "临时绕过吸附", + "zoom": "缩放", + "saveSnapshot": "保存快照" + } + }, + "versionConflict": { + "title": "选择要保留的项目版本", + "subtitle": "选择你希望在当前设备上继续使用的版本。", + "thisProject": "这个项目", + "body": "在你离线期间,这个项目在另一台设备上被改动过。请选择当前设备要继续使用哪一个版本。", + "localVersion": "当前设备版本", + "cloudVersion": "账户版本", + "lastChanged": "最后更改于 {date}", + "keepLocal": "保留当前设备版本", + "useCloud": "使用账户版本" + } +} diff --git a/lang/zh/editor.json b/lang/zh/editor.json new file mode 100644 index 00000000..fae7b7c5 --- /dev/null +++ b/lang/zh/editor.json @@ -0,0 +1,465 @@ +{ + "toolbar": { + "homepageLabel": "返回首页", + "projects": "项目", + "projectsTooltip": "管理项目", + "import": "导入", + "importTooltip": "导入 JSON", + "export": "导出", + "exportTooltip": "导出赛道" + }, + "contextMenu": { + "quickActions": "快捷操作", + "selectedCount": "已选择 {count} 项", + "segmentSelected": "已选择线段", + "waypointSelected": "已选择路径点", + "addWaypoint": "添加路径点", + "deleteWaypoint": "删除路径点", + "arrange": "排列", + "bringForward": "上移一层", + "sendBackward": "下移一层", + "rotate": "旋转", + "rotateLeft": "向左旋转", + "rotateRight": "向右旋转" + }, + "canvasOverlay": { + "pathEditing": "路径编辑", + "addPoints": "添加点", + "finish": "完成", + "cancel": "取消", + "connectEnds": "点击连接首尾", + "grid": "网格 {grid}", + "snapOn": "吸附开:网格、物件、路线", + "snapOff": "吸附关:自由摆放", + "midClick": "中键", + "rightClick": "右键", + "pan": "平移", + "menu": "菜单", + "bypass": "临时绕过", + "fitToWindow": "适应窗口", + "viewerPanZoom": "拖动画布平移,滚轮缩放" + }, + "preview3dOverlay": { + "pause": "暂停", + "play": "播放", + "speed": "速度", + "toggleFpv": "切换 FPV 相机行为", + "on": "开", + "off": "关", + "exit": "退出", + "flyThrough": "模拟飞行", + "dragWaypointMobile": "上下拖动路径点控制点来调整高度", + "dragWaypointDesktop": "上下拖动路径点控制柄来调整高度", + "dragDiveGateMobile": "拖动圆环旋转 · 拖动橙色控制点调倾角", + "dragDiveGateDesktop": "拖动圆环旋转 · 拖动橙色控制点调倾角", + "dragRing": "拖动圆环旋转", + "orbitHint": "拖拽环绕查看 · 滚轮缩放 · 中键拖动平移", + "drawRoute": "先在 2D 里画出路径,才能启用模拟飞行" + }, + "statusBar": { + "snapActive": "吸附", + "snapOn": "吸附开", + "snapOff": "吸附关", + "snapIsOn": "吸附已开启", + "snapIsOff": "吸附已关闭", + "snapOnDescription": "拖拽和摆放会锁到附近网格、物件和路径点。放置或拖动时按住 Alt 可临时绕过一次。", + "snapOffDescription": "摆放和拖动保持自由状态,直到你重新开启吸附。", + "selected": "已选 {count} 项", + "grouped": "已分组", + "groupNamed": "分组:{name}", + "groups": "{count} 个分组", + "devOn": "开发模式 开", + "dev": "开发模式" + }, + "accountMenu": { + "checkingAuth": "正在检查登录状态…", + "signIn": "登录", + "signedIn": "已登录", + "noDisplayName": "未设置显示名", + "trackdrawAccount": "TrackDraw 账户", + "profile": "个人资料", + "dashboard": "后台", + "signOut": "退出登录", + "signingOut": "正在退出…" + }, + "common": { + "canvas2d": "2D 画布", + "preview3d": "3D 预览" + }, + "elementPlacementControl": { + "official": "官方", + "typeLabel": "{tool} 类型" + }, + "header": { + "untitledTitle": "未命名", + "statusFailed": "失败", + "statusPending": "等待中", + "statusSyncing": "同步中", + "statusSynced": "已同步", + "expandSidebar": "展开侧边栏", + "collapseSidebar": "收起侧边栏", + "sharedReviewBadge": "共享审阅", + "makeEditableCopy": "创建可编辑副本", + "undo": "撤销", + "redo": "重做", + "saveSnapshot": "保存快照", + "keyboardShortcuts": "快捷键", + "retrySync": "重试账户同步 ({status})", + "share": "分享", + "export": "导出", + "openAppMenu": "打开应用菜单", + "hideObstacleNumbers": "隐藏障碍编号", + "showObstacleNumbers": "显示障碍编号" + }, + "sharedHeader": { + "embedLabel": "嵌入", + "sharedPreviewLabel": "共享预览" + }, + "mobileAppMenu": { + "openAppMenu": "打开应用菜单", + "title": "TrackDraw", + "subtitle": "项目、分享、账户和应用设置", + "signIn": "登录", + "yourAccount": "你的 TrackDraw 账户", + "trackdrawAccount": "TrackDraw 账户", + "projectsSection": "项目", + "projectsLabel": "项目", + "projectsDescription": "打开并管理已保存项目", + "accountSection": "账户", + "profileLabel": "个人资料", + "profileDescription": "管理个人资料、安全设置和 API 密钥", + "dashboardLabel": "后台", + "dashboardDescription": "打开审核和管理工具", + "shareSection": "分享与传输", + "shareLabel": "分享", + "shareDescription": "发布这条赛道的只读链接", + "importLabel": "导入", + "importDescription": "导入 JSON 项目文件", + "exportLabel": "导出", + "exportDescription": "下载 PNG、PDF、SVG 或 JSON", + "themeLabel": "主题", + "themeDescription": "切换浅色、深色或跟随系统", + "signOut": "退出登录", + "signingOut": "正在退出…" + }, + "layoutPresetPicker": { + "itemsCount": "{count} 个项目", + "renamePresetTooltip": "重命名预设", + "deletePresetTooltip": "删除预设", + "signInTitle": "登录后使用预设", + "signInDescription": "预设会保存到你的账户中,这样你在所有设备上都能直接使用。", + "noPresetsTitle": "还没有预设", + "noPresetsDescription": "在画布上选中多个图形,然后在检查器里使用 保存为预设,就能把第一个预设加到这里。", + "comingSoonTitle": "即将推出", + "comingSoonDescription": "社区共享预设会显示在这里。先保存你自己的预设,之后也可以分享给 TrackDraw 社区。", + "myPresetsNav": "我的预设", + "storeNav": "商店", + "soonBadge": "即将推出", + "mobileTitle": "预设", + "mobileSubtitle": "先选一个预设,再在画布上点一下即可放置。", + "placeFlowTitle": "放置流程", + "placeFlowDescription": "在这里选择预设后点一下画布,结果会立即展开成普通可编辑图形。", + "desktopTitle": "布局预设", + "desktopSubtitle": "选择一个预设放到画布上,然后像普通图形一样继续编辑。", + "tipText": "在画布上选中图形,然后在检查器里使用 保存为预设 添加你自己的预设。" + }, + "starterOverlay": { + "dialog": { + "title": "欢迎使用 TrackDraw", + "mobileSubtitle": "先摆几个门,画出路径,看看 3D,赛道准备好后再分享。", + "studioEyebrow": "Studio", + "desktopDescription": "先在 2D 里搭好布局,再到 3D 检查高度,赛道准备好后再分享只读链接。", + "dismissAria": "关闭新手引导" + }, + "hints": { + "gate": { + "badge": "引导", + "title": "先放第一个门", + "description": "在画布上点一下就能放下第一个穿越门。先摆几个门,再切到路径工具。", + "action": "知道了", + "dismiss": "关闭穿越门摆放提示" + }, + "path": { + "badge": "引导", + "title": "接下来画路径", + "description": "先摆几个门,再切到路径工具,把一圈线路串起来。画完路径后再去看 3D。", + "action": "开始画路径", + "dismiss": "关闭路径引导提示" + }, + "preview3d": { + "badge": "预览", + "title": "先有路径,3D 才更有用", + "description": "这里已经能预览障碍摆放,但真正让高度和模拟飞行审阅有意义的,还是路径本身。", + "action": "去 2D 画路径", + "dismiss": "关闭 3D 预览引导提示" + }, + "review3d": { + "badge": "引导", + "title": "现在去 3D 看看", + "description": "绕着路径看看高度和门距,感觉顺了以后再去分享或导出。", + "action": "下一步去分享或导出", + "dismiss": "关闭 3D 审阅提示" + }, + "postPath": { + "badge": "下一步", + "title": "路径已就绪,去 3D 检查", + "description": "现在切到 3D 标签页,先检查这条线路,再继续细调障碍摆放。", + "action": "切换到 3D", + "dismiss": "关闭路径完成后的 3D 提示" + } + } + }, + "starterFlow": { + "intro": { + "goodFirstSteps": "推荐的起步方式" + }, + "steps": { + "step1": { + "title": "先摆几个门和障碍", + "description": "先从小布局开始,让赛道骨架尽快出现。" + }, + "step2": { + "title": "再把比赛路径画出来", + "description": "很多时候,先把路径画出来,比单纯调障碍位置更快看出问题。" + }, + "step3": { + "title": "检查 3D,然后导出或分享", + "description": "尽早检查路径,准备好后再发出只读链接。" + } + }, + "starterLayouts": { + "label": "起始布局" + }, + "guided": { + "label": "引导式开始", + "title": "按引导开始", + "descriptionDesktop": "打开一个空场地,并默认选中 `Gate`,先把赛道框架摆出来。等第一批障碍落位后,再去画路径。", + "descriptionMobile": "打开空场地并默认选中 Gate。第一批障碍落位后再去画路径。" + }, + "empty": { + "label": "空白开始", + "continue": "以空白画布继续", + "description": "跳过引导,直接进入空白 Studio。" + }, + "shortcuts": { + "gate": "放置穿越门", + "path": "开始画路径", + "finish": "完成路径" + } + }, + "saveAsPresetDialog": { + "title": "保存为预设", + "subtitle": "给当前选择起个名字,之后就能从预设选择器里再次放置。", + "shapeCount": "{count} 个图形", + "pathsExcluded": "将排除 {count} 条路径——预设只会记录非路径图形。", + "presetNameLabel": "预设名称", + "presetNamePlaceholder": "例如:计时门组合", + "savePreset": "保存预设" + }, + "mobilePanels": { + "viewControls": { + "mode": { + "current": "当前模式", + "viewModeLabel": "视图模式" + }, + "controls": { + "fitToField": { + "label": "适应场地", + "description": "将当前设计居中显示" + }, + "snap": { + "label": "吸附", + "description": "吸附到附近网格和物件" + }, + "rulers": { + "label": "标尺", + "description": "在移动端显示上侧和左侧辅助标尺" + }, + "obstacleNumbers": { + "label": "障碍编号", + "description": "在路径障碍上显示路径编号" + }, + "flyThrough": { + "label": "模拟飞行", + "descriptions": { + "available": "启动路径预览相机", + "readOnly": "这条共享赛道没有路径", + "draw": "先在 2D 里画出路径后才能启用" + } + }, + "gizmo": { + "label": "坐标轴", + "description": "在 3D 预览中显示坐标轴辅助器" + } + }, + "readOnly": { + "review3dWithPath": "当前共享赛道为只读 3D 审阅,且可用模拟飞行。", + "review3dNoPath": "当前共享赛道为只读 3D 审阅,但没有路径可用于模拟飞行。", + "review2dWithPath": "当前共享赛道为只读 2D 审阅。切到 3D 后可使用模拟飞行。", + "review2dNoPath": "当前共享赛道为只读 2D 审阅。" + }, + "share": { + "sectionLabel": "分享", + "editableCopy": "可编辑副本", + "shareButton": "分享" + } + }, + "multiSelect": { + "header": { + "title": "多选", + "lockedHint": "当前选择已锁定。先解锁后才能移动或缩放。", + "tapHint": "点按项目可将其加入或移出选择。" + }, + "fields": { + "groupNamePlaceholder": "分组名称" + }, + "actions": { + "duplicate": "复制", + "lock": "锁定", + "unlock": "解锁", + "delete": "删除", + "ungroup": "取消分组", + "group": "分组" + } + }, + "pathBuilder": { + "header": { + "title": "路径绘制" + }, + "status": { + "loopConnected": "闭环已连接 · {count} 个点 · {length}", + "pointsCount": "{count} 个点 · {length}", + "tapToStart": "点按画布放置第一个点" + }, + "actions": { + "undo": "撤销", + "connectEnds": "连接首尾", + "finish": "完成", + "cancel": "取消" + } + }, + "editorPanels": { + "quickActions": { + "title": "快捷操作", + "adjustTitle": "微调", + "hints": { + "addPoint": "在线段上添加点", + "deleteWaypoint": "删除选中的路径点", + "resume": "继续编辑或调整这条路径", + "selectionStep": "{label} · 步进 {step}", + "selectionFallback": "选择" + }, + "actions": { + "addPoint": "添加点", + "editPath": "编辑路径", + "duplicate": "复制", + "lock": "锁定", + "unlock": "解锁", + "delete": "删除", + "deletePoint": "删除点", + "adjust": "微调", + "back": "返回" + }, + "adjust": { + "step": "步进", + "left": "左", + "up": "上", + "down": "下", + "right": "右" + } + }, + "nav": { + "exit": "退出", + "select": "选择", + "tools": "工具", + "inspect": "检查", + "view": "视图", + "review": "审阅", + "share": "分享", + "editCopy": "编辑副本", + "preview": "预览" + }, + "inspector": { + "designSettingsHint": "设计设置", + "title": "检查器", + "subtitles": { + "empty": "设计设置和已放置项目", + "selected": "当前选择的属性与编辑" + } + }, + "tools": { + "title": "工具", + "sections": { + "items": "赛道项目", + "tools": "画布工具" + }, + "toolDescriptions": { + "startfinish": "标记起飞区或计时起终点", + "cone": "放置赛道标记物", + "label": "在画布上添加备注", + "polyline": "绘制或编辑比赛路径" + }, + "subtitles": { + "preview3d": "当前 3D 会话的项目操作", + "canvas2d": "绘制与编辑工具" + } + }, + "view": { + "title": "视图", + "subtitle": "画布模式、辅助线和视口控制" + } + } + }, + "viewModeSwitch": { + "ariaLabel": "视图模式", + "canvas2dShort": "2D", + "preview3dShort": "3D", + "canvas2dDrawer": "2D 画布", + "preview3dDrawer": "3D 预览" + }, + "desktopInspectorPanel": { + "ariaLabel": "检查器", + "expandTooltip": "展开检查器", + "collapseTooltip": "收起检查器", + "verticalLabel": "检查器" + }, + "shell": { + "untitledTrack": "未命名赛道", + "untitledFallback": "未命名", + "exportFailed": "导出失败", + "exportFailedDescription": "{message} 请重试,或打开导出面板,从那里下载项目 JSON 备份。", + "exportFailedNoProjectDescription": "TrackDraw 无法加载这个本地项目。请先打开它,或改选另一个已保存项目。", + "exportSuccess": "项目 JSON 已导出", + "embeddedTrack": "嵌入赛道", + "sharedPreview": "共享预览", + "loading3d": "正在加载 3D…" + }, + "languagePicker": { + "ariaLabel": "语言" + }, + "performanceHud": { + "title": "开发模式 HUD", + "summary": "工具 {tool} · 已选 {count} 项", + "reset": "重置", + "close": "关闭", + "metrics": { + "canvas2d": "2D 画布", + "preview3d": "3D 视图", + "inspector": "检查器", + "autosave": "自动保存" + }, + "levels": { + "noData": "无数据", + "heavy": "负载高", + "busy": "繁忙", + "ok": "正常" + } + }, + "textureDebug": { + "title": "贴图调试", + "copyConfig": "复制配置", + "copied": "已复制", + "noPanels": "还没有注册任何面板", + "flip": "翻转" + } +} diff --git a/lang/zh/exportPdf.json b/lang/zh/exportPdf.json new file mode 100644 index 00000000..8a877949 --- /dev/null +++ b/lang/zh/exportPdf.json @@ -0,0 +1,66 @@ +{ + "renderFailed": "PDF 导出失败", + "untitledTrack": "未命名赛道", + "footer": { + "brand": "TrackDraw · {pageLabel}", + "pageOf": "第 {pageNumber} / {totalPages} 页", + "pageOverview": "概览", + "pageTrackMap": "赛道图", + "pageBuildSheet": "布场清单" + }, + "sharedView": { + "title": "共享视图", + "scanToOpen": "扫码打开赛道", + "scanDescription": "打开 TrackDraw 规范共享链接,方便手机端审阅和现场讲解。", + "noShare": "未关联已发布共享链接", + "noShareDescription": "如果你希望在布场清单里带上扫码入口,请先发布这个项目。" + }, + "cover": { + "title": "赛事布场清单", + "numberingIncluded": "已含编号", + "numberingHidden": "已隐藏编号", + "statusLabel": "状态", + "statusBuildable": "可布场", + "statusShort": "库存不足", + "missingLabel": "缺口", + "obstaclesLabel": "障碍数", + "intro": "这份布场清单是给比赛日布场组随身携带用的。地图页用于现场摆放参考,物料页用于核对库存,布场顺序页则给出更实际的进场搭建顺序。时间估算基于常见 2-3 人俱乐部布场流程:卸车、摊器材、预组,然后进场摆放与固定。", + "contentsTitle": "内容", + "contentsItem1": "1. 赛道图与场地概览", + "contentsItem2": "2. 物料清单、库存核对与计时点", + "contentsItem3": "3. 布场顺序与比赛日备注", + "summaryTitle": "概要", + "summaryShortage": "{count, plural, one {当前布局在 1 类障碍上超出可用库存。} other {当前布局在 {count} 类障碍上超出可用库存。}} 在把这份布场清单作为最终参考前,请先补齐缺口。", + "summaryBuildable": "当前布局可由已保存的器材库存完成布场。以下页面可直接作为比赛日布场和交接参考。" + }, + "mapPage": { + "title": "赛道图", + "stockShort": "库存差 {count}", + "stockBuildable": "库存检查:可布场", + "numberingShown": "显示编号", + "numberingHidden": "隐藏编号" + }, + "buildSheet": { + "title": "布场清单", + "materialListTitle": "物料清单", + "obstacleColumn": "障碍", + "requiredColumn": "需求", + "availableColumn": "现有", + "missingColumn": "缺口", + "timingPointsTitle": "计时点", + "noTimingPoints": "目前还没有指定计时点。比赛日前交接前,请先在检查器里把门设置为起终点或分段点。", + "setupSequenceTitle": "布场顺序", + "setupIntro": "预计布场用时:约 {min}-{max} 分钟。总体复杂度:{complexity}。{summary}", + "crewStepLabel": "布场步骤", + "groupedTaskLabel": "分组任务", + "obstacleNumberSuffix": "{kind} · 障碍 #{number}", + "minutesSuffix": "{minutes} 分钟", + "noSetupSequenceNumbering": "当前还没有可用的布场顺序。先补一条路径并摆放一些障碍,才能生成更实用的布场清单。", + "noSetupSequenceHidden": "这次导出隐藏了编号。如果你希望布场顺序能按路径编号走,请开启障碍编号。", + "notesTitle": "比赛日备注", + "note1": "布场和飞手讲解时,请以赛道图页作为现场摆放参考。", + "note2": "决定是否按当前布局布场前,请把库存核对结果当作最终依据。", + "note3": "先把赛道主骨架落位,再去固定重力门、目字门等高工作量结构,最后完成常规穿越门。", + "note4": "如果路径或障碍数量发生变化,请重新导出布场清单,确保地图、库存表和布场顺序保持一致。" + } +} diff --git a/lang/zh/index.ts b/lang/zh/index.ts new file mode 100644 index 00000000..315de9eb --- /dev/null +++ b/lang/zh/index.ts @@ -0,0 +1,10 @@ +export { default as editor } from "./editor.json"; +export { default as common } from "./common.json"; +export { default as inspector } from "./inspector.json"; +export { default as dialogs } from "./dialogs.json"; +export { default as landing } from "./landing.json"; +export { default as login } from "./login.json"; +export { default as share } from "./share.json"; +export { default as shapes } from "./shapes.json"; +export { default as setupEstimate } from "./setupEstimate.json"; +export { default as exportPdf } from "./exportPdf.json"; diff --git a/lang/zh/inspector.json b/lang/zh/inspector.json new file mode 100644 index 00000000..1cf4b701 --- /dev/null +++ b/lang/zh/inspector.json @@ -0,0 +1,343 @@ +{ + "tabs": { + "ariaLabel": "检查器面板", + "layout": "布局", + "selection": "选择" + }, + "emptyState": { + "title": "当前未选择内容", + "body": "在画布上选择一个或多个项目后,就能在这里编辑它们的属性。" + }, + "actions": { + "group": "分组", + "continueEditing": "继续", + "connectEnds": "连接", + "deleteShort": "删除", + "duplicateShort": "复制", + "ungroup": "取消分组", + "groupSelection": "选中项分组", + "ungroupSelection": "取消选中项分组", + "lockShort": "锁定", + "unlockShort": "解锁", + "savePreset": "保存为预设", + "joinPaths": "合并路径", + "remove": "移除", + "removeItem": "移除项目" + }, + "hints": { + "locked": "当前项目已在画布上锁定。解锁后才能移动、缩放或继续编辑路径。" + }, + "group": { + "sectionTitle": "分组", + "nameLabel": "分组名称", + "namePlaceholder": "可选分组名称" + }, + "raceTiming": { + "sectionTitle": "计时", + "ariaLabel": "计时角色", + "splitIdLabel": "分段 ID", + "splitIdPlaceholder": "split-1", + "roleStart": "起点", + "roleSplit": "分段", + "metaTimingStart": "计时:起点", + "metaTimingSplit": "计时:{id}", + "metaGrouped": "已分组", + "metaLocked": "已锁定", + "metaEditable": "可编辑" + }, + "transform": { + "sectionTitle": "变换", + "positionLabel": "位置", + "rotationLabel": "旋转(度)", + "colorLabel": "颜色", + "pickColorAriaLabel": "选择颜色", + "editingSubtitle": "正在编辑 {kind} 的属性和摆放。" + }, + "ladder": { + "sectionTitle": "目字门" + }, + "tower": { + "sectionTitle": "日字门" + }, + "catalog": { + "sectionTitle": "目录", + "sourceLabel": "来源", + "sizeLabel": "尺寸", + "officialBadge": "官方" + }, + "dimensions": { + "widthLabel": "宽度({unit})", + "heightLabel": "高度({unit})", + "gridLabel": "网格({unit})", + "thicknessLabel": "厚度({unit})", + "radiusLabel": "半径({unit})", + "poleHeightLabel": "杆高({unit})", + "tiltLabel": "倾角(度)", + "gatesLabel": "门数", + "elevationLabel": "高度({unit})", + "levelsLabel": "层数", + "sizeLabel": "尺寸({unit})", + "openingHeightLabel": "开口高度({unit})" + }, + "label": { + "sectionTitle": "标注", + "textLabel": "文本", + "fontSizeLabel": "字号(px)", + "threeDModeLabel": "3D 模式", + "floatOption": "悬浮(看板)", + "groundOption": "投射到地面" + }, + "multiSelection": { + "sectionTitle": "选择", + "mixedTypesPlaceholder": "混合类型", + "titleSelected": "已选择 {count} 个项目", + "subtitle": "这里可以做批量操作。需要精细编辑时,请回到画布选中单个项目。", + "groupCountMeta": "{count} 个分组", + "joinAvailableMeta": "可合并", + "saveSectionLabel": "保存片段", + "lockedItemsNote": "已锁定的项目不会被改动。" + }, + "polyline": { + "sectionTitle": "路径", + "flowLabel": "流向", + "directionLabel": "方向", + "waypointsTitle": "路径点", + "waypointsSubtitle": "调整每个点及其高度。", + "elevationColumn": "高", + "editColumn": "编", + "insertPointAfter": "在后面插入点", + "removePoint": "移除点", + "addPoint": "添加点" + }, + "listPanel": { + "panelTitle": "赛道项目", + "placedItemsTitle": "已放置项目", + "placedItemsSubtitle": "从列表中选择一个项目。", + "filterPlaceholder": "筛选项目", + "filterAll": "全部", + "filterObstacles": "障碍", + "itemColumn": "项目", + "pathColumn": "路径", + "noItemsMatchFilter": "没有项目匹配当前筛选。", + "routeStatus": { + "off": "关" + } + }, + "layout": { + "mapReference": { + "title": "地图参考", + "fields": { + "opacity": "透明度", + "rotation": "旋转" + }, + "actions": { + "removeTitle": "移除地图参考", + "editTitle": "编辑地图参考", + "addTitle": "添加地图参考", + "edit": "编辑", + "add": "添加地图", + "showTitle": "显示地图参考", + "hideTitle": "隐藏地图参考", + "show": "显示" + } + }, + "sections": { + "routeNumbering": "路径编号", + "inventory": "库存", + "field": "场地" + }, + "field": { + "unitsLabel": "单位", + "renderScaleLabel": "渲染比例", + "renderScaleTooltip": "画布内部密度,单位为每米像素数", + "pxPerMeterAriaLabel": "每米像素数,画布内部比例" + }, + "project": { + "titlePlaceholder": "未命名赛道", + "subtitle": "调整项目标题、场地设置和可用障碍库存。" + }, + "overview": { + "title": "当前布局", + "subtitle": "查看当前布局的已放置项目、路径编号和可布场性。", + "emptyTitle": "还没有放置任何项目", + "emptyDescription": "先在画布上放几个障碍,再回来检查布局并和你的库存做对照。" + }, + "inventory": { + "status": { + "buildable": "可布场", + "short": "库存不足" + }, + "labels": { + "missing": "缺口", + "types": "类型" + }, + "description": "TrackDraw 会把当前布局和这个项目里保存的障碍库存做对比。", + "needCountSuffix": "需求 {count}", + "stockOk": "足够" + }, + "meta": { + "itemsCount": "{count} 个项目", + "buildable": "可布场", + "shortItems": "缺少 {count} 项", + "stockCovered": "库存已覆盖", + "typesShort": "缺 {count} 类", + "numbered": "已编号 {count}", + "numberingIdle": "编号未生成", + "numberingNeedsReview": "编号需复核" + } + }, + "routeNumbering": { + "status": { + "ready": "就绪", + "partial": "需复核", + "noRouteMatches": "无匹配", + "missingRoute": "缺少路径", + "noNumberedObstacles": "无路径障碍", + "empty": "空" + }, + "messages": { + "ready": "已编号 {count} 个路径障碍。", + "partial": "{mapped} / {total} 个路径障碍已编号。", + "noRouteMatches": "找到 {count} 个路径障碍,但都没有离路径足够近。", + "missingRoute": "支持的障碍已经准备好,可以生成比赛路径。", + "noNumberedObstacles": "穿越门、目字门和重力门会出现在路径编号里。", + "empty": "添加路径障碍和路径后即可生成编号。" + }, + "issues": { + "offRoutePrefix": "偏离路径:{names}", + "moreSuffix": " 另有 {count} 个" + }, + "meta": { + "numbered": "已编号", + "issues": "问题", + "raceLine": "比赛路径" + }, + "generate": { + "button": "生成比赛路径", + "regenerateButton": "重新生成比赛路径", + "success": "比赛路径已生成。", + "errors": { + "noObstacles": "无法生成比赛路径——请先添加至少两个受支持的障碍(穿越门、目字门或重力门)。" + }, + "warnings": { + "unsupportedShapes": "{count, plural, one {有 1 个障碍暂不支持,已跳过。} other {有 {count} 个障碍暂不支持,已跳过。}}", + "tooFewObstacles": "再添加一些受支持的障碍,生成的比赛路径会更有参考价值。", + "closeObstacles": "{count, plural, one {有 1 对障碍距离非常近,请检查生成的路径。} other {有 {count} 对障碍距离非常近,请检查生成的路径。}}" + } + } + }, + "elevationChart": { + "noRouteSelected": "未选择路径", + "title": "高度剖面", + "subtitle": "路径高度、着色警告区段和线路审阅提示。", + "openDetailsAria": "打开高度详情", + "detailsTooltip": "高度详情", + "chartAriaLabel": "高度剖面图", + "routeDistance": "{distance} 路径", + "elevationRange": "高度 {min}-{max}", + "moreCount": "另有 {count} 个", + "maneuversHeading": "动作识别", + "maneuverDetected": "在 {range} 附近检测到 {maneuver}", + "apexSuffix": ",顶点 {index}", + "waypointSingle": "路径点 {index}", + "waypointRange": "路径点 {start}-{end}", + "waypointMarkerAria": "在高度剖面中选中路径点 {index}", + "warningMarkerAria": "跳转到 {warning} 的第 {segment} 段", + "timingMarkerAria": "在高度剖面中选中计时点 {label}", + "obstacleMarkerAria": "在高度剖面中选中障碍 {index}", + "routeReviewHeading": "路径审阅", + "warningSegmentsHeading": "警告区段", + "jumpToSegment": "跳转到第 {segment} 段", + "noWarnings": "这条路径当前没有路径警告。", + "normal": "正常", + "timingMarkersLegend": "计时", + "obstacleMarkersLegend": "障碍", + "warningMarkersLegend": "警告", + "maneuvers": { + "powerloop": "Powerloop", + "split-s": "Split-S" + }, + "warnings": { + "stub": { + "summary": "路径至少需要 2 个路径点才能形成路径", + "shortLabel": "路线不完整", + "title": "路径还没画完整", + "problem": "路径至少需要两个路径点,TrackDraw 才能把它当成完整路径来审阅。", + "fix": "再添加一个路径点,或先把当前路径画完。" + }, + "flat": { + "summary": "还没设置高度,3D 预览会保持平面", + "shortLabel": "高度平直", + "title": "还没有设置高度", + "problem": "所有路径点目前都还是 0 米,所以 3D 预览和高度剖面都会保持平的。", + "fix": "如果这条线路需要爬升或下落,请在检查器或 3D 视图里设置路径点高度。" + }, + "steep": { + "summary": "{count, plural, one {路径点 {first} 附近坡度过大} other {{count} 段坡度过大}}", + "shortLabel": "坡度过大", + "title": "高度变化过于突然", + "problem": "有一段线路在很短距离内急升或急降,会让 3D 线路显得很突兀。", + "fix": "把高度变化拉长,或增加一个中间路径点。" + }, + "hairpin": { + "summary": "{count, plural, one {路径点 {first} 处转向过急} other {{count} 个急转}}", + "shortLabel": "急转", + "title": "转向过于紧凑", + "problem": "某个路径点形成了近乎折返的急转,高速飞行时可能很难顺畅通过。", + "fix": "把路径点往外挪,或在转弯前后留出更多空间。" + }, + "close-points": { + "summary": "{count, plural, one {路径点 {first} 附近点距过近} other {{count} 处路径点间距过近}}", + "shortLabel": "点距过近", + "title": "路径点之间太近", + "problem": "两个路径点距离太近,导致中间出现极短的路径段。", + "fix": "删除其中一个点,或把它移远一些。" + }, + "spacing-shift": { + "summary": "{count, plural, one {路径点 {first} 附近节奏突变} other {{count} 处节奏突变}}", + "shortLabel": "节奏突变", + "title": "门距节奏变化过猛", + "problem": "某个路径点前后的距离变化太突然,会让整条线路节奏不均匀。", + "fix": "调整附近路径点,让间距变化更平滑。" + }, + "rhythm-break": { + "summary": "{count, plural, one {路径点 {first} 附近有短修正段} other {{count} 段短修正}}", + "shortLabel": "短修正", + "title": "短修正打断流畅度", + "problem": "较长线路中夹了一个很短的中间线段,容易产生别扭的小摆动。", + "fix": "移动这个点、删除它,或加一个更平顺的过渡点。" + } + } + }, + "mapReference": { + "title": "地图参考", + "subtitle": "选择场地中心并对齐场地轮廓。", + "searchPlaceholder": "搜索地点或粘贴坐标", + "searching": "搜索中", + "enterHint": "回车", + "zoomLabel": "缩放 {zoom}", + "zoomIn": "放大", + "zoomOut": "缩小", + "resetRotation": "重置旋转", + "rotateLeft15": "向左旋转 15 度", + "rotateLeft1": "向左旋转 1 度", + "rotateRight1": "向右旋转 1 度", + "rotateRight15": "向右旋转 15 度", + "latitude": "纬度", + "longitude": "经度", + "rotation": "旋转", + "rotationValue": "{deg} 度", + "view": "视图", + "out": "缩小", + "in": "放大", + "previewZoom": "预览缩放 {zoom}", + "center": "居中", + "useLocation": "使用当前位置", + "locating": "正在定位", + "saveMap": "保存地图", + "locationUnavailable": "当前浏览器不支持定位。", + "locationPermissionDenied": "定位权限被拒绝。", + "locationTimeout": "定位超时。", + "locationError": "无法确定你的位置。" + } +} diff --git a/lang/zh/landing.json b/lang/zh/landing.json new file mode 100644 index 00000000..91798a50 --- /dev/null +++ b/lang/zh/landing.json @@ -0,0 +1,267 @@ +{ + "metadata": { + "homeTitle": "TrackDraw · FPV 赛道设计工具", + "homeSocialTitle": "TrackDraw | FPV 赛道设计工具", + "homeDescription": "TrackDraw 是一款面向 FPV 赛事组织者的穿越机竞速赛道设计工具。按真实比例设计赛道,在 3D 中检查线路流畅度,并分享只读比赛日布局。", + "homeSocialDescription": "用 TrackDraw 按真实比例规划 FPV 赛道,在 3D 中检查线路流畅度,并分享只读比赛日方案。", + "studioTitle": "FPV 赛道设计工具", + "studioSocialTitle": "TrackDraw | FPV 赛道设计工具", + "studioDescription": "打开 TrackDraw,按真实比例设计 FPV 赛道布局,添加门型和障碍,并在 3D 中预览线路流畅度。", + "studioSocialDescription": "按真实比例设计 FPV 穿越机竞速赛道,添加门型和障碍,并在 3D 中预览线路流畅度。" + }, + "header": { + "homeAriaLabel": "首页", + "homeTooltip": "返回主站首页", + "featuresNav": "功能", + "featuresTooltip": "跳到产品概览", + "galleryNav": "图库", + "galleryTooltip": "浏览社区公开布局", + "pricingNav": "方案", + "pricingTooltip": "查看方案与价格", + "faqNav": "FAQ", + "faqTooltip": "查看常见问题与解答", + "logoAriaLabel": "TrackDraw", + "siteNavAriaLabel": "首页、图库和公开站点导航", + "mobileNavAriaLabel": "导航", + "themeAriaLabel": "主题", + "themeTooltip": "切换公开站点主题。", + "languageAriaLabel": "语言", + "languageTooltip": "切换站点语言。", + "openStudio": "打开 Studio" + }, + "hero": { + "eyebrow": "为 FPV 赛事组织者打造", + "headingLine1": "比赛日的顺畅,", + "headingLine2": "从一张好图开始。", + "description": "TrackDraw 是一款基于浏览器的 FPV 穿越机竞速赛道设计工具。你可以在 2D 视图中按比例布场,在 3D 视图中检查线路流畅度,把只读布局分享给飞手和布场组,而且桌面端和移动端都好用。", + "ctaStudio": "打开 Studio", + "ctaFeatures": "查看功能", + "feature1": "官方障碍目录", + "feature2": "赛道片段", + "feature3": "3D 预览与模拟飞行", + "feature4": "赛事布场清单 PDF 与 SVG", + "videoAlt": "TrackDraw FPV 赛道设计工具演示", + "videoDescription": "一个简短的产品演示,展示 TrackDraw 如何帮助 FPV 赛事组织者设计赛道、在 3D 中检查线路,并分享给飞手和布场组。" + }, + "features": { + "sectionTitle": "功能", + "sectionHeading": "设计、检查、分享。", + "sectionDescription": "TrackDraw 让 FPV 赛事组织者能更实际地规划赛道:真实场地尺寸、正确的障碍类型,以及从布局到审阅再到现场交接的一体化流程。", + "trueToScale": { + "title": "真实比例画布", + "description": "设置场地尺寸,按比例摆放障碍,并在与真实场地一致的网格上工作。" + }, + "obstacleCatalog": { + "title": "官方障碍目录", + "description": "放置带真实尺寸的官方 MultiGP 障碍,包括穿越门、标旗、目字门、跨栏和围挡,并获得准确的 3D 渲染。" + }, + "preview3d": { + "title": "更接近实飞的 3D 预览", + "description": "在 3D 中环绕查看、给路径加高度、检查动作弧线,并通过模拟飞行提前发现节奏问题。" + }, + "shareLinks": { + "title": "共享审阅链接", + "description": "把只读布局发给飞手和布场组。游客链接是临时的,账户发布链接则可一直保留到你撤销为止。" + }, + "sections": { + "title": "赛道片段", + "description": "把任意画布选择保存成命名片段,一键再次放置。片段会跟随你的账户在设备间同步。" + }, + "export": { + "title": "布场清单与导出", + "description": "生成包含障碍布局、路径图和布场备注的飞手说明 PDF,同时还能导出 SVG、PNG 和 JSON。" + } + }, + "workflow": { + "sectionEyebrow": "深入看看", + "sectionHeading": "从第一版布局到现场交接。", + "sectionDescription": "最顺手的方式通常是按流程走:先在 2D 里把赛道搭起来,再到 3D 检查,场地临时变化时快速调整,最后把同一份方案交给飞手和布场组。", + "layout": { + "title": "2D 布局", + "subtitle": "先在和真实场地一致的画布上搭赛道。", + "description": "从场地尺寸开始,再摆放布场组和飞手都熟悉的门型与障碍。这样第一版方案就能立刻派上用场,而不是只是一张草图。", + "screenshotAlt": "TrackDraw 2D 赛道编辑器,带障碍目录和赛道方案", + "bullets": [ + "按比例设置场地尺寸", + "穿越门、标旗、锥桶、起飞区和大件障碍", + "用于讲解的路径和标注" + ] + }, + "fineTune": { + "title": "细调", + "subtitle": "场地有变化,也能继续改。", + "description": "好方案必须经得起现场变化。检查器控制项和移动端友好编辑,让你能快速改属性、挪障碍,即使现场条件变了,也能把方案继续用下去。", + "screenshotAlt": "TrackDraw 移动端编辑器,设置面板已展开", + "bullets": [ + "尺寸、旋转和颜色的检查器控制", + "手机和平板也能顺手操作", + "不离开主方案就能快速修正" + ] + }, + "preview": { + "title": "3D 预览", + "subtitle": "先走线,再进场。", + "description": "一键从 2D 平面切到实时 3D 场景。给路径上的每个路径点设置高度,就能把整个垂直剖面模拟出来,在地钉还没入地前先把风险线路看出来。", + "screenshotAlt": "TrackDraw 3D 预览,显示赛道流向与高度", + "bullets": [ + "直接由 2D 设计生成的实时 3D 场景", + "路径路径点支持逐点设置高度", + "检查器内可查看高度剖面图" + ] + }, + "handOff": { + "title": "交接", + "subtitle": "给飞手和布场组同一份当前版本。", + "description": "方案只有别人也能打开才有价值。只读共享链接和导出文件,能让飞手预看、布场组打印、主办方后续复用,看到的都是同一份内容。", + "screenshotAlt": "TrackDraw 只读共享视图在手机上打开", + "gallery": "去图库找灵感", + "bullets": [ + "任何设备都能打开的只读链接", + "适合讲解和打印的 PDF、PNG 与 SVG", + "方便归档与复用的 JSON 导出" + ] + } + }, + "faq": { + "sectionTitle": "FAQ", + "sectionHeading": "这些你可能会关心。", + "items": [ + { + "question": "TrackDraw 是做穿越机赛道设计的吗?", + "answer": "是的。TrackDraw 是一款基于浏览器的 FPV 穿越机竞速赛道设计工具,把按比例布局、3D 审阅、分享和导出放在同一套流程里。" + }, + { + "question": "规划一条 FPV 赛道最好的方式是什么?", + "answer": "先按场地尺寸起图,再摆上你实际有的障碍,然后在 3D 里检查路径,最后再分享或导出。TrackDraw 的整个流程就是围绕这个顺序设计的。" + }, + { + "question": "飞手不用账户也能看布局吗?", + "answer": "可以。共享链接会以只读模式在任何设备上打开,不需要账户,也不用安装 App。发给飞手、裁判或布场组,大家看到的都是同一个已发布版本。" + }, + { + "question": "在现场用平板改图行吗?", + "answer": "可以。编辑器对触摸操作做了适配,所以当现场条件变化时,用手机或平板快速微调是完全可行的。" + }, + { + "question": "支持导出哪些格式?", + "answer": "支持 PDF、PNG、SVG、JSON,以及一条 cinematic FPV 视频导出。既能做打印版 briefing,也能发群里看图,或把项目留档复用。" + }, + { + "question": "一开始就必须注册账户吗?", + "answer": "不用。你可以直接打开 Studio 开始设计。注册账户之后,才会额外获得云端项目、长期发布链接、图库发布和嵌入能力。" + }, + { + "question": "以后办活动还能复用旧布局吗?", + "answer": "可以。你可以把布局中的一部分存成命名预设,一键再次摆放;如果要复用整张图,就把项目导出为 JSON,在新活动开始时再导入。" + }, + { + "question": "TrackDraw 能帮我提前看出路径问题吗?", + "answer": "可以。3D 预览和路径审阅警告会提前暴露出门距不匀、节奏断裂和线路对位问题,让你在分享出去或开始布场前先修掉它们。" + } + ] + }, + "pricing": { + "sectionEyebrow": "方案", + "sectionHeading": "先直接用,需要更多时再注册。", + "sectionDescription": "当前两种方式都免费。游客模式无需注册即可使用核心编辑器;账户模式则带来长期发布、嵌入、图库展示,以及跨设备项目连续性。", + "guest": { + "badge": "无需注册", + "name": "游客", + "price": "免费", + "description": "适合快速草图、一次性的临时审阅链接,以及不登录情况下的本地导出。", + "cta": "打开 Studio" + }, + "account": { + "badge": "账户模式", + "name": "账户", + "price": "免费", + "description": "适合长期发布链接、嵌入、图库、赛道片段、REST API 和跨设备继续编辑。", + "cta": "创建账户" + }, + "features": { + "fullDesigner": "完整赛道设计工具", + "preview2d3d": "2D 与 3D 预览", + "export": "PDF、SVG、PNG 与 JSON 导出", + "shareLinks": "共享链接", + "temporaryLinks": "临时链接", + "publishedLinks": "发布后持续有效,直到撤销", + "shareLinksNote": "游客分享链接是临时的。账户发布链接会一直保留到你撤销为止,也可以直接嵌入网页。", + "embeds": "网页嵌入", + "embedsDetail": "通过 iframe 嵌入任意网站", + "gallery": "发布到图库", + "cloudProjects": "云端同步项目", + "cloudProjectsDetail": "项目跟随账户同步,让你可以在任何设备上继续,从家里的桌面端一路接到现场的手机端。", + "sections": "赛道片段", + "sectionsDetail": "把任意画布选择保存成命名预设,并从预设选择器里再次放置。会跟随账户在多设备间同步。", + "api": "REST API 访问", + "apiDetail": "Bearer 鉴权,支持 API Key 管理" + }, + "categories": { + "design": "设计", + "sharing": "分享", + "projects": "项目", + "integration": "集成" + } + }, + "footer": { + "tagline": "面向 FPV 赛事组织者的浏览器赛道设计工具。按比例画图,3D 预览,分享只读链接,在移动端也能快速改图。", + "productTitle": "产品", + "openStudio": "打开 Studio", + "releaseNotes": "更新日志", + "legalTitle": "法律", + "privacy": "隐私政策", + "terms": "服务条款", + "communityTitle": "社区", + "github": "GitHub", + "dutchDroneSquad": "Dutch Drone Squad", + "reportIssue": "反馈问题", + "supportTitle": "支持项目", + "githubSponsors": "GitHub Sponsors", + "kofi": "Ko-fi", + "copyright": "© {year} Dutch Drone Squad。开源项目。", + "madeWithPre": "用", + "madeWithPost": "为 FPV 社区打造" + }, + "gallery": { + "pageTitle": "FPV 穿越机竞速赛道图库", + "metaTitle": "TrackDraw | FPV 赛道图库", + "metaDescription": "浏览 TrackDraw 社区分享的 FPV 穿越机竞速赛道布局。", + "sectionLabel": "图库", + "headingLine1": "看看别人怎么摆,", + "headingLine2": "启发你的下一条赛道。", + "description": "浏览社区公开分享的 TrackDraw 布局找灵感。精选赛道会优先显示,其次是最新发布的社区作品。", + "grid": { + "recentlyAdded": "刚刚发布", + "fieldSizeNotSet": "未设置场地尺寸", + "unknownPilot": "TrackDraw 飞手", + "featuredBadge": "精选", + "byOwner": "作者:{owner}", + "obstacleCount": "{count} 个障碍", + "published": "发布于 {date}", + "open": "打开", + "emptyTitle": "这里还没有赛道。", + "emptyDescription": "等 TrackDraw 用户选择公开分享后,图库内容就会出现在这里。", + "featuredHeading": "精选", + "featuredSubtitle": "我们挑出来、优先推荐的布局。", + "recentHeading": "最新", + "recentSubtitle": "社区刚刚发布的新布局。", + "loadMore": "加载更多" + } + }, + "notFound": { + "logoAriaLabel": "TrackDraw", + "badge": "错误 404", + "heading": "这个页面飞出赛道了", + "description": "你访问的页面不存在、可能已经移动,或者在门后拐错了方向。最新的赛道或路线应该就在附近。", + "whatToTry": "可以试试", + "step1": "回到 Studio,打开最新的赛道。", + "step2": "如果你是从旧书签进来的,先回首页。", + "step3": "如果页面最近调整过,重新打开最新链接再试一次。", + "goHome": "返回首页", + "openStudio": "打开 Studio" + }, + "screenshotPlaceholder": { + "label": "截图占位", + "plannedAsset": "待补素材" + } +} diff --git a/lang/zh/login.json b/lang/zh/login.json new file mode 100644 index 00000000..973ed815 --- /dev/null +++ b/lang/zh/login.json @@ -0,0 +1,62 @@ +{ + "metadata": { + "title": "登录", + "description": "使用一次性魔法链接登录 TrackDraw。" + }, + "goHome": "返回首页", + "backToStudio": "返回 Studio", + "pageTitle": "TrackDraw 账户", + "heading": "无论你在哪,都能继续你的 TrackDraw 工作。", + "description": "跨设备访问你的赛道布局,让共享赛道绑定到你的账户,并通过简单的邮件链接快速继续。", + "featureDevices": { + "title": "跨设备继续", + "description": "在另一台设备上接着做,不用改变原来的编辑流程。" + }, + "featureOwnership": { + "title": "归属清晰", + "description": "让共享布局和后续账户功能都有明确归属。" + }, + "featureMagicLink": { + "title": "魔法链接兜底", + "description": "支持时可用 passkey 登录,不支持时可退回到邮箱魔法链接。" + }, + "signIn": { + "title": "登录", + "description": "在当前设备上用 passkey 登录,或通过邮箱接收无需密码的魔法链接。", + "checkInboxTitle": "请查收邮箱", + "checkInboxBody": "一次性登录链接已发送到 {email}。可在当前设备或另一台设备上打开继续。如果一时没收到,也请检查垃圾邮件箱。", + "howItWorksTitle": "魔法链接登录方式", + "howItWorksBody": "每封邮件都会带一个一次性登录链接。无需设置密码,直接打开链接即可登录 TrackDraw。", + "useDifferentEmail": "换个邮箱", + "openingPasskey": "正在打开 passkey…", + "signInPasskey": "使用 passkey 登录", + "or": "或", + "emailLabel": "邮箱", + "emailPlaceholder": "you@example.com", + "sendingLink": "正在发送链接…", + "sendLink": "发送登录链接到我的邮箱", + "noPasskeySupport": "当前浏览器这里还不支持 passkey,请改用魔法链接登录。", + "passkeyNotFound": "这个 passkey 没有关联到你的 TrackDraw 账户。请换一个 passkey,或改用邮箱登录链接。", + "passkeyFailed": "passkey 登录失败。", + "checkEmail": "请到邮箱查收登录链接。" + }, + "magicLinkVerify": { + "eyebrow": "安全登录", + "secureSession": "受保护的魔法链接交接", + "needsNewLink": "需要新的链接", + "autoTitle": "正在为你登录", + "autoDescription": "TrackDraw 正在用你邮箱里的登录链接打开工作区。", + "title": "打开 TrackDraw", + "description": "请在当前浏览器继续完成登录。这能防止一次性链接被邮件安全检测自动消耗。", + "statusEmail": "已收到邮件链接", + "statusVerify": "正在验证登录链接", + "statusStudio": "正在打开工作区", + "continue": "继续前往 TrackDraw", + "continuing": "正在继续...", + "requestNewLink": "重新申请链接", + "backToLogin": "返回登录", + "invalidTitle": "这个登录链接不完整", + "invalidDescription": "请重新申请一个登录链接,并从最新的 TrackDraw 邮件中打开它。", + "failed": "这个登录链接无法使用。请重新申请一个新的链接后再试。" + } +} diff --git a/lang/zh/setupEstimate.json b/lang/zh/setupEstimate.json new file mode 100644 index 00000000..ca1d9300 --- /dev/null +++ b/lang/zh/setupEstimate.json @@ -0,0 +1,48 @@ +{ + "complexity": { + "light": "轻量", + "standard": "常规", + "heavy": "重度" + }, + "kinds": { + "trackGroup": "赛道分组", + "crew": "布场组" + }, + "flagsFinalPass": { + "label": "标旗收尾", + "note": "主编号障碍线摆完后,再用标旗完成场地可视化与最终视认检查。" + }, + "conesTrackWalk": { + "label": "赛前走场时放锥桶(可选)", + "note": "锥桶可以放在飞手走场时一起摆放,避免单独再跑一轮;如果你更喜欢提前摆好,现场请预留一点额外时间。" + }, + "crewUnloadStage": { + "label": "卸车并摆放器材", + "note": "先把车上器材卸下,分好地钉和五金件,并把整套器材摊开,避免后面因为预组门架卡住布场节奏。" + }, + "crewPreassemble": { + "label": "预组门架和软质器材", + "noteWithSoftGoods": "在把器材搬进场地前,先把 PVC 门架和旗帜等软质器材预组好,这样进场摆放时可以专注在门距和线路上。", + "noteWithoutSoftGoods": "在把器材搬进场地前,先把 PVC 门架预组好,这样进场摆放时可以专注在门距和线路上。" + }, + "crewRiggingCheck": { + "label": "吊挂与地钉检查", + "note": "在抬升或悬挂较重结构前,先确认锚点、上方线缆和整体净空方案。" + }, + "obstacleNoteWithNumber": "{note} 以地图上的 #{number} 号障碍为摆放参考。", + "summaryHeavy": "{count, plural, one {有 1 个高工作量结构需要在常规门组前先摆放并固定。} other {有 {count} 个高工作量结构需要在常规门组前先摆放并固定。}} 该时间估算基于 2-3 人布场组;如果涉及吊挂或长距离搬运,耗时会继续增加。", + "summaryLight": "大部分布场时间会花在卸车、预组门架,以及由 2-3 人把编号障碍线按顺序摆正落位。", + "crewAssumption": "该时间估算基于 2-3 人布场组,默认按常见俱乐部流程进行:卸车、铺开器材、预组、再进场摆放和固定。", + "obstacleNotes": { + "gate": "等定位结构先摆好后,再把这个穿越门摆正落位。", + "tower": "和其他高工作量结构一起优先摆放并固定。日字门需要稳固支撑、垂直对正,以及足够的分层开口净空。", + "flag": "主障碍就位后,再用标旗补足可视性和边界识别。", + "cone": "最后收尾时用锥桶做边界标记和间距检查。", + "label": "这个项目暂时还没有布场建议。", + "polyline": "这个项目暂时还没有布场建议。", + "startfinish": "优先摆放这个起终点,让起飞和冲线参考先固定下来,再继续铺后面的赛道。", + "ladder": "把它视为高工作量障碍;如果需要吊挂或精细悬挂点,请额外预留时间。", + "divegate": "尽早摆放并固定。重力门通常比标准穿越门更吃对正和锚固。", + "barrier": "主门型和其他障碍摆完后,再布置围挡。" + } +} diff --git a/lang/zh/shapes.json b/lang/zh/shapes.json new file mode 100644 index 00000000..f453379c --- /dev/null +++ b/lang/zh/shapes.json @@ -0,0 +1,48 @@ +{ + "kindLabels": { + "gate": "穿越门", + "tower": "日字门", + "flag": "标旗", + "cone": "锥桶", + "label": "标注", + "polyline": "路径", + "startfinish": "起终点", + "ladder": "目字门", + "divegate": "重力门", + "barrier": "围挡" + }, + "toolLabels": { + "select": "选择", + "grab": "拖移", + "preset": "预设", + "gate": "穿越门", + "tower": "日字门", + "flag": "标旗", + "cone": "锥桶", + "label": "标注", + "polyline": "路径", + "startfinish": "起飞区", + "ladder": "目字门", + "divegate": "重力门", + "barrier": "围挡" + }, + "toolGroups": { + "track": "赛道", + "extra": "附加" + }, + "toolMobileLabels": { + "startfinish": "起飞", + "divegate": "重力门", + "barrier": "围挡" + }, + "mobileStatus": { + "multiSelect": "多选", + "tool": "工具", + "itemsCount": "{count} 个项目" + }, + "canvas": { + "selectionCount": "{count} 个项目", + "selectionFallback": "选择", + "frontLabel": "前方" + } +} diff --git a/lang/zh/share.json b/lang/zh/share.json new file mode 100644 index 00000000..5865a9ec --- /dev/null +++ b/lang/zh/share.json @@ -0,0 +1,58 @@ +{ + "badge": "共享视图", + "backToHome": "返回首页", + "whatToTry": "可以试试", + "openStudio": "打开 Studio", + "error": { + "heading": "这个共享赛道链接已不再受支持", + "description": "TrackDraw 现在使用已发布共享链接,不再支持旧的 URL 内嵌共享格式。这个旧链接已经无法直接打开。", + "whatToDo": "该怎么做", + "step1": "请让发送者从 Studio 重新发布一个新的共享链接。", + "step2": "如果你仍然需要离线交接,请让对方发送一份 JSON 导出,然后在 Studio 里通过导入打开。", + "step3": "新的已发布链接使用规范的 `/share/[token]` 路由,也是当前唯一受支持的只读分享方式。", + "openStudioToImport": "打开 Studio 导入" + }, + "expired": { + "heading": "这个共享链接已过期", + "description": "临时 TrackDraw 共享链接会在创建时选择的时长后过期;账户发布的项目链接则会一直有效,直到所有者主动撤销。如果你还需要这个布局,请让发送者重新发布。", + "step1": "请让发送者重新发布这个赛道,或发送一个账户发布链接来提供更长期的只读访问。", + "step2": "如果需要更长期的交接方式,请让对方发送 JSON 导出。" + }, + "notFound": { + "heading": "无法打开这个共享赛道", + "description": "这个共享链接可能已被所有者撤销,也可能已经过时、不完整,或者复制时出错。请让发送者确认链接仍然有效,或重新发布一个新的链接。如果赛道特别大,JSON 导出会比共享链接更稳妥。", + "step1": "请让发送者确认分享仍然有效,并重新复制最新的共享链接。", + "step2": "尽量直接从原始消息里打开链接。", + "step3": "如果赛道特别大,请改用 JSON 导出;JSON 文件不受 URL 长度限制。" + }, + "viewer": { + "dismissIntro": "关闭共享赛道介绍", + "sharedByReadOnly": "由 {author} 分享。当前为只读模式,不会保存修改。可切换到 {view},或在 Studio 中另存为可编辑副本。", + "readOnlyNoAuthor": "当前为只读模式,不会保存修改。可切换到 {view},或在 Studio 中另存为可编辑副本。", + "openView": "打开 {view}", + "makeEditableCopy": "创建可编辑副本" + }, + "metadata": { + "title": "查看赛道", + "description": "查看这条用 TrackDraw 设计的 FPV 赛道。", + "expiredTitle": "赛道共享已过期", + "expiredDescription": "这个 TrackDraw 共享链接已过期。请让发送者重新发布一个新的链接。", + "retiredTitle": "旧版赛道共享不再受支持", + "retiredDescription": "旧版 TrackDraw 共享格式已不再受支持。请让发送者重新发布一个新的链接。", + "embedTitle": "赛道嵌入", + "embedDescription": "TrackDraw 只读赛道嵌入视图。" + }, + "embedUnavailable": { + "temporaryTitle": "暂时无法嵌入", + "temporaryDescription": "只有账户发布的共享内容才支持嵌入。你可以直接打开共享链接,或登录后发布一个长期可用的嵌入。", + "expiredTitle": "这个嵌入已过期", + "expiredDescription": "这个临时 TrackDraw 共享已经失效。请让所有者发布一个长期有效的账户共享。", + "revokedTitle": "这个嵌入已被撤销", + "revokedDescription": "所有者已经撤销了这个 TrackDraw 共享,因此嵌入的赛道也无法继续查看。", + "missingTitle": "未找到赛道", + "missingDescription": "这个 TrackDraw 嵌入未对应到任何有效的账户发布共享。", + "openShareLink": "打开共享链接", + "signIn": "登录", + "brand": "TrackDraw" + } +} diff --git a/src/components/LanguagePicker.tsx b/src/components/LanguagePicker.tsx index a57f3ed8..0197c4fa 100644 --- a/src/components/LanguagePicker.tsx +++ b/src/components/LanguagePicker.tsx @@ -23,6 +23,7 @@ const localeConfig: Record = { en: { label: "English", abbr: "EN" }, nl: { label: "Nederlands", abbr: "NL" }, de: { label: "Deutsch", abbr: "DE" }, + zh: { label: "简体中文", abbr: "ZH" }, }; function LocaleBadge({ abbr }: { abbr: string }) { diff --git a/src/lib/i18n/locales.ts b/src/lib/i18n/locales.ts index 2d582a65..6f7d8d8d 100644 --- a/src/lib/i18n/locales.ts +++ b/src/lib/i18n/locales.ts @@ -1,4 +1,4 @@ -export const supportedLocales = ["en", "nl", "de"] as const; +export const supportedLocales = ["en", "nl", "de", "zh"] as const; export type SupportedLocale = (typeof supportedLocales)[number]; export const defaultLocale: SupportedLocale = "en";