7bc4594b94
Server (git-diff.js, git-ops.js, worktree.js, routes/lanes.js): - Every lane git call runs with GIT_LITERAL_PATHSPECS=1 and -z porcelain, so a file name is a file name, never a glob, and odd names parse correctly. - A lane that is a subdirectory of its repo sees and acts on ONLY its own paths (reads, stage/unstage/discard/commit, and gitFacts.files). - Any file named settings.local.json (the lane's router API key) is refused as 403 ESECRET on disk (symlinks resolved) and at every committed rev. - commit refuses with 400 ESTAGEDOUTSIDE while anything outside the lane is staged (checked with --no-renames, so a move into the lane is caught). - push resolves its remote (upstream → only remote → origin), else 400 ENOREMOTE; unstaging a rename unstages both halves. - Git writes serialize on the repo's git dir, not the lane id, so two lanes on one index cannot collide on index.lock. - git refusing an action (a numeric exit status) answers 409 EGIT with git's stderr instead of a 500 carrying a numeric code; one shared parseNumstat. Client: - Changes panel: no stale answer from a previous lane or an older quick-open query repaints the panel; discard disarms on lane switch; Enter on a file path opens it instead of folding; bounded tree expand-all. - Large diffs (> 500 rows) render only the visible rows, with stable width and in-sync split columns; mirrored split scroll is clamped. - Git bar: no "in sync" without counts, a clear no-remote message, branch list re-read after a switch, and no previous-lane facts after a lane switch. - Tooltips no longer flicker or close on unrelated scrolls.
246 lines
16 KiB
JSON
246 lines
16 KiB
JSON
{
|
||
"action.clear": "Dọn trạng thái",
|
||
"action.forget": "Xóa lane",
|
||
"action.purge": "Xóa lịch sử",
|
||
"action.reset": "Đặt lại worktree",
|
||
"action.start": "Bắt đầu",
|
||
"action.stop": "Dừng",
|
||
"actionError": "Thao tác làn đường thất bại: {{message}}",
|
||
"actionErrorUnknown": "Lỗi không xác định",
|
||
"actions.agentsInstall": "Cài agent",
|
||
"actions.agentsInstallResult": "Đã cài: {{files}}",
|
||
"actions.busy": "…",
|
||
"actions.mcpSync": "Đồng bộ MCP",
|
||
"actions.mcpSyncResult": "Đã đồng bộ: {{servers}}",
|
||
"actions.syncCheck": "Kiểm tra đồng bộ dev",
|
||
"actions.syncCheckClean": "DEV_DELTA: {{count}} file, trùng: {{overlap}}",
|
||
"actions.syncCheckCollision": "Trùng migration: {{file}} → đổi tên thành {{suggestion}}",
|
||
"actions.tools": "Công cụ",
|
||
"actions.writeDocs": "Tạo/cập nhật file readme",
|
||
"actions.writeDocsHint": "Mở terminal của lane này và chạy skill /sg-all:sg-md-writing: tạo CLAUDE.md/AGENTS.md nếu chưa có, hoặc cập nhật ngắn gọn nếu đã có.",
|
||
"actions.writeDocsResult": "Đã chạy — xem terminal của lane này.",
|
||
"add": "Thêm lane",
|
||
"addLane": "Tạo lane từ một thư mục làm việc",
|
||
"addLaneBaseLabel": "Nhánh để tạo nhánh mới",
|
||
"addLaneBranchBusy": "{{branch}} — đang được checkout tại {{path}}",
|
||
"addLaneBranchExistingHint": "Worktree checkout đúng nhánh này — không tạo nhánh mới, không cần nhánh gốc.",
|
||
"addLaneBranchExistingLabel": "Nhánh cần checkout",
|
||
"addLaneBranchHintAuto": "Để trống để server tự đặt tên nhánh theo tiêu đề.",
|
||
"addLaneBranchLabel": "Tên nhánh mới",
|
||
"addLaneBranchModeCurrent": "Nhánh hiện tại",
|
||
"addLaneBranchModeCurrentHint": "Làm thẳng trên nhánh repo đang mở (main) — không tạo worktree, không fork nhánh. Chuyển sang chế độ Repo.",
|
||
"addLaneBranchModeExisting": "Nhánh có sẵn",
|
||
"addLaneBranchModeNew": "Nhánh mới",
|
||
"addLaneBranchPlaceholder": "Tự động — đặt theo tiêu đề",
|
||
"addLaneNoBranches": "Repo này chưa có commit nào — worktree sẽ bắt đầu trống.",
|
||
"addLaneNoFreeBranches": "Mọi nhánh của repo này đều đang nằm trong một worktree. Hãy tạo nhánh mới.",
|
||
"addLaneExists": "Thư mục này đã có lane: #{{id}} {{title}}. Lane đó đã được chọn; hãy chọn thư mục khác để thêm lane mới.",
|
||
"addLaneNotARepo": "Chưa phải repo git (hoặc không có quyền đọc).",
|
||
"addLaneProvisionFailed": "Tạo worktree thất bại hoặc quá thời gian chờ — lane vẫn tồn tại nhưng tự động thiết lập đã bị bỏ qua. Kiểm tra git facts của lane, rồi chạy thiết lập agents/mcp thủ công.",
|
||
"addLaneRepoHint": "Một repo git có sẵn. Dashboard tự tạo worktree mới cho lane, không phải thư mục bạn chọn.",
|
||
"addLaneRepoHintAdopt": "Một thư mục có sẵn. Dashboard theo dõi nguyên trạng — không tạo worktree, không tạo nhánh mới.",
|
||
"addLaneRepoLabel": "Repo nguồn",
|
||
"addLaneRepoLabelAdopt": "Thư mục",
|
||
"addLaneRouterCustom": "AI Router",
|
||
"addLaneRouterEmptyHint": "Chưa lưu profile router nào — thêm ở Settings › AI Router.",
|
||
"addLaneRouterHint": "Native là gọi thẳng API của hãng. Chọn profile AI Router thì cấu hình được ghi vào config riêng của lane này lúc tạo.",
|
||
"addLaneRouterLabel": "Endpoint model",
|
||
"addLaneRouterLoading": "Đang tải danh sách router…",
|
||
"addLaneRouterNative": "Native",
|
||
"addLaneRouterProfileLabel": "Profile router",
|
||
"addLaneSetupAgents": "Agent",
|
||
"addLaneSetupClose": "Đóng",
|
||
"addLaneSetupMcp": "MCP server",
|
||
"addLaneSetupProfile": "Profile",
|
||
"addLaneSetupRouter": "Endpoint model",
|
||
"addLaneSetupTitle": "Thiết lập",
|
||
"addLaneTitleLabel": "Tiêu đề",
|
||
"addLaneTitlePlaceholder": "Không bắt buộc",
|
||
"addLaneToolClaude": "Claude Code",
|
||
"addLaneToolCodex": "Codex",
|
||
"addLaneToolHint": "CLI nào điều khiển lane này. Không thể thay đổi sau này.",
|
||
"addLaneWorktreeDetached": "(detached)",
|
||
"addLaneWorktreesMore": "+{{count}} nữa",
|
||
"addLaneWorktreesTitle": "Worktree của repo này ({{count}})",
|
||
"browse.button": "Duyệt",
|
||
"browse.empty": "Thư mục này trống.",
|
||
"browse.select": "Chọn thư mục này",
|
||
"browse.title": "Duyệt chọn thư mục",
|
||
"changes.back": "Tất cả file",
|
||
"changes.bin": "bin",
|
||
"changes.binary": "File nhị phân — không hiển thị.",
|
||
"changes.collapse": "Thu gọn bảng thay đổi",
|
||
"changes.collapseAll": "Thu gọn tất cả file",
|
||
"changes.collapseAllFolders": "Thu gọn tất cả thư mục",
|
||
"changes.collapseContext": "Bớt dòng xung quanh",
|
||
"changes.collapseFile": "Thu gọn {{path}}",
|
||
"changes.commitPlaceholder": "Nội dung commit",
|
||
"changes.commitStaged": "Commit {{count}} file đã stage",
|
||
"changes.conflicted": "xung đột",
|
||
"changes.discard": "Bỏ thay đổi của file này",
|
||
"changes.discardConfirm": "Bấm lần nữa để bỏ — không thể hoàn tác",
|
||
"changes.empty.branch": "Nhánh này không khác nhánh gốc.",
|
||
"changes.empty.commit": "Commit này không đổi file nào.",
|
||
"changes.empty.work": "Không có gì chưa commit.",
|
||
"changes.expand": "Mở bảng thay đổi",
|
||
"changes.expandAll": "Mở tất cả file",
|
||
"changes.expandAllFolders": "Mở tất cả thư mục",
|
||
"changes.expandContext": "Hiện thêm dòng xung quanh",
|
||
"changes.expandFile": "Mở {{path}}",
|
||
"changes.fileGone": "File này không còn thay đổi nào trong phạm vi đang xem.",
|
||
"changes.fileTruncated": "Diff bị cắt bớt — mở file trong editor để xem phần còn lại.",
|
||
"changes.layout.split": "Hai cột",
|
||
"changes.layout.unified": "Một cột",
|
||
"changes.loading": "Đang đọc working copy…",
|
||
"changes.mode.browse": "Tệp",
|
||
"changes.mode.history": "Lịch sử",
|
||
"changes.mode.overview": "Tổng quan",
|
||
"changes.mode.review": "Xem toàn bộ",
|
||
"changes.noCommits": "Chưa có commit nào.",
|
||
"changes.noHunks": "Không có thay đổi nội dung.",
|
||
"changes.noMatches": "Không có file nào khớp.",
|
||
"changes.openFile": "Mở {{path}}",
|
||
"changes.quickOpen": "Tìm file theo đường dẫn…",
|
||
"changes.range.branch": "So nhánh gốc",
|
||
"changes.range.branchHint": "base...HEAD — toàn bộ phần nhánh của lane này thêm vào so với nhánh gốc",
|
||
"changes.range.work": "Chưa commit",
|
||
"changes.range.workHint": "Working tree so với HEAD — những gì vừa sửa mà chưa commit",
|
||
"changes.refresh": "Tải lại",
|
||
"changes.resize": "Đổi độ rộng khung changes (kế tiếp: {{size}})",
|
||
"changes.size.compact": "Hẹp",
|
||
"changes.size.half": "50 / 50",
|
||
"changes.size.wide": "70 / 30",
|
||
"changes.stage": "Đưa file này vào index",
|
||
"changes.title": "Thay đổi",
|
||
"changes.truncated": "Một phần thay đổi đã bị lược bớt để bảng này còn mượt.",
|
||
"changes.unavailable": "Thư mục của lane này không phải repo git đọc được.",
|
||
"changes.unstage": "Bỏ file này khỏi index",
|
||
"changes.wholeFile": "Toàn bộ file",
|
||
"confirmRemoveCancel": "Hủy",
|
||
"confirmRemoveConfirm": "Xóa",
|
||
"confirmRemoveMessage": "Hành động này không thể hoàn tác.",
|
||
"confirmRemoveTitle": "Xóa làn đường?",
|
||
"countDead": "đã chết",
|
||
"countNeedsYou": "cần bạn",
|
||
"countRunning": "đang chạy",
|
||
"countTotal": "làn đường",
|
||
"destructive.blocked.adopted": "Đây là thư mục đã nhận và không thể thay đổi bằng thao tác worktree.",
|
||
"destructive.blocked.missing": "Thư mục làn đường không còn, nên không thể tiếp tục thao tác này.",
|
||
"destructive.blocked.unreadable": "Không thể đọc thư mục làn đường như một Git worktree, nên không thể tiếp tục thao tác này.",
|
||
"destructive.cancel": "Hủy",
|
||
"destructive.count.bytesEstimate": "Dung lượng giải phóng (ước tính)",
|
||
"destructive.count.dirty": "Tệp đã sửa",
|
||
"destructive.count.events": "Sự kiện",
|
||
"destructive.count.head": "HEAD",
|
||
"destructive.count.sessions": "Phiên",
|
||
"destructive.count.tokenRows": "Dòng token",
|
||
"destructive.count.unpushed": "Commit chưa đẩy",
|
||
"destructive.count.untracked": "Tệp chưa theo dõi",
|
||
"destructive.force": "Tôi hiểu các commit chưa đẩy này sẽ bị loại bỏ.",
|
||
"destructive.loading": "Đang tải trạng thái hiện tại…",
|
||
"destructive.notice.activeSessionSkipped": "Một hoặc nhiều phiên trong làn đường này vẫn đang hoạt động và sẽ được giữ lại, không bị dọn.",
|
||
"destructive.notice.adopted": "Lane này trỏ tới thư mục của bạn. Chỉ bản ghi trên bảng điều khiển bị xóa — thư mục và các tệp bên trong không bị thay đổi.",
|
||
"destructive.notice.missing": "Thư mục lane đã không còn. Việc xóa chỉ bỏ bản ghi trên bảng điều khiển; không tệp nào trên đĩa và không bản ghi Git nào bị đụng tới.",
|
||
"destructive.notice.unreadable": "Không thể đọc thư mục lane như một Git worktree. Điều đó không ảnh hưởng gì: chỉ bản ghi trên bảng điều khiển bị xóa, thư mục không bao giờ bị đụng tới.",
|
||
"destructive.purge.confirm": "Xóa lịch sử",
|
||
"destructive.purge.message": "Hãy xác nhận các số liệu hiện tại trước khi xóa lịch sử phiên đã lưu.",
|
||
"destructive.purge.title": "Xóa lịch sử làn đường?",
|
||
"destructive.remove.confirm": "Xóa bản ghi lane",
|
||
"destructive.remove.message": "Chỉ bản ghi của lane trên bảng điều khiển bị xóa. Working tree, nhánh và cơ sở dữ liệu của lane được giữ nguyên — bạn tự xóa nếu thực sự muốn bỏ chúng.",
|
||
"destructive.remove.title": "Xóa bản ghi lane?",
|
||
"destructive.reset.confirm": "Đặt lại worktree",
|
||
"destructive.reset.message": "Hãy xác nhận các số liệu hiện tại trước khi đặt lại worktree này.",
|
||
"destructive.reset.survives": "Các tệp Git bỏ qua như node_modules và .env được giữ lại; các tệp chưa theo dõi sẽ bị xóa.",
|
||
"destructive.reset.title": "Đặt lại worktree được quản lý?",
|
||
"destructive.warning.no-remote": "Chưa cấu hình Git remote, nên không có bản sao lưu ở xa. Bạn vẫn có thể tiếp tục.",
|
||
"empty.body": "Mỗi lane là một thư mục làm việc mà dashboard theo dõi — pipeline, session và terminal của nó. Bấm vào bất kỳ đâu trên ô này để thêm, hoặc từ terminal:",
|
||
"empty.title": "Chưa có lane nào",
|
||
"features.archived": "đã lưu trữ",
|
||
"features.live": "Phiên bản trực tiếp",
|
||
"features.viewingArchived": "Xem tính năng đã lưu trữ \"{{slug}}\" — lane tiếp tục chạy; đây là ảnh chụp nhanh chỉ đọc.",
|
||
"git.filesHeading_one": "{{count}} tệp thay đổi",
|
||
"git.filesHeading_other": "{{count}} tệp thay đổi",
|
||
"git.filesMore_one": "… và {{count}} tệp nữa",
|
||
"git.filesMore_other": "… và {{count}} tệp nữa",
|
||
"git.noDelta": "mới",
|
||
"git.uncommitted": "{{dirty}} đã sửa · {{untracked}} chưa theo dõi",
|
||
"gitBar.branchCreate": "Tạo",
|
||
"gitBar.branchCreateHint": "Tạo nhánh này mà không chuyển sang",
|
||
"gitBar.branchDelete": "Xoá",
|
||
"gitBar.branchDeleteHint": "Xoá nhánh đang chọn (git từ chối nếu còn commit chưa merge)",
|
||
"gitBar.branchNewPlaceholder": "Tên nhánh mới…",
|
||
"gitBar.cancel": "Huỷ",
|
||
"gitBar.commit": "Commit",
|
||
"gitBar.commitAll_one": "Commit {{count}} file",
|
||
"gitBar.commitAll_other": "Commit {{count}} file",
|
||
"gitBar.commitHint": "Stage tất cả rồi commit",
|
||
"gitBar.commitNothing": "Không có gì để commit",
|
||
"gitBar.commitPlaceholder": "Nội dung commit",
|
||
"gitBar.commitStaged": "Commit phần đã stage",
|
||
"gitBar.commitStagedHint": "Chỉ commit những file đã stage ở thẻ Changes",
|
||
"gitBar.fetch": "Fetch",
|
||
"gitBar.fetchHint": "git fetch --all --prune",
|
||
"gitBar.errNoRemote": "Không có remote để push hoặc pull. Hãy thêm remote, hoặc đặt upstream cho nhánh này, trong terminal.",
|
||
"gitBar.inSync": "đã đồng bộ",
|
||
"gitBar.newLane": "Tạo lane mới",
|
||
"gitBar.newLaneHeld": "Nhánh đã được checkout tại {{path}} — git chỉ cho một worktree trên mỗi nhánh",
|
||
"gitBar.newLaneHint": "Mở Add lane để tạo worktree anh em trên nhánh đang chọn",
|
||
"gitBar.noUpstream": "chưa push",
|
||
"gitBar.pull": "Pull",
|
||
"gitBar.pullHint": "git pull --ff-only — nhánh đã rẽ nhánh thì báo lỗi chứ không merge",
|
||
"gitBar.push": "Push",
|
||
"gitBar.pushHint": "git push -u origin HEAD",
|
||
"gitBar.switch": "Nhánh",
|
||
"gitBar.switchApply": "Chuyển nhánh",
|
||
"gitBar.switchCurrent": "hiện tại",
|
||
"gitBar.switchDirty": "Hãy commit hoặc bỏ các thay đổi chưa commit trước",
|
||
"gitBar.switchHint": "Đổi worktree này sang nhánh local khác",
|
||
"gitBar.switchLabel": "Đổi nhánh…",
|
||
"kind.adopted": "đã nhận",
|
||
"kind.managed": "được quản lý",
|
||
"laneDetail.hide": "Ẩn chi tiết",
|
||
"laneDetail.show": "Chi tiết lane",
|
||
"locks.held_one": "Đang giữ {{count}} khóa",
|
||
"locks.held_other": "Đang giữ {{count}} khóa",
|
||
"mode.repo": "Repo",
|
||
"mode.repoHint": "Gắn thẳng thư mục này làm lane, nguyên trạng — không worktree, không nhánh mới.",
|
||
"mode.worktree": "Worktree",
|
||
"mode.worktreeHint": "Tạo worktree git mới + nhánh mới từ repo nguồn.",
|
||
"moreActions": "Thêm hành động",
|
||
"pipelinePicker.label": "Mẫu pipeline",
|
||
"pipelinePicker.stageMismatch": "Stage \"{{stage}}\" không khớp node nào trong \"{{pipeline}}\" — khai báo một trong: {{nodes}}",
|
||
"preflightError": "Không thể tải trạng thái làn đường hiện tại.",
|
||
"preflightErrorWithMessage": "Không thể tải trạng thái làn đường hiện tại: {{message}}",
|
||
"proof.ticketReport": "Báo cáo nhiệm vụ",
|
||
"renameLabel": "Đổi tên làn đường này",
|
||
"reorderHint": "Kéo để sắp xếp · Alt+←/→",
|
||
"router.applied": "Đã áp dụng.",
|
||
"router.appliedRestart": "Đã áp dụng, nhưng phiên đang chạy trong lane này vẫn dùng model cũ — khởi động lại phiên để có hiệu lực.",
|
||
"router.label": "Model",
|
||
"router.restart": "Khởi động lại",
|
||
"router.restarted": "Đã khởi động lại phiên với router mới.",
|
||
"runtime.boot": "▶ chạy",
|
||
"runtime.busy.down": "đang dừng…",
|
||
"runtime.busy.up": "đang khởi động…",
|
||
"runtime.down": "tắt",
|
||
"runtime.healthy": "khoẻ",
|
||
"runtime.partial": "một phần",
|
||
"runtime.slot": "slot {{slot}}",
|
||
"runtime.steppedAsideTitle": "Đã lùi khỏi {{expected}} vì cổng đó đang bận",
|
||
"runtime.stop": "■ dừng",
|
||
"runtime.toggleTitle": "Chạy hoặc dừng stack ứng dụng của lane này (khác với phiên Claude của nó)",
|
||
"splitView.emptyPane": "Chưa chọn lane cho ô này.",
|
||
"splitView.paneCount": "{{count}} ô",
|
||
"splitView.paneLaneLabel": "Bộ chọn lane cho ô",
|
||
"splitView.pickLane": "Chọn lane",
|
||
"status.failed": "thất bại",
|
||
"status.idle": "rảnh",
|
||
"status.provisioning": "đang khởi tạo",
|
||
"status.running": "đang chạy",
|
||
"statusDead": "ĐÃ CHẾT",
|
||
"title": "Làn đường",
|
||
"tooltipStart": "Tạo một lần chạy ở chế độ hội thoại mà không có lời nhắc ban đầu; được điều khiển từ CLI hoặc qua tin nhắn",
|
||
"worktrees.heading_one": "{{count}} worktree",
|
||
"worktrees.heading_other": "{{count}} worktree"
|
||
}
|