127070e226
设备名与令牌解耦(presence 按 device_id 键控):
- hub 主键改稳定 device_id(presence / online / kick 用之),peer 路由仍按设备名经 hub 内 clientFor 解析——不动传输核心;Client 加 Name + 连接键,PresenceDevice 加 device_id,sse 传 claims.DeviceID,publishPresence 按 device_id 判在线、name 取自 devices 表
- 改名走已有 PATCH /api/devices/{device_id}(不重铸会话、不换 token、不中断 SSE)+ hub.Rename 更新活跃连接名 + 重广播 presence,全端即时无重复项;sessions 列表 name 用 devices 表覆盖 broker Label;web 改名从重铸迁到 PATCH,iOS 设置页即时改名;DeviceItem / DeviceInfo 按 device_id 键控判本机
- 移除「需移除两次」:publishPresence 不再把「有 device_id 但无 devices 行」的 live 连接当在线(吊销后用未过期 token 重连的僵尸),只显示 code-less 连接;合法重登新建行故正常
- revoke 对无 broker 会话的本地行也清(清掉合成测试 / 幽灵设备);跨端吊销 / 断连一律按 device_id Kick
iOS Broker 应用内登录(免扫码):
- BrokerLogin.swift:ASWebAuthenticationSession 跑 broker /device/authorize + /device/token 的 PKCE 设备授权流(对齐桌面 loopback,自定义 scheme cdrop://auth-callback 回调)→ /api/auth/device-session 代铸出带真名 + 稳定 device_id 的设备会话;DeviceIDStore 跨登录复用 device_id 杜绝重登重复
- LoginView 主登录改 broker 账号登录、扫码降级为可展开备选
iOS 功能补全:
- 设备间消息(引擎 main.ts 单条增量过桥 + onNativeEvent sendMessage;EngineController 消息状态 + 新 MessagesView 会话 UI;后端 /api/message 已就绪)
- 图库发送(PhotosPicker 文件 / 图库菜单 → stagePhotoData → 复用 cdrop-file 发送链路;NSPhotoLibraryUsageDescription)
- 传输历史 / 消息记录持久化(RecordsStore 落 Application Support JSON,跨重启存活)+ 长按删除 + 清空(二次确认)
- 进行中传输主动取消 / 立即切中继(详情页两按钮,复用引擎桥命令)
- 身份愈合(引擎 /api/me 取真实显示名经 identityUpdated 回报 Keychain,修扫码登录「用户显示 UUID」)
- 触发本地网络权限(NWBrowser 浏览 _cdrop._tcp)使 WKWebView WebRTC 取 host 候选
iOS 发送 / 启动修复:
- 发送在读文件即失败:引擎在 https 源下 fetch("cdrop-file://") 跨源 + Range 头触发 CORS 预检被拦 → 改走原生桥 bridgeFileSource / readFileSlice(512 KiB 分块 base64),绕开 CORS、整文件仍不整体进内存
- 冷启重开「missing injected session」:EngineWebView.makeUIView 在 boot 注入前确定性接好 auth,消除注入竞态
桌面 bug 修复:
- 登出静默失败:window.confirm 在 Wails WebView 失效(返回 falsy 致动作被静默跳过)→ 跨平台 DOM 确认弹窗(utils/confirm 的 Promise 式 confirmDialog + ui/ConfirmHost 的 Mantine Modal)取代之;登出改 await 先吊销 broker 会话再清本地(桌面 clearDesktopSession 会丢 Go 侧 refresh 的竞态)
- 扫 iOS 登录二维码被拒「不是本站的码」:wails:// 源永不等 https 站点源 → 桌面放宽 parseLinkApprovalUrl 的 origin 等值校验(仍留 /link 路径 + r/c;批准 r/c 一律发往本端后端、外站码只 404,无开放重定向)
- macOS 本地网络权限触发(localnetwork_darwin NWBrowser + Info.plist 键);相机修复(NSCameraUsageDescription)
web / 跨端:
- presence 透传 device_id + 离线检测按 device_id;会话列表随 presence 防抖重拉(已登出设备自动从列表消失,不再需手动刷新);移除设备 / 清剪贴板 / 撤销令牌 / 登出均改 DOM 确认弹窗
分发 / 签名:
- 真机签名从手动 scaffold 改自动 provisioning(删 Signing.xcconfig,project.yml 去 profile specifier,just ios-device 用 -allowProvisioningUpdates + ASC 团队 key);App Group 改名 group.net.commilitia.Commilitia-Drop(含 entitlements)
- hub 测试加 device_id 键控 + 改名解耦回归(-race)
379 lines
22 KiB
JSON
379 lines
22 KiB
JSON
{
|
|
"app.brand": "Commilitia Drop",
|
|
"deviceType.browser": "Browser",
|
|
"deviceType.macos": "macOS client",
|
|
"deviceType.windows": "Windows client",
|
|
"deviceType.linux": "Linux client",
|
|
"deviceType.ios": "iOS client",
|
|
"deviceType.shortcut": "Shortcut",
|
|
"app.connected": "Online",
|
|
"app.connecting": "Connecting…",
|
|
"app.disconnectedTitle": "Reconnecting",
|
|
"app.disconnectedHint": "Lost connection to the server — retrying automatically. No need to refresh.",
|
|
"auth.sessionLost.title": "Signed out",
|
|
"auth.sessionLost.body": "This device's login was revoked or expired. Please sign in again.",
|
|
"nav.accountMenu": "Account menu",
|
|
"nav.thisDeviceLabel": "This device: ",
|
|
"nav.settings": "Settings",
|
|
"nav.signOut": "Sign out",
|
|
"nav.backToHome": "← Home",
|
|
"nav.language": "语言 / Language",
|
|
"nav.theme": "主题 / Theme",
|
|
"nav.theme.light": "Light",
|
|
"nav.theme.dark": "Dark",
|
|
"nav.theme.system": "Follow system",
|
|
"nav.themeToggle": "Toggle theme",
|
|
"home.greeting": "Hello, {{name}}",
|
|
"home.deviceList.title": "Devices",
|
|
"home.deviceList.onlineSuffix": "({{count}} online)",
|
|
"home.deviceList.showOffline": "Show offline ({{count}})",
|
|
"home.deviceList.hideOffline": "Hide offline",
|
|
"home.deviceList.empty": "No peer devices yet. Open the app in another tab with a different device name (Settings → Device name) to see it here.",
|
|
"home.tabs.file": "File",
|
|
"home.tabs.message": "Message",
|
|
"home.tabs.clipboard": "Clipboard",
|
|
"home.file.dropzone": "Drop a file here or click to browse",
|
|
"home.file.selectedSize": "{{name}} ({{size}})",
|
|
"home.file.rejected": "File rejected",
|
|
"home.cta.selectDeviceAndFile": "Select a device and a file",
|
|
"home.cta.selectFile": "Select a file",
|
|
"home.cta.selectDevice": "Select a device",
|
|
"home.cta.sendTo": "Send to {{name}}",
|
|
"home.message.placeholder": "Type a message to {{name}}…",
|
|
"home.message.placeholderEmpty": "Select a device first",
|
|
"home.message.hint": "Enter to send, Shift+Enter for newline. Max 4 KB.",
|
|
"home.message.title": "Messages",
|
|
"home.message.countSuffix": "({{count}})",
|
|
"home.message.clearAll": "Clear all",
|
|
"home.transfer.active": "Active",
|
|
"home.transfer.history": "Recent transfers",
|
|
"home.clipboard.cloudTitle": "Latest from cloud",
|
|
"home.clipboard.cloudFrom": "From {{name}} · {{time}}",
|
|
"home.clipboard.cloudFromSelf": "From this device · {{time}}",
|
|
"home.clipboard.cloudEmpty": "Nothing synced yet.",
|
|
"home.clipboard.copyToLocal": "Copy to local clipboard",
|
|
"home.clipboard.copyToLocalSuccess": "Copied to local clipboard",
|
|
"home.clipboard.uploadTitle": "Sync local clipboard",
|
|
"home.clipboard.uploadHint": "Plain text only, max 64 KB. Styles (bold / color) are stripped.",
|
|
"home.clipboard.uploadButton": "Upload local clipboard",
|
|
"home.clipboard.uploading": "Uploading…",
|
|
"home.clipboard.uploadEmpty": "Local clipboard is empty",
|
|
"home.clipboard.refresh": "Refresh",
|
|
"home.clipboard.clear": "Clear cloud",
|
|
"home.clipboard.clearConfirm": "Clear the cloud clipboard? All devices will see empty content.",
|
|
"home.clipboard.clearSuccess": "Cloud clipboard cleared",
|
|
"home.clipboard.hidden": "Content hidden — click Reveal to show",
|
|
"home.clipboard.reveal": "Reveal",
|
|
"home.clipboard.hide": "Hide",
|
|
"setup.title": "Name this device",
|
|
"setup.help": "The name is shown to your other devices. Avoid spaces, ≤32 chars.",
|
|
"setup.field": "Device name",
|
|
"setup.save": "Save and continue",
|
|
"login.title": "Sign in",
|
|
"login.dev.help": "Dev mode: pick any user identifier. Two tabs with the same identifier simulate two devices of one user.",
|
|
"login.dev.field": "Dev user id",
|
|
"login.dev.continue": "Continue",
|
|
"login.prod.help": "Sign in via Casdoor.",
|
|
"login.prod.button": "Sign in with Casdoor",
|
|
"oauth.failed": "Sign-in failed",
|
|
"oauth.signingIn": "Signing in…",
|
|
"oauth.missingParams": "Missing 'code' or 'state' query parameter",
|
|
"qr.entry.fromLogin": "Sign in with a QR code",
|
|
"qr.entry.scanNewDevice": "Scan to link a device",
|
|
"qr.scan.title": "Scan to link a device",
|
|
"qr.scan.subtitle": "Point the code shown on the other device at the frame. Approval stays on this signed-in device.",
|
|
"qr.scan.starting": "Starting the camera…",
|
|
"qr.scan.aiming": "Line the code up inside the frame",
|
|
"qr.scan.foreign": "That isn't a sign-in code for this site. Aim at the code from “Sign in with a QR code” on the new device.",
|
|
"qr.scan.noCamera": "This device has no camera available. Use a phone or tablet with a camera to scan instead.",
|
|
"qr.scan.denied": "Camera access was blocked. Allow the camera in your browser's site settings, then reopen this page.",
|
|
"qr.scan.error": "Couldn't open the camera. Try again, or scan from a device with a camera.",
|
|
"qr.scan.howto1": "On the new device, open “Sign in with a QR code” to show a code.",
|
|
"qr.scan.howto2": "Line that code up in the frame above — approval starts automatically.",
|
|
"qr.scan.back": "Back",
|
|
"qr.show.title": "Sign in with a QR code",
|
|
"qr.show.subtitle": "Name this device, then scan the code from a signed-in phone to let it into your account.",
|
|
"qr.show.nameHint": "Shown to your other devices. ASCII characters only.",
|
|
"qr.show.generate": "Generate code",
|
|
"qr.show.scanTitle": "Scan to approve",
|
|
"qr.show.howto1": "Open the camera or cdrop on another signed-in device.",
|
|
"qr.show.howto2": "Scan the code above and approve this device.",
|
|
"qr.show.waiting": "Waiting for approval…",
|
|
"qr.show.approved": "Approved — signing you in…",
|
|
"qr.show.expired": "This code has expired. Generate a new one and scan again.",
|
|
"qr.show.denied": "This request was declined. If that was you, generate a new code and try again.",
|
|
"qr.show.regenerate": "Generate a new code",
|
|
"qr.show.back": "Back to home",
|
|
"qr.show.backToName": "Back",
|
|
"qr.approve.title": "Approve a new device",
|
|
"qr.approve.subtitle": "Confirm this device may sign in to your account. Check the device and origin first.",
|
|
"qr.approve.loading": "Reading device details…",
|
|
"qr.approve.badLink": "This approval link is incomplete. Generate a new code on the device and scan it again.",
|
|
"qr.approve.signInFirst": "Sign in to your account first, then approve this device.",
|
|
"qr.approve.trustLabel": "Trust duration",
|
|
"qr.approve.trust.title": "Trust this device (7 days)",
|
|
"qr.approve.trust.hint": "Skip approval for 7 days, renewed on each use.",
|
|
"qr.approve.once.title": "Just this once (1 hour)",
|
|
"qr.approve.once.hint": "Signed in for 1 hour, then scan again.",
|
|
"qr.approve.scopeFull": "This device signs fully into your account for 7 days: it can send and receive files and messages, and manage your account and devices.",
|
|
"qr.approve.scopeGuest": "Signs in as a limited guest — it can send and receive files and messages, but can't change your account or approve other devices; expires after 1 hour.",
|
|
"qr.approve.nativeFull": "This is a native app device — it signs in with full access (trusted, valid for 7 days). Native apps don't support limited guest sessions.",
|
|
"qr.approve.trustContinue": "Trust & Continue",
|
|
"qr.approve.approve": "Approve sign-in",
|
|
"qr.approve.stepUp.notice": "For security, verify your identity before approving — the button below takes you through a fresh sign-in.",
|
|
"qr.approve.stepUp.button": "Verify and approve",
|
|
"qr.approve.stepUp.rejected": "Identity verification didn't pass or has expired. Verify again to confirm it's you, then approve.",
|
|
"qr.approve.deny": "Decline",
|
|
"qr.approve.doneApprovedTitle": "Approved",
|
|
"qr.approve.doneApproved": "This device is now linked to your account. You can send and receive files on it.",
|
|
"qr.approve.doneDeniedTitle": "Declined",
|
|
"qr.approve.doneDenied": "The request was declined. That device won't sign in to your account.",
|
|
"qr.approve.expiredTitle": "Code expired",
|
|
"qr.approve.expired": "This code is no longer valid. Generate a new one on the device and scan again.",
|
|
"qr.approve.errorTitle": "Something went wrong",
|
|
"qr.approve.error": "That didn't go through. Try again, or generate a new code on the device.",
|
|
"qr.approve.backHome": "Back to home",
|
|
"settings.title": "Settings",
|
|
"settings.currentDevice.title": "This device",
|
|
"settings.deviceName.field": "Device name",
|
|
"settings.deviceName.save": "Save",
|
|
"settings.deviceName.unchanged": "Name is unchanged",
|
|
"settings.deviceName.empty": "Name cannot be empty",
|
|
"settings.deviceName.asciiOnly": "Device name must use ASCII characters only (letters, numbers, symbols).",
|
|
"settings.deviceName.success": "Device name updated",
|
|
"settings.unregister.current": "Unregister this device",
|
|
"settings.unregister.currentHint": "Removes this device's registration and signs you out.",
|
|
"settings.unregister.currentConfirm": "Unregistering this device will remove its registration and sign you out. Continue?",
|
|
"settings.error.delete": "Action failed: {{message}}",
|
|
"settings.guest.badge": "Limited guest",
|
|
"settings.guest.title": "This device is a limited guest",
|
|
"settings.guest.body": "This device is signed in as a limited guest: it can send and receive files, messages, and clipboard, but cannot manage the account, remove devices, or authorize new ones. To get full access, sign in again on your main device.",
|
|
"settings.sessions.title": "Login sessions",
|
|
"settings.sessions.hint": "Every device currently signed in to this account — browser, desktop, and mobile clients; online ones are listed first. Signing a device out ends its session immediately; it must sign in again on that device to continue.",
|
|
"settings.sessions.guestNote": "Limited guests can't manage login sessions. To manage them, sign in fully on your main device.",
|
|
"settings.sessions.empty": "No other login sessions.",
|
|
"settings.sessions.loadError": "Failed to load login sessions.",
|
|
"settings.sessions.retry": "Retry",
|
|
"settings.sessions.current": "This device",
|
|
"settings.sessions.scopeFull": "Full",
|
|
"settings.sessions.scopeGuest": "Limited guest",
|
|
"settings.sessions.kind.oidc": "Account login",
|
|
"settings.sessions.kind.self": "Local account",
|
|
"settings.sessions.kind.guest": "Scanned guest",
|
|
"settings.sessions.lastUsed": "Last active {{time}}",
|
|
"settings.sessions.neverUsed": "Not active yet",
|
|
"settings.sessions.signOut": "Sign out",
|
|
"settings.sessions.signingOut": "Signing out…",
|
|
"settings.sessions.signOutCurrent": "Sign out this device",
|
|
"settings.sessions.confirmOther": "Sign out device \"{{name}}\"? Its session will be invalidated immediately and it must sign in again.",
|
|
"settings.sessions.confirmCurrent": "Signing out this device is the same as logging out. Continue?",
|
|
"settings.sessions.revoked": "Signed that device out",
|
|
"settings.sessions.stepUpRejected": "Identity verification failed or expired. Please try again.",
|
|
"settings.online": "Online",
|
|
"settings.offline": "Offline",
|
|
"settings.lastSeen": "Last seen {{time}}",
|
|
"settings.push.title": "Push notifications",
|
|
"settings.push.hint": "When this page is closed, new files, messages, and transfer results arrive as system notifications; while it's open, you'll see in-app alerts instead.",
|
|
"settings.push.enable": "Enable push",
|
|
"settings.push.enabled": "Enabled",
|
|
"settings.push.disable": "Turn off",
|
|
"settings.push.deniedHint": "Notifications are blocked by the browser. Allow them in this site's settings, then try again.",
|
|
"settings.push.denied": "Notification permission denied",
|
|
"settings.push.enableOk": "Push notifications enabled",
|
|
"settings.push.disableOk": "Push notifications turned off",
|
|
"settings.push.failed": "Couldn't enable push. Please try again.",
|
|
"notify.incoming.title": "Incoming file",
|
|
"notify.incoming.body": "{{sender}} is sending {{file}}",
|
|
"notify.transfer.doneTitle": "Transfer complete",
|
|
"notify.transfer.failedTitle": "Transfer failed",
|
|
"settings.account.title": "Account",
|
|
"settings.account.signOut": "Sign out",
|
|
"settings.desktop.title": "Desktop",
|
|
"settings.desktop.clipboardSync": "Auto-sync clipboard",
|
|
"settings.desktop.clipboardSyncHint": "Copies are uploaded to the cloud, and clipboard updates from your other devices are applied here.",
|
|
"settings.desktop.launchAtLogin": "Launch at login",
|
|
"settings.desktop.launchAtLoginHint": "Start cdrop in the background after you sign in (lives in the menu bar).",
|
|
"settings.desktop.downloadDir": "Download folder",
|
|
"settings.desktop.downloadDirHint": "Received files are saved here. Leave unset to use the system Downloads folder.",
|
|
"settings.desktop.downloadDirChoose": "Change…",
|
|
"settings.desktop.downloadDirReset": "Reset to default",
|
|
"settings.desktop.downloadDirPicker": "Choose download folder",
|
|
"settings.desktop.ttlNote": "For security, cloud clipboard content is cleared automatically after 5 minutes.",
|
|
"settings.shortcut.title": "iOS Shortcut",
|
|
"settings.shortcut.intro": "Issue a long-lived, clipboard-only, revocable token for the iOS Shortcuts app. Even if leaked it can only read/write the clipboard — nothing else.",
|
|
"settings.shortcut.labelField": "Label",
|
|
"settings.shortcut.labelPlaceholder": "e.g. My iPhone",
|
|
"settings.shortcut.issue": "Issue",
|
|
"settings.shortcut.empty": "No tokens issued yet.",
|
|
"settings.shortcut.loadError": "Failed to load tokens.",
|
|
"settings.shortcut.retry": "Retry",
|
|
"settings.shortcut.revoke": "Revoke",
|
|
"settings.shortcut.revoking": "Revoking…",
|
|
"settings.shortcut.revokeConfirm": "Revoking immediately disables any Shortcut using this token. Continue?",
|
|
"settings.shortcut.revoked": "Revoked",
|
|
"settings.shortcut.revokedToast": "Token revoked",
|
|
"settings.shortcut.expired": "Expired",
|
|
"settings.shortcut.expiresAt": "Expires {{date}}",
|
|
"settings.shortcut.lastUsed": "Last used {{time}}",
|
|
"settings.shortcut.neverUsed": "Never used",
|
|
"settings.shortcut.unavailable": "Shortcut tokens are not enabled on the server.",
|
|
"settings.shortcut.tooMany": "You have reached the active-token limit. Revoke an old one first.",
|
|
"settings.shortcut.issueError": "Failed: {{message}}",
|
|
"settings.shortcut.issuedTitle": "Token issued",
|
|
"settings.shortcut.onceWarning": "This token is shown only once. Copy it now — you won't be able to see it again after closing.",
|
|
"settings.shortcut.tokenLabel": "Token (Bearer)",
|
|
"settings.shortcut.copy": "Copy",
|
|
"settings.shortcut.copyFailed": "Copy failed — select and copy manually.",
|
|
"settings.shortcut.copied": "Copied",
|
|
"settings.shortcut.baseLabel": "Server address",
|
|
"settings.shortcut.guideTitle": "Set up on iOS (manual sync)",
|
|
"settings.shortcut.guideIntro": "Build two shortcuts in the iPhone Shortcuts app, filling in the server address and token above on first run:",
|
|
"settings.shortcut.guideDeviceName": "Add header X-Device-Name: your-device-name (ASCII only) to both shortcuts so it shows as one device in the list.",
|
|
"settings.shortcut.guidePullTitle": "Pull (cloud → local clipboard)",
|
|
"settings.shortcut.guidePull1": "'Get Contents of URL': set the URL to your-server-address + /api/clipboard, method GET, add header Authorization: Bearer your-token.",
|
|
"settings.shortcut.guidePull2": "'Get Dictionary Value': key content.",
|
|
"settings.shortcut.guidePull3": "If non-empty, 'Copy to Clipboard'; optionally add 'Show Notification'.",
|
|
"settings.shortcut.guidePushTitle": "Push (local clipboard → cloud)",
|
|
"settings.shortcut.guidePush1": "'Get Clipboard'; stop if empty.",
|
|
"settings.shortcut.guidePush2": "'Get Contents of URL': same URL, method PUT, headers Authorization: Bearer your-token and Content-Type: application/json, JSON body: {\"content\": clipboard, \"content_type\": \"text/plain\"}.",
|
|
"settings.shortcut.guidePush3": "Optionally add 'Show Notification'.",
|
|
"settings.shortcut.guideToleranceNote": "A single run may fail on a flaky network — just retry. The automatic polling version (next phase) retries silently in the background.",
|
|
"settings.shortcut.done": "Done",
|
|
"transfer.savedTo": "Saved to {{path}}",
|
|
"transfer.saveFailed": "Failed to save file: {{error}}",
|
|
"transfer.action.cancel": "Cancel",
|
|
"transfer.action.delete": "Delete",
|
|
"transfer.action.relayNow": "Relay now",
|
|
"transfer.action.details": "Details",
|
|
"transfer.state.PENDING": "Pending",
|
|
"transfer.state.ACCEPTED": "Accepted",
|
|
"transfer.state.P2P_ACTIVE": "Transferring",
|
|
"transfer.state.RELAY_ACTIVE": "Relaying",
|
|
"transfer.state.DONE": "Done",
|
|
"transfer.state.FAILED": "Failed",
|
|
"transfer.state.CANCELLED": "Cancelled",
|
|
"transfer.mode.p2p": "P2P",
|
|
"transfer.mode.relay": "Relay",
|
|
"transfer.phase.initializing": "Initializing…",
|
|
"transfer.phase.waiting_accept": "Waiting for the other side to accept…",
|
|
"transfer.phase.ice_gathering": "Gathering network candidates…",
|
|
"transfer.phase.ice_checking": "Establishing direct tunnel…",
|
|
"transfer.phase.ice_connected": "Tunnel established",
|
|
"transfer.phase.dc_open": "Channel open",
|
|
"transfer.phase.transferring": "Transferring",
|
|
"transfer.phase.completing": "Finalizing…",
|
|
"transfer.phase.fallback_pending": "Falling back to relay…",
|
|
"transfer.phase.relay_uploading": "Uploading via relay",
|
|
"transfer.phase.relay_downloading": "Downloading via relay",
|
|
"transfer.stalled": "Looks stalled ({{seconds}}s without progress)",
|
|
"transfer.bytesProgress": "{{sent}} / {{total}}",
|
|
"transfer.bytesRate": "{{rate}}/s",
|
|
"transfer.debug.toggle": "Debug",
|
|
"transfer.debug.iceGathering": "Gathering",
|
|
"transfer.debug.iceConnection": "Connection",
|
|
"transfer.debug.candidatesLocal": "Local candidates",
|
|
"transfer.debug.candidatesRemote": "Remote candidates",
|
|
"transfer.debug.selectedPair": "Selected pair",
|
|
"transfer.debug.noPair": "No nominated pair yet",
|
|
"transfer.debug.candidatesLine": "host {{host}} · mDNS {{mdns}} · srflx {{srflx}} · prflx {{prflx}} · relay {{relay}}",
|
|
"transfer.debug.dataChannel": "Data channel",
|
|
"transfer.debug.dcLine": "{{state}} · buffered {{buffered}}",
|
|
"time.justNow": "just now",
|
|
"time.secondsAgo": "{{n}}s ago",
|
|
"time.minutesAgo": "{{n}}m ago",
|
|
"time.hoursAgo": "{{n}}h ago",
|
|
"common.dismiss": "Dismiss",
|
|
"common.cancel": "Cancel",
|
|
"common.confirm": "Confirm",
|
|
"errors.messageEmpty": "Message is empty",
|
|
"errors.messageOverflow": "Message exceeds 4 KB; send a file instead",
|
|
"errors.noReceiver": "No receiver selected",
|
|
"errors.devTokenMissing": "Dev mode: VITE_CDROP_DEV_TOKEN is not set in .env.local",
|
|
"errors.noAccessToken": "No access token; user must log in",
|
|
"errors.clipboardUnavailable": "Browser clipboard API unavailable (requires HTTPS + permission)",
|
|
"errors.clipboardOverflow": "Content too large; exceeds {{max}} byte limit",
|
|
"errors.clipboardWriteFailed": "Failed to write to local clipboard: {{message}}",
|
|
"ios.tab.transfer": "Transfers",
|
|
"ios.tab.devices": "Devices",
|
|
"ios.tab.settings": "Settings",
|
|
"ios.login.generating": "Generating code…",
|
|
"ios.login.waiting": "Waiting for approval",
|
|
"ios.login.guide": "Scan to approve from another signed-in device",
|
|
"ios.login.expired": "Code expired — please try again",
|
|
"ios.login.failed": "Sign-in failed — please try again",
|
|
"ios.login.broker": "Sign In",
|
|
"ios.login.brokerStarting": "Opening sign-in…",
|
|
"ios.login.scanAlt": "Sign in by scanning from another device",
|
|
"ios.login.refresh": "Refresh QR Code",
|
|
"ios.login.needFull": "This device needs full access — choose \"Trust this device\" when approving.",
|
|
"ios.send.pickDevice": "Choose a device",
|
|
"ios.transfer.sending": "Sending",
|
|
"ios.settings.engine": "Engine",
|
|
"ios.settings.status": "Status",
|
|
"ios.settings.device": "Device",
|
|
"ios.settings.clipboard": "Clipboard",
|
|
"ios.settings.clipboardNote": "Upload pushes this device's clipboard to your others; Pull writes the latest cloud clipboard.",
|
|
"ios.clipboard.upload": "Upload Clipboard",
|
|
"ios.clipboard.pull": "Pull Clipboard",
|
|
"ios.clipboard.uploading": "Uploading…",
|
|
"ios.clipboard.pulling": "Pulling…",
|
|
"ios.clipboard.uploaded": "Uploaded to cloud clipboard",
|
|
"ios.clipboard.pulled": "Written to clipboard",
|
|
"ios.clipboard.empty": "Clipboard is empty",
|
|
"ios.clipboard.failed": "Clipboard sync failed",
|
|
"ios.devices.remove": "Remove Device",
|
|
"ios.devices.thisDevice": "This device",
|
|
"ios.devices.revoked": "Device removed",
|
|
"ios.devices.revokeFailed": "Remove failed",
|
|
"ios.devices.revokeStepUp": "Re-verify on the web to remove this device",
|
|
"ios.engine.disconnected": "Disconnected",
|
|
"ios.engine.ready": "Ready",
|
|
"ios.transfer.empty": "No transfers yet",
|
|
"ios.transfer.incoming": "Receive",
|
|
"ios.transfer.outgoing": "Send",
|
|
"ios.send.noDevices": "No online devices to send to",
|
|
"ios.devices.empty": "No devices yet",
|
|
"ios.detail.title": "Transfer Details",
|
|
"ios.detail.direction": "Direction",
|
|
"ios.detail.peer": "Peer",
|
|
"ios.detail.state": "State",
|
|
"ios.detail.phase": "Phase",
|
|
"ios.detail.channel": "Channel",
|
|
"ios.detail.size": "Size",
|
|
"ios.detail.progress": "Progress",
|
|
"ios.detail.speed": "Speed",
|
|
"ios.settings.account": "Account",
|
|
"ios.settings.user": "User",
|
|
"ios.settings.logout": "Sign Out",
|
|
"ios.settings.deviceName": "Device Name",
|
|
"ios.settings.deviceNameNote": "Renaming takes effect at once and syncs to all devices, without affecting sign-in.",
|
|
"ios.settings.deviceName.success": "Device name updated",
|
|
"ios.tab.messages": "Messages",
|
|
"ios.send.fromFiles": "From Files",
|
|
"ios.send.fromPhotos": "From Photos",
|
|
"ios.messages.empty": "No messages yet",
|
|
"ios.messages.noPeers": "No device to message",
|
|
"ios.messages.placeholder": "Type a message…",
|
|
"ios.records.clear": "Clear",
|
|
"ios.records.clearConfirm": "Clear all records? This cannot be undone.",
|
|
"ios.transfer.cancel": "Cancel transfer",
|
|
"ios.transfer.forceRelay": "Relay now",
|
|
"ios.settings.deviceCount": "Known Devices",
|
|
"ios.settings.signaling": "Signaling",
|
|
"ios.settings.presenceEvents": "Presence Events",
|
|
"ios.settings.reconnecting": "Reconnecting",
|
|
"ios.settings.lastLog": "Last Log",
|
|
"ios.tab.files": "Files",
|
|
"ios.files.title": "Received Files",
|
|
"ios.files.empty": "No files received yet",
|
|
"ios.files.share": "Share",
|
|
"ios.files.done": "Done",
|
|
"ios.detail.openFile": "Open File",
|
|
"ios.bg.title": "Transferring files",
|
|
"ios.bg.subtitle": "Continues in the background",
|
|
"ios.share.title": "Send {{n}} file(s) to a device",
|
|
"ios.control.upload": "Upload Clipboard",
|
|
"ios.control.pull": "Pull Clipboard",
|
|
"ios.control.sessionLabel": "{{name}} · Control"
|
|
}
|