From a369b0e2dc33b8b25a4dd3aa7862325f6b3c2947 Mon Sep 17 00:00:00 2001 From: minhn4 Date: Sun, 23 Aug 2026 19:35:15 +0700 Subject: [PATCH 1/2] feat: add Vietnamese locale across app, console, and stats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds vi as a third locale alongside en and es, translated from English: - New dictionaries: packages/app (1,126 keys), packages/console (716), packages/stats (228), packages/ui (172), packages/desktop renderer (21) - Locale registries extended with vi: app language context and entry, desktop renderer i18n, console i18n + language lib (shared by stats), enterprise app; "Tiếng Việt" label and vi accept-language detection - Console docs routing maps vi to the published /docs/vi locale - Terminology captured in .codev/glossary/vi.md: UI chrome translated, common dev terms kept in English, user addressed as "bạn" - translate-app script: vi registered; glossaryFile repointed from the stale .opencode/glossary/ to .codev/glossary/ so glossaries load - e2e locale-projection spec now also asserts the vi projection - DIVERGENCES.md rows updated + new Vietnamese row; CHANGELOG entry Co-Authored-By: Claude Fable 5 --- .codev/glossary/vi.md | 53 + CHANGELOG.md | 12 + DIVERGENCES.md | 5 +- ...session-timeline-locale-projection.spec.ts | 5 +- packages/app/src/context/language.tsx | 8 +- packages/app/src/entry.tsx | 6 +- packages/app/src/i18n/en.ts | 1 + packages/app/src/i18n/es.ts | 1 + packages/app/src/i18n/vi.ts | 1248 +++++++++++++++++ packages/console/app/src/i18n/index.ts | 2 + packages/console/app/src/i18n/vi.ts | 839 +++++++++++ packages/console/app/src/lib/language.ts | 9 +- packages/desktop/src/renderer/i18n/index.ts | 8 +- packages/desktop/src/renderer/i18n/vi.ts | 28 + packages/enterprise/src/app.tsx | 7 +- packages/stats/app/src/i18n.ts | 2 + packages/stats/app/src/i18n/vi.ts | 235 ++++ packages/ui/src/i18n/vi.ts | 197 +++ script/translate-app.test.ts | 6 +- script/translate-app.ts | 8 +- 20 files changed, 2663 insertions(+), 17 deletions(-) create mode 100644 .codev/glossary/vi.md create mode 100644 packages/app/src/i18n/vi.ts create mode 100644 packages/console/app/src/i18n/vi.ts create mode 100644 packages/desktop/src/renderer/i18n/vi.ts create mode 100644 packages/stats/app/src/i18n/vi.ts create mode 100644 packages/ui/src/i18n/vi.ts diff --git a/.codev/glossary/vi.md b/.codev/glossary/vi.md new file mode 100644 index 0000000000..b8a766e0af --- /dev/null +++ b/.codev/glossary/vi.md @@ -0,0 +1,53 @@ +# vi Glossary + +## Sources + +- Fork decision (2026-08-23): keep common developer terms in English; translate UI chrome fully. + +## Do Not Translate (Locale Additions) + +- `OpenCode`, `OpenCode Zen`, `OpenCode Go`, `CoDev`, `CoDev Hub` (product names; preserve casing) +- `CLI`, `TUI`, `MCP`, `OAuth`, `LSP`, `API` +- Common developer terms stay in English: `session`, `prompt`, `token`, `commit`, `branch`, `worktree`, `agent`, `model`, `sandbox`, `cache`, `provider`, `terminal`, `repo`/`repository`, `pull request`, `merge`, `rebase`, `diff`, `checkpoint`, `webview` +- Commands, flags, file paths, keybinds, and code literals (keep exactly as written) + +## Preferred Terms + +- Settings → "Cài đặt" +- Appearance → "Giao diện" +- Theme → "Chủ đề" (distinct from Appearance; both appear as sibling settings rows) +- Color scheme → "chế độ màu" +- Context (LLM context window) → keep "context" in English +- Steer (mid-run follow-up) → "Điều hướng ngay" +- Language → "Ngôn ngữ" +- Update → "Cập nhật" +- Install → "Cài đặt" +- Download → "Tải xuống" +- Upload → "Tải lên" +- Delete → "Xóa" +- Cancel → "Hủy" +- Retry → "Thử lại" +- Undo/Redo → "Hoàn tác"/"Làm lại" +- Copy/Paste → "Sao chép"/"Dán" +- Share → "Chia sẻ" +- Search → "Tìm kiếm" +- Permission(s) → "quyền" +- Tool → "công cụ" in flowing prose descriptions; keep "tool" in compact technical labels (e.g. "Lệnh gọi tool") +- Error → "lỗi" +- Workspace → "workspace" (keep English; recurring identifier in the product) +- File → "tệp"; Folder/Directory → "thư mục" +- Sign in / Log in → "Đăng nhập"; Sign out → "Đăng xuất" + +## Guidance + +- Address the user as "bạn"; keep tone clear, direct, and friendly in UI labels. +- Use Vietnamese sentence casing: capitalize only the first word of a label or sentence (and proper nouns) — no English-style Title Case. +- Prefer natural Vietnamese phrasing over literal translation; keep labels short. +- Keep `{{placeholder}}` tokens exactly as in the English source. +- Docs links may point at the Vietnamese docs locale (`/vi`), which is published. + +## Avoid + +- Avoid translating product and protocol names that are fixed identifiers. +- Avoid mixing multiple Vietnamese terms for the same recurring UI action once a preferred term is established (e.g. always "Hủy" for Cancel, never "Bỏ qua"). +- Avoid stiff, overly formal register ("quý khách", "vui lòng" everywhere) — one "vui lòng" per message at most, only where English says "please". diff --git a/CHANGELOG.md b/CHANGELOG.md index de113c6ecd..423c46534e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ file is the human-readable summary of what changed and why. ## Unreleased +### Language + +- **CoDev speaks Vietnamese** — the desktop and web app, the shared UI + components, the console, and the stats site are now fully translated into + Vietnamese alongside English and Spanish. The app follows your system + language automatically (`vi` in your browser or OS picks it up) and "Tiếng + Việt" appears in every language picker; docs links for Vietnamese users + point at the published Vietnamese docs. Terminology follows the convention + Vietnamese developers expect: UI chrome is translated, while terms like + session, prompt, token, commit, branch, and agent stay in English (the + glossary lives in `.codev/glossary/vi.md`). + ### Privacy - **CoDev no longer asks for access to your Downloads, Photos, Desktop, or diff --git a/DIVERGENCES.md b/DIVERGENCES.md index 39bcb9720a..ed4b495028 100644 --- a/DIVERGENCES.md +++ b/DIVERGENCES.md @@ -266,8 +266,9 @@ from the squash message first, or use a plain merge. | `sdks/vscode/package.json`, `sdks/vscode/src/extension.ts`, `sdks/vscode/README.md` | `name` `opencode` → `codev-code` (the extension ID is `publisher.name`); `displayName` "opencode" → "CoDev Code"; `publisher` `sst-dev` → `CoDev` (PLACEHOLDER — no such Marketplace publisher is registered; the mixed-case `CoDev` also isn't a valid Marketplace publisher id, which must be lowercase — fine for the sideloaded `.vsix` the fork ships, but replace with the real fork-owned lowercase publisher id before any `vsce publish`, and keep it in sync with the CLI auto-install target); `description` "opencode for VS Code" → "CoDev Code for VS Code"; `icon` `images/icon.png` is a fork-local generated PNG of the CoDev "c" badge (brand red `#ee0033` — the "dev" half of the wordmark, see `packages/tui/src/logo.ts` — on `#0a0a0a`), with `images/icon.svg` as its source of record — replacing the earlier symlink to the shared `packages/identity` bracket mark; the two per-theme command `icon` objects (`{light,dark}` → `images/button-{dark,light}.svg`) collapse to a single `"images/button.svg"` string and `extension.ts`'s terminal `iconPath` object collapses to a single `Uri`, both pointing at a fork-local transparent brand-red `#ee0033` "c" glyph that reads on light+dark themes (replacing the two `packages/identity` bracket-mark symlinks, now deleted); `repository.url` → `github.com/quickbeard/codev-code`; command IDs `opencode.*` → `codev.*` across `contributes.commands`/`menus`/`keybindings` and the `registerCommand` calls; command/keybinding titles "Open opencode…"/"Run opencode"/"opencode: Insert At-Mentioned" → "Open CoDev Code…"/"Run CoDev Code"/"CoDev Code: Insert At-Mentioned" (keys unchanged); `TERMINAL_NAME` `opencode` → `codev` and the terminal launches `codev --port `; scripts gain `"vsix"` (`vsce package --no-dependencies --skip-license -o dist/`) with `@vscode/vsce` added as a pinned devDependency; README body rebranded and trimmed to the details-panel essentials — CoDev Code intro linking the fork repo, `npm install -g codev-code` prerequisite, and feature prose only; upstream's Support and Development/Making-Changes sections are dropped (they're contributor-facing, not end-user extension description) | The fork ships this extension as an installable `.vsix`, so every user-visible surface — palette titles, command IDs, terminal name, launched binary, details-panel README — must say codev, and the launched binary must be the fork's `codev` CLI. Distinct `codev.*` IDs also avoid clashing with upstream's extension if both are installed. `--no-dependencies` because the bundle is self-contained (no runtime deps, bun-managed lockfile); `--skip-license` because `sdks/vscode` has no LICENSE file and vsce aborts without it. | Keep our name + displayName + publisher + repository + description, command IDs + titles, `TERMINAL_NAME`, the `codev --port` launch line, the `vsix` script + `@vscode/vsce` devDependency, and the trimmed README (branding + dropped Support/Development sections); take upstream's extension-logic and manifest-structure changes. | | `packages/opencode/src/ide/index.ts` | IDE auto-install target `sst-dev.opencode` → `CoDev.codev-code` (`publisher.name` from `sdks/vscode/package.json`) | The CLI must install the fork's extension ID (`publisher.name` from `sdks/vscode/package.json`), not upstream's Marketplace listing. `Ide.install` currently has no runtime caller (only its test imports the module), and the install only resolves once the extension is actually published — until then the local `.vsix` is the supported install path. | Keep our extension ID (and re-sync it if the registered publisher id changes); take upstream's install/detection logic changes. | | Env-var rename, repo-wide (~230 files): both flag registries (`packages/core/src/flag/flag.ts`, `packages/opencode/src/effect/runtime-flags.ts`), every `process.env`/`Bun.env` read+set site across `packages/*`, `sdks/vscode`, `infra/enterprise.ts`, `nix/opencode.nix`, `turbo.json`, `.github/workflows/test.yml`, test preloads/fixtures/snapshots, docs (en + es), `SECURITY.md`, specs | `OPENCODE_*` env vars → `CODEV_*` (hard cutover, no legacy fallback), including the bundler define identifiers (`CODEV_VERSION`, `CODEV_CHANNEL`, `CODEV_MODELS_DEV`, `CODEV_LIBC`, `CODEV_CLI_NAME`, `CODEV_WORKER_PATH`), `VITE_OPENCODE_*` → `VITE_CODEV_*`, `import.meta.env.OPENCODE_CHANNEL` → `CODEV_CHANNEL`, the VSCode extension's `OPENCODE_CALLER`/`_EXTENSION_OPENCODE_PORT`, and the bare `OPENCODE=1` marker → `CODEV=1` | The fork's env surface uses the fork's name (reverses the earlier "internal names stay upstream" decision for env vars). NOT renamed: `__OPENCODE_PHOTON_WASM_PATH` and `__OPENCODE__` globals (not env vars; the photon name is baked into `patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch`), `patches/install-korean-ime-fix.sh` script-local shell vars, upstream secret names in workflow-deletion notes, lowercase identifiers (`@opencode-ai/*`, URLs, provider id) | Keep our names; rename any new upstream `OPENCODE_*` env var (or define/`VITE_` name) to `CODEV_*` when syncing. | -| Locale dicts in `packages/{ui,app}/src/i18n/`, `packages/desktop/src/renderer/i18n/`, `packages/console/app/src/i18n/`, `packages/stats/app/src/i18n/`, `packages/web/src/content/i18n/`; every translated docs dir in `packages/web/src/content/docs/` | Deleted every locale except `en`/`es` (incl. `packages/app/src/i18n/parity.test.ts`) | Fork ships English + Spanish only. | Keep deleted (`git rm` the upstream side of modify/delete conflicts); also delete any locale files upstream adds. | -| `packages/app/src/context/language.tsx`, `packages/app/src/entry.tsx`, `packages/desktop/src/renderer/i18n/index.ts`, `packages/console/app/src/i18n/index.ts`, `packages/console/app/src/lib/language.ts`, `packages/console/app/src/component/footer.tsx`, `packages/stats/app/src/i18n.ts`, `packages/enterprise/src/app.tsx`, `packages/app/e2e/regression/session-timeline-locale-projection.spec.ts` | Locale registries trimmed to `en` + `es`: `Locale` types, LOCALES/LABEL/TAG/DOCS maps, dict loaders, accept-language matchers; enterprise/entry `zh` special cases now use `es`; console footer always links Discord (Feishu was zh-only); e2e locale-projection spec tests `es` ("Explorado") instead of de/ar | Fork ships English + Spanish only. | Keep our trimmed lists; take upstream's structural changes and re-trim any new locale entries. | +| Locale dicts in `packages/{ui,app}/src/i18n/`, `packages/desktop/src/renderer/i18n/`, `packages/console/app/src/i18n/`, `packages/stats/app/src/i18n/`, `packages/web/src/content/i18n/`; every translated docs dir in `packages/web/src/content/docs/` | Deleted every locale except `en`/`es` (incl. `packages/app/src/i18n/parity.test.ts`) | Fork ships English + Spanish, plus fork-authored Vietnamese (`vi.ts` dicts are new fork files — see the Vietnamese row below); every other upstream locale stays deleted. | Keep deleted (`git rm` the upstream side of modify/delete conflicts); also delete any locale files upstream adds — except a `vi` locale, which the fork owns (prefer our translations and reconcile key-by-key). | +| `packages/app/src/context/language.tsx`, `packages/app/src/entry.tsx`, `packages/desktop/src/renderer/i18n/index.ts`, `packages/console/app/src/i18n/index.ts`, `packages/console/app/src/lib/language.ts`, `packages/console/app/src/component/footer.tsx`, `packages/stats/app/src/i18n.ts`, `packages/enterprise/src/app.tsx`, `packages/app/e2e/regression/session-timeline-locale-projection.spec.ts` | Locale registries trimmed to `en` + `es`, then extended with fork-added `vi`: `Locale` types, LOCALES/LABEL/TAG/DOCS maps (console maps `vi` to the published `/docs/vi` segment), dict loaders, accept-language matchers; enterprise/entry `zh` special cases now use `es` (both also match `vi`); console footer always links Discord (Feishu was zh-only); e2e locale-projection spec tests `es` ("Explorado") instead of de/ar | Fork ships English + Spanish + fork-authored Vietnamese. | Keep our locale lists (including `vi`); take upstream's structural changes, re-trim any new upstream locale entries, and re-apply the `vi` entries alongside them. | +| `packages/app/src/i18n/{en,es}.ts` (`language.vi` label), `script/translate-app.ts`, `script/translate-app.test.ts` | Added Vietnamese (`vi`) as a fork-only third locale: autonym label `"language.vi": "Tiếng Việt"` inserted into the en/es app dicts' language list; `vi`/"Vietnamese" registered in the translate script's locale tables (desktop surface included — only `th`/`tr` are excluded); `glossaryFile` repointed `.opencode/glossary/` → `.codev/glossary/` (test expectations updated) so glossaries actually load from the fork's renamed config dir. The dictionaries themselves — `packages/{app,ui}/src/i18n/vi.ts`, `packages/desktop/src/renderer/i18n/vi.ts`, `packages/console/app/src/i18n/vi.ts`, `packages/stats/app/src/i18n/vi.ts` — and `.codev/glossary/vi.md` are fork-only new files (not tracked per policy). | Vietnamese docs are published on the fork's docs site (`…/codev/vi/docs`), and the product UI should match. Upstream has no `vi` locale, so the new files never collide with upstream content. | Keep our `vi` entries and the `.codev` glossary path; take upstream's surrounding script/dict changes. If upstream ever adds a `vi` locale, prefer our translations and reconcile key-by-key. | | `packages/tui/src/component/prompt/autocomplete.tsx`, `packages/opencode/src/cli/cmd/run/footer.prompt.tsx`, `packages/core/src/filesystem/search.ts`, `packages/tui/test/fixture/tui-sdk.ts`, `packages/opencode/test/cli/run/footer.view.test.tsx`, `packages/core/test/filesystem/search.test.ts` | `@` mention autocomplete lists file/directory results before references/agents/MCP resources; TUI and run-CLI menus cap the leading files so up to 3 non-file matches stay in the first visible page. TUI and run-CLI file resources track menu visibility so opening on an empty query fetches (previously nothing refetched until the query changed); core's ripgrep-fallback `find` returns its first entries for an empty query to mirror fff. Tests: tui-sdk fixture's `FetchHandler` gains an optional `method` arg, `renderFooter` accepts `findFiles`/`agents` overrides + a mention-menu ordering test, core search tests cover the fallback's empty query (fork-only test file: `packages/tui/test/prompt-autocomplete.test.tsx`). The legacy web prompt originally carried the same reorder (fork-only `prompt-input/at-options.ts`); it was deleted with the legacy prompt input (see the removal row below), and the V2 composer's files-first ordering is tracked in its own `session-ui` row (PR #289). | Files are the dominant `@` use case; they should rank first and appear on a bare `@`. | Keep our ordering/reserve logic, resource source tracking, empty-query branch, fixture/test additions; take upstream's surrounding changes. | | `packages/slack/.gitignore`, `sdks/vscode/.gitignore` | Added the missing trailing newline (content unchanged) | Upstream's copies end without a newline; POSIX text files should end with one, and tools that concatenate or append to these files misbehave without it. | Whitespace-only: keep the trailing newline; take upstream's entries. | | `packages/tui/src/config/keybind.ts`, `packages/tui/src/component/prompt/index.tsx`, `packages/tui/src/app.tsx` | Permission-mode indicator + shift+tab cycle. keybind.ts: added `permission_mode_cycle: keybind("shift+tab", ...)` + its `permission.mode` CommandMap entry; `agent_cycle_reverse` default `"shift+tab"` → `"none"` (tab still cycles agents forward; previous-agent stays rebindable). prompt/index.tsx: the footer badge next to the agent name always renders in prompt mode — gray `manual` (`theme.textMuted`) in normal mode, red `auto` (`theme.error`) in auto mode (upstream only showed gray `auto`); the footer's shortcut hints gain a `shift+tab modes` text (via a `modeShortcut` `useCommandShortcut("permission.mode")` memo) left of `tab agents`, in the same `Show` gate (see the hints-and-usage row above for when that gate renders). app.tsx: `"permission.mode"` added to `appBindingCommands` so the binding is gathered. | The permission mode is always visible (manual/auto) with auto in red as a danger signal, and shift+tab toggles it without opening the palette. | Keep the `permission_mode_cycle` definition + mapping, the `agent_cycle_reverse` `"none"` default, the badge `Show` block, the `modeShortcut` memo + hint text, and the `appBindingCommands` entry; take upstream's surrounding changes. If upstream reworks the auto badge or rebinds shift+tab, re-apply on top and re-audit the shift+tab conflict. | diff --git a/packages/app/e2e/regression/session-timeline-locale-projection.spec.ts b/packages/app/e2e/regression/session-timeline-locale-projection.spec.ts index 1bc0f18280..2ff5d9942b 100644 --- a/packages/app/e2e/regression/session-timeline-locale-projection.spec.ts +++ b/packages/app/e2e/regression/session-timeline-locale-projection.spec.ts @@ -1,7 +1,10 @@ import { expect, test } from "@playwright/test" import { assistantMessage, setupTimeline, toolPart, userMessage } from "../performance/timeline-stability/fixture" -for (const profile of [{ locale: "es", label: "Explorado" }] as const) { +for (const profile of [ + { locale: "es", label: "Explorado" }, + { locale: "vi", label: "Đã khám phá" }, +] as const) { test(`projects translated context status in ${profile.locale}`, async ({ page }) => { const ids = [`prt_locale_${profile.locale}_01_read`, `prt_locale_${profile.locale}_02_glob`] await setupTimeline(page, { diff --git a/packages/app/src/context/language.tsx b/packages/app/src/context/language.tsx index aa93c8968a..92d56a6b93 100644 --- a/packages/app/src/context/language.tsx +++ b/packages/app/src/context/language.tsx @@ -6,7 +6,7 @@ import { Persist, persisted } from "@/utils/persist" import { dict as en } from "@/i18n/en" import { dict as uiEn } from "@opencode-ai/ui/i18n/en" -export type Locale = "en" | "es" +export type Locale = "en" | "es" | "vi" type RawDictionary = typeof en & typeof uiEn type Dictionary = i18n.Flatten @@ -16,16 +16,18 @@ function cookie(locale: Locale) { return `oc_locale=${encodeURIComponent(locale)}; Path=/; Max-Age=31536000; SameSite=Lax` } -const LOCALES: readonly Locale[] = ["en", "es"] +const LOCALES: readonly Locale[] = ["en", "es", "vi"] const INTL: Record = { en: "en", es: "es", + vi: "vi", } const LABEL_KEY: Record = { en: "language.en", es: "language.es", + vi: "language.vi", } const base = i18n.flatten({ ...en, ...uiEn }) @@ -36,6 +38,7 @@ const merge = (app: Promise, ui: Promise) => const loaders: Record, () => Promise> = { es: () => merge(import("@/i18n/es"), import("@opencode-ai/ui/i18n/es")), + vi: () => merge(import("@/i18n/vi"), import("@opencode-ai/ui/i18n/vi")), } function loadDict(locale: Locale) { @@ -56,6 +59,7 @@ export function loadLocaleDict(locale: Locale) { const localeMatchers: Array<{ locale: Locale; match: (language: string) => boolean }> = [ { locale: "en", match: (language) => language.startsWith("en") }, { locale: "es", match: (language) => language.startsWith("es") }, + { locale: "vi", match: (language) => language.startsWith("vi") }, ] function detectLocale(): Locale { diff --git a/packages/app/src/entry.tsx b/packages/app/src/entry.tsx index 1feb18f30e..d3c62018ac 100644 --- a/packages/app/src/entry.tsx +++ b/packages/app/src/entry.tsx @@ -6,6 +6,7 @@ import { AppBaseProviders, AppInterface } from "@/app" import { type Platform, PlatformProvider } from "@/context/platform" import { dict as en } from "@/i18n/en" import { dict as es } from "@/i18n/es" +import { dict as vi } from "@/i18n/vi" import { handleNotificationClick } from "@/utils/notification-click" import { authFromToken } from "@/utils/server" import pkg from "../package.json" @@ -19,6 +20,7 @@ const getLocale = () => { for (const language of languages) { if (!language) continue if (language.toLowerCase().startsWith("es")) return "es" as const + if (language.toLowerCase().startsWith("vi")) return "vi" as const } return "en" as const } @@ -26,7 +28,9 @@ const getLocale = () => { const getRootNotFoundError = () => { const key = "error.dev.rootNotFound" as const const locale = getLocale() - return locale === "es" ? (es[key] ?? en[key]) : en[key] + if (locale === "es") return es[key] ?? en[key] + if (locale === "vi") return vi[key] ?? en[key] + return en[key] } const getStorage = (key: string) => { diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index d6709e2c79..a4bc7669a3 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -599,6 +599,7 @@ export const dict = { "language.ko": "한국어", "language.de": "Deutsch", "language.es": "Español", + "language.vi": "Tiếng Việt", "language.fr": "Français", "language.da": "Dansk", "language.ja": "日本語", diff --git a/packages/app/src/i18n/es.ts b/packages/app/src/i18n/es.ts index 8bfdc49e48..330bd005b8 100644 --- a/packages/app/src/i18n/es.ts +++ b/packages/app/src/i18n/es.ts @@ -590,6 +590,7 @@ export const dict = { "language.ko": "한국어", "language.de": "Deutsch", "language.es": "Español", + "language.vi": "Tiếng Việt", "language.fr": "Français", "language.da": "Dansk", "language.ja": "日本語", diff --git a/packages/app/src/i18n/vi.ts b/packages/app/src/i18n/vi.ts new file mode 100644 index 0000000000..e15ac09379 --- /dev/null +++ b/packages/app/src/i18n/vi.ts @@ -0,0 +1,1248 @@ +export const dict = { + "command.category.suggested": "Đề xuất", + "command.category.view": "Xem", + "command.category.project": "Dự án", + "command.category.provider": "Provider", + "command.category.server": "Máy chủ", + "command.category.session": "Session", + "command.category.theme": "Chủ đề", + "command.category.language": "Ngôn ngữ", + "command.category.file": "Tệp", + "command.category.context": "Context", + "command.category.terminal": "Terminal", + "command.category.model": "Model", + "command.category.mcp": "MCP", + "command.category.agent": "Agent", + "command.category.permissions": "Quyền", + "command.category.workspace": "Workspace", + "command.category.settings": "Cài đặt", + + "theme.scheme.system": "Hệ thống", + "theme.scheme.light": "Sáng", + "theme.scheme.dark": "Tối", + + "command.sidebar.toggle": "Bật/tắt thanh bên", + "command.project.open": "Mở dự án", + "command.project.previous": "Dự án trước", + "command.project.next": "Dự án tiếp theo", + "command.project.index": "Chuyển đến dự án {{index}}", + "command.provider.connect": "Kết nối provider", + "command.server.switch": "Chuyển máy chủ", + "command.gateway.apiKey.new": "Lấy API key mới", + "command.settings.open": "Mở cài đặt", + "command.session.previous": "Session trước", + "command.session.next": "Session tiếp theo", + "command.session.previous.unseen": "Session chưa đọc trước đó", + "command.session.next.unseen": "Session chưa đọc tiếp theo", + "command.session.archive": "Lưu trữ session", + + "command.palette": "Bảng lệnh", + + "command.theme.cycle": "Đổi chủ đề", + "command.theme.set": "Dùng chủ đề: {{theme}}", + "command.theme.scheme.cycle": "Đổi chế độ màu", + "command.theme.scheme.set": "Dùng chế độ màu: {{scheme}}", + + "command.language.cycle": "Đổi ngôn ngữ", + "command.language.set": "Dùng ngôn ngữ: {{language}}", + + "command.session.new": "Session mới", + "command.file.open": "Mở tệp", + "command.tab.close": "Đóng tab", + "command.tab.reopenClosed": "Mở lại tab đã đóng", + "command.context.addSelection": "Thêm vùng chọn vào context", + "command.context.addSelection.description": "Thêm các dòng đã chọn từ tệp hiện tại", + "command.input.focus": "Đưa con trỏ vào ô nhập", + "command.terminal.toggle": "Bật/tắt terminal", + "command.fileTree.toggle": "Bật/tắt cây tệp", + "command.review.toggle": "Bật/tắt review", + "command.terminal.new": "Terminal mới", + "command.terminal.new.description": "Tạo tab terminal mới", + "command.steps.toggle": "Bật/tắt các bước", + "command.steps.toggle.description": "Hiện hoặc ẩn các bước của tin nhắn hiện tại", + "command.message.previous": "Tin nhắn trước", + "command.message.previous.description": "Chuyển đến tin nhắn người dùng trước đó", + "command.message.next": "Tin nhắn tiếp theo", + "command.message.next.description": "Chuyển đến tin nhắn người dùng tiếp theo", + "command.model.choose": "Chọn model", + "command.model.choose.description": "Chọn một model khác", + "command.mcp.toggle": "Bật/tắt MCP", + "command.mcp.toggle.description": "Bật/tắt MCP", + "command.agent.cycle": "Đổi agent", + "command.agent.cycle.description": "Chuyển sang agent tiếp theo", + "command.agent.cycle.reverse": "Đổi agent ngược lại", + "command.agent.cycle.reverse.description": "Chuyển về agent trước đó", + "command.model.variant.cycle": "Đổi mức độ suy nghĩ", + "command.model.variant.cycle.description": "Chuyển sang mức tiếp theo", + "command.prompt.mode.shell": "Shell", + "command.prompt.mode.normal": "Prompt", + "command.permissions.autoaccept.enable": "Tự động chấp nhận quyền", + "command.permissions.autoaccept.disable": "Ngừng tự động chấp nhận quyền", + "command.workspace.toggle": "Bật/tắt workspace", + "command.workspace.toggle.description": "Bật hoặc tắt nhiều workspace trong thanh bên", + "command.session.undo": "Hoàn tác", + "command.session.undo.description": "Hoàn tác tin nhắn cuối cùng", + "command.session.redo": "Làm lại", + "command.session.redo.description": "Làm lại tin nhắn vừa hoàn tác", + "command.session.compact": "Nén session", + "command.session.compact.description": "Tóm tắt session để giảm kích thước context", + "command.session.fork": "Fork từ tin nhắn", + "command.session.fork.description": "Tạo session mới từ một tin nhắn trước đó", + "command.session.share": "Chia sẻ session", + "command.session.share.description": "Chia sẻ session này và sao chép URL vào clipboard", + "command.session.unshare": "Ngừng chia sẻ session", + "command.session.unshare.description": "Ngừng chia sẻ session này", + + "palette.search.placeholder": "Tìm tệp, lệnh và session", + "palette.search.placeholder.home": "Tìm lệnh và session", + "palette.empty": "Không tìm thấy kết quả", + "palette.group.commands": "Lệnh", + "palette.group.files": "Tệp", + + "dialog.provider.search.placeholder": "Tìm provider", + "dialog.provider.empty": "Không tìm thấy provider", + "dialog.provider.group.popular": "Phổ biến", + "dialog.provider.group.other": "Khác", + "dialog.provider.custom.label": "Provider tùy chỉnh tương thích OpenAI", + "dialog.provider.tag.recommended": "Khuyên dùng", + "dialog.provider.opencode.note": "Các model được tuyển chọn gồm Claude, GPT, Gemini và nhiều hơn nữa", + "dialog.provider.opencode.tagline": "Model đáng tin cậy, đã được tối ưu", + "dialog.provider.opencodeGo.tagline": "Gói thuê bao chi phí thấp cho mọi người", + "dialog.provider.anthropic.note": "Truy cập trực tiếp các model Claude, bao gồm Pro và Max", + "dialog.provider.copilot.note": "Model AI hỗ trợ lập trình qua GitHub Copilot", + "dialog.provider.openai.note": "Model GPT cho các tác vụ AI tổng quát nhanh và mạnh", + "dialog.provider.google.note": "Model Gemini cho phản hồi nhanh, có cấu trúc", + "dialog.provider.openrouter.note": "Truy cập mọi model được hỗ trợ từ một provider duy nhất", + "dialog.provider.vercel.note": "Truy cập hợp nhất các model AI với định tuyến thông minh", + + "dialog.model.select.title": "Chọn model", + "dialog.model.search.placeholder": "Tìm model", + "dialog.model.empty": "Không có kết quả model", + "dialog.model.manage": "Quản lý model", + "dialog.model.manage.description": "Tùy chỉnh những model hiển thị trong bộ chọn model.", + "dialog.model.manage.provider.toggle": "Bật/tắt tất cả model {{provider}}", + + "dialog.model.unpaid.freeModels.title": "Đã bao gồm model miễn phí", + "dialog.model.unpaid.addMore.title": "Thêm model từ các provider phổ biến", + "dialog.model.unpaid.viewMoreProviders": "Xem thêm hơn 70 provider khác", + + "dialog.provider.viewAll": "Hiện thêm provider", + + "provider.connect.title": "Kết nối {{provider}}", + "provider.connect.title.anthropicProMax": "Đăng nhập với Claude Pro/Max", + "provider.connect.selectMethod": "Chọn cách đăng nhập cho {{provider}}.", + "provider.connect.method.apiKey": "API key", + "provider.connect.status.inProgress": "Đang ủy quyền...", + "provider.connect.status.waiting": "Đang chờ ủy quyền...", + "provider.connect.status.failed": "Ủy quyền thất bại: {{error}}", + "provider.connect.apiKey.description": + "Nhập API key {{provider}} của bạn để kết nối tài khoản và dùng các model {{provider}} trong CoDev.", + "provider.connect.apiKey.label": "API key {{provider}}", + "provider.connect.apiKey.placeholder": "API key", + "provider.connect.apiKey.required": "Cần nhập API key", + "provider.connect.opencodeZen.line1": + "OpenCode Zen cho bạn quyền truy cập một bộ model được tuyển chọn, đáng tin cậy và đã được tối ưu cho các agent lập trình.", + "provider.connect.opencodeZen.line2": + "Với một API key duy nhất, bạn sẽ dùng được các model như Claude, GPT, Gemini, GLM và nhiều hơn nữa.", + "provider.connect.opencodeZen.visit.prefix": "Truy cập ", + "provider.connect.opencodeZen.visit.link": "opencode.ai/zen", + "provider.connect.opencodeZen.visit.suffix": " để lấy API key của bạn.", + "provider.connect.oauth.code.visit.prefix": "Truy cập ", + "provider.connect.oauth.code.visit.link": "liên kết này", + "provider.connect.oauth.code.visit.suffix": + " để lấy mã ủy quyền, kết nối tài khoản và dùng các model {{provider}} trong CoDev.", + "provider.connect.oauth.code.label": "Mã ủy quyền {{method}}", + "provider.connect.oauth.code.placeholder": "Mã ủy quyền", + "provider.connect.oauth.code.required": "Cần nhập mã ủy quyền", + "provider.connect.oauth.code.invalid": "Mã ủy quyền không hợp lệ", + "provider.connect.oauth.auto.visit.prefix": "Truy cập ", + "provider.connect.oauth.auto.visit.link": "liên kết này", + "provider.connect.oauth.auto.visit.suffix": + " rồi nhập mã bên dưới để kết nối tài khoản và dùng các model {{provider}} trong CoDev.", + "provider.connect.oauth.auto.confirmationCode": "Mã xác nhận", + "provider.connect.toast.connected.title": "Đã kết nối {{provider}}", + "provider.connect.toast.connected.description": "Các model {{provider}} đã sẵn sàng để dùng.", + + "provider.custom.title": "Provider tùy chỉnh", + "provider.custom.description.prefix": "Cấu hình một provider tương thích OpenAI. Xem ", + "provider.custom.description.link": "tài liệu cấu hình provider", + "provider.custom.description.suffix": ".", + "provider.custom.field.providerID.label": "Provider ID", + "provider.custom.field.providerID.placeholder": "myprovider", + "provider.custom.field.providerID.description": "Chữ thường, số, gạch ngang hoặc gạch dưới", + "provider.custom.field.name.label": "Tên hiển thị", + "provider.custom.field.name.placeholder": "Provider AI của tôi", + "provider.custom.field.baseURL.label": "Base URL", + "provider.custom.field.baseURL.placeholder": "https://api.myprovider.com/v1", + "provider.custom.field.apiKey.label": "API key", + "provider.custom.field.apiKey.placeholder": "API key", + "provider.custom.field.apiKey.description": "Không bắt buộc. Để trống nếu bạn quản lý xác thực qua header.", + "provider.custom.models.label": "Model", + "provider.custom.models.id.label": "ID", + "provider.custom.models.id.placeholder": "model-id", + "provider.custom.models.name.label": "Tên", + "provider.custom.models.name.placeholder": "Tên hiển thị", + "provider.custom.models.remove": "Xóa model", + "provider.custom.models.add": "Thêm model", + "provider.custom.headers.label": "Header (không bắt buộc)", + "provider.custom.headers.key.label": "Header", + "provider.custom.headers.key.placeholder": "Header-Name", + "provider.custom.headers.value.label": "Giá trị", + "provider.custom.headers.value.placeholder": "giá trị", + "provider.custom.headers.remove": "Xóa header", + "provider.custom.headers.add": "Thêm header", + "provider.custom.error.providerID.required": "Cần nhập Provider ID", + "provider.custom.error.providerID.format": "Dùng chữ thường, số, gạch ngang hoặc gạch dưới", + "provider.custom.error.providerID.exists": "Provider ID này đã tồn tại", + "provider.custom.error.name.required": "Cần nhập tên hiển thị", + "provider.custom.error.baseURL.required": "Cần nhập Base URL", + "provider.custom.error.baseURL.format": "Phải bắt đầu bằng http:// hoặc https://", + "provider.custom.error.required": "Bắt buộc", + "provider.custom.error.duplicate": "Trùng lặp", + + "provider.disconnect.toast.disconnected.title": "Đã ngắt kết nối {{provider}}", + "provider.disconnect.toast.disconnected.description": "Các model {{provider}} không còn khả dụng.", + + "model.tag.free": "Miễn phí", + "model.tag.latest": "Mới nhất", + "model.provider.anthropic": "Anthropic", + "model.provider.openai": "OpenAI", + "model.provider.google": "Google", + "model.provider.xai": "xAI", + "model.provider.meta": "Meta", + "model.input.text": "văn bản", + "model.input.image": "hình ảnh", + "model.input.audio": "âm thanh", + "model.input.video": "video", + "model.input.pdf": "pdf", + "model.tooltip.allows": "Hỗ trợ: {{inputs}}", + "model.tooltip.context.label": "Context", + "model.tooltip.inputs": "Đầu vào", + "model.tooltip.model": "Model", + "model.tooltip.provider": "Provider", + "model.tooltip.reasoning": "Suy luận", + "model.tooltip.reasoning.allowed": "Hỗ trợ suy luận", + "model.tooltip.reasoning.none": "Không suy luận", + "model.tooltip.context": "Giới hạn context {{limit}}", + + "common.search.placeholder": "Tìm kiếm", + "common.goBack": "Quay lại", + "common.goForward": "Tiến tới", + "common.loading": "Đang tải", + "common.loading.ellipsis": "...", + "common.cancel": "Hủy", + "common.clear": "Xóa", + "common.open": "Mở", + "common.connect": "Kết nối", + "common.disconnect": "Ngắt kết nối", + "common.continue": "Tiếp tục", + "common.submit": "Gửi", + "common.save": "Lưu", + "common.saving": "Đang lưu...", + "common.default": "Mặc định", + "common.attachment": "tệp đính kèm", + + "prompt.placeholder.shell": "Nhập lệnh shell... {{example}}", + "prompt.placeholder.normal": 'Hỏi bất cứ điều gì... "{{example}}"', + "prompt.placeholder.simple": "Hỏi bất cứ điều gì...", + "prompt.placeholder.summarizeComments": "Tóm tắt các bình luận…", + "prompt.placeholder.summarizeComment": "Tóm tắt bình luận…", + "prompt.mode.shell": "Shell", + "prompt.mode.normal": "Prompt", + "prompt.mode.shell.exit": "esc để thoát", + "session.child.promptDisabled": "Không thể gửi prompt cho session của subagent.", + "session.child.backToParent": "Quay lại session chính.", + + "prompt.example.1": "Sửa một TODO trong codebase", + "prompt.example.2": "Tech stack của dự án này là gì?", + "prompt.example.3": "Sửa các test bị hỏng", + "prompt.example.4": "Giải thích cách hoạt động của phần xác thực", + "prompt.example.5": "Tìm và sửa các lỗ hổng bảo mật", + "prompt.example.6": "Thêm unit test cho user service", + "prompt.example.7": "Refactor hàm này cho dễ đọc hơn", + "prompt.example.8": "Lỗi này nghĩa là gì?", + "prompt.example.9": "Giúp tôi debug vấn đề này", + "prompt.example.10": "Tạo tài liệu API", + "prompt.example.11": "Tối ưu các truy vấn cơ sở dữ liệu", + "prompt.example.12": "Thêm kiểm tra dữ liệu đầu vào", + "prompt.example.13": "Tạo một component mới cho...", + "prompt.example.14": "Làm sao để deploy dự án này?", + "prompt.example.15": "Review code của tôi theo best practice", + "prompt.example.16": "Thêm xử lý lỗi cho hàm này", + "prompt.example.17": "Giải thích mẫu regex này", + "prompt.example.18": "Chuyển đoạn này sang TypeScript", + "prompt.example.19": "Thêm logging trong toàn bộ codebase", + "prompt.example.20": "Những dependency nào đã lỗi thời?", + "prompt.example.21": "Giúp tôi viết script migration", + "prompt.example.22": "Triển khai cache cho endpoint này", + "prompt.example.23": "Thêm phân trang cho danh sách này", + "prompt.example.24": "Tạo một lệnh CLI cho...", + "prompt.example.25": "Biến môi trường hoạt động thế nào ở đây?", + + "prompt.popover.emptyResults": "Không có kết quả phù hợp", + "prompt.popover.emptyCommands": "Không có lệnh phù hợp", + "prompt.dropzone.label": "Thả hình ảnh, PDF hoặc tệp văn bản vào đây", + "prompt.dropzone.file.label": "Thả để @mention tệp", + "prompt.slash.badge.custom": "tùy chỉnh", + "prompt.slash.badge.skill": "skill", + "prompt.slash.badge.mcp": "mcp", + "prompt.context.active": "đang mở", + "prompt.context.includeActiveFile": "Kèm tệp đang mở", + "prompt.context.removeActiveFile": "Bỏ tệp đang mở khỏi context", + "prompt.context.removeFile": "Bỏ tệp khỏi context", + "prompt.action.attachFile": "Thêm tệp", + "prompt.menu.addImagesAndFiles": "Thêm tệp và nội dung khác", + "prompt.menu.imagesAndFiles": "Hình ảnh và tệp", + "prompt.menu.commands": "Lệnh", + "prompt.menu.context": "Context", + "prompt.menu.shellCommand": "Lệnh shell", + "prompt.attachment.remove": "Gỡ tệp đính kèm", + "prompt.action.send": "Gửi", + "prompt.action.stop": "Dừng", + + "prompt.toast.pasteUnsupported.title": "Tệp đính kèm không được hỗ trợ", + "prompt.toast.pasteUnsupported.description": "Chỉ có thể đính kèm hình ảnh, PDF hoặc tệp văn bản ở đây.", + "prompt.toast.modelAgentRequired.title": "Chọn agent và model", + "prompt.toast.modelAgentRequired.description": "Chọn một agent và model trước khi gửi prompt.", + "prompt.toast.worktreeCreateFailed.title": "Tạo worktree thất bại", + "prompt.toast.sessionCreateFailed.title": "Tạo session thất bại", + "prompt.toast.shellSendFailed.title": "Gửi lệnh shell thất bại", + "prompt.toast.commandSendFailed.title": "Gửi lệnh thất bại", + "prompt.toast.promptSendFailed.title": "Gửi prompt thất bại", + "prompt.toast.promptSendFailed.description": "Không thể truy xuất session", + + "dialog.mcp.title": "MCP", + "dialog.mcp.description": "Đã bật {{enabled}} trong {{total}}", + "dialog.mcp.empty": "Chưa cấu hình MCP nào", + + "dialog.lsp.empty": "LSP được tự động phát hiện theo loại tệp", + "dialog.plugins.empty": "Plugin được cấu hình trong codev.json", + + "mcp.status.connected": "đã kết nối", + "mcp.status.failed": "thất bại", + "mcp.status.needs_auth": "cần xác thực", + "mcp.status.disabled": "đã tắt", + "mcp.auth.clickToAuthenticate": "Nhấn để xác thực", + + "dialog.fork.empty": "Không có tin nhắn nào để fork", + + "dialog.directory.search.placeholder": "Tìm thư mục", + "dialog.directory.empty": "Không tìm thấy thư mục", + "dialog.directory.action.selectFile": "Chọn tệp", + "dialog.directory.action.selectFolder": "Chọn thư mục", + "dialog.directory.root": "Gốc", + "dialog.directory.parent": "Thư mục cha", + "dialog.directory.readError": "Không thể đọc thư mục này", + + "app.server.unreachable": "Không thể kết nối tới {{server}}", + "app.server.retrying": "Đang tự động thử lại...", + "app.server.otherServers": "Máy chủ khác", + + "dialog.server.title": "Máy chủ", + "dialog.server.description": "Chuyển máy chủ CoDev mà ứng dụng này kết nối tới.", + "dialog.server.search.placeholder": "Tìm máy chủ", + "dialog.server.empty": "Chưa có máy chủ nào", + "dialog.server.add.title": "Thêm máy chủ", + "dialog.server.add.url": "Địa chỉ máy chủ", + "dialog.server.add.placeholder": "http://localhost:4096", + "dialog.server.add.error": "Không thể kết nối tới máy chủ", + "dialog.server.add.checking": "Đang kiểm tra...", + "dialog.server.add.button": "Thêm máy chủ", + "dialog.server.add.name": "Tên máy chủ (không bắt buộc)", + "dialog.server.add.namePlaceholder": "Localhost", + "dialog.server.add.username": "Tên đăng nhập (không bắt buộc)", + "dialog.server.add.usernamePlaceholder": "tên đăng nhập", + "dialog.server.add.password": "Mật khẩu (không bắt buộc)", + "dialog.server.add.passwordPlaceholder": "mật khẩu", + "dialog.server.edit.title": "Sửa máy chủ", + "dialog.server.default.title": "Máy chủ mặc định", + "dialog.server.default.description": + "Kết nối tới máy chủ này khi mở ứng dụng thay vì khởi động máy chủ cục bộ. Cần khởi động lại.", + "dialog.server.default.none": "Chưa chọn máy chủ nào", + "dialog.server.default.set": "Đặt máy chủ hiện tại làm mặc định", + "dialog.server.default.clear": "Xóa", + "dialog.server.action.remove": "Gỡ máy chủ", + + "dialog.server.menu.edit": "Sửa", + "dialog.server.menu.default": "Đặt làm mặc định", + "dialog.server.menu.defaultRemove": "Bỏ mặc định", + "dialog.server.menu.delete": "Xóa", + "dialog.server.current": "Máy chủ hiện tại", + "dialog.server.status.default": "Mặc định", + "wsl.server.add": "Thêm máy chủ WSL", + "wsl.server.addShort": "Thêm WSL", + "wsl.server.label": "WSL", + "wsl.server.menu.label": "Máy chủ WSL", + "wsl.server.retryStart": "Thử khởi động lại", + "wsl.delete.title": "Gỡ máy chủ WSL", + "wsl.delete.confirm": "Gỡ {{name}}?", + "wsl.delete.distro": + "Bản phân phối không bị xóa: các tệp và session của nó vẫn ở trên máy này, và bạn có thể thêm lại bất cứ lúc nào.", + "wsl.delete.projects": "Các dự án và tab đã ghi nhớ của nó sẽ bị gỡ; khi thêm lại, danh sách dự án sẽ bắt đầu trống.", + "wsl.delete.button": "Gỡ máy chủ", + "wsl.server.updating": "Đang cập nhật...", + "workspace.code": "Code", + "workspace.work": "Work", + "workspace.loading.sandbox": "Đang tải sandbox…", + "workspace.empty.none": "Chưa có sandbox nào. Hãy tạo một sandbox để làm việc trong môi trường cách ly.", + "workspace.empty.starting": "Sandbox của bạn chưa chạy. Nó sẽ hiện ở đây khi sẵn sàng.", + "workspace.empty.stopped": "Sandbox của bạn đã dừng. Khởi động lại từ Cài đặt → Máy chủ.", + "workspace.empty.failed": "Sandbox của bạn khởi động thất bại.", + "workspace.empty.failed.retry": "Thử lại", + "sandbox.add.workIntro": "Work cần một môi trường sandbox để chạy session.", + "sandbox.addDefault.title": "Thiết lập sandbox của bạn", + "sandbox.addDefault.intro": + "Work chạy các session của bạn trong một sandbox: môi trường cách ly trên máy này, với công cụ và tệp riêng. Agent chỉ truy cập được những thư mục bạn mount vào đó, nên nó có thể chạy lệnh mà không đụng tới phần còn lại của máy. Việc thiết lập sẽ tải image của sandbox, mất vài phút; bạn chọn thư mục khi nó sẵn sàng.", + "sandbox.addDefault.create": "Thiết lập", + "sandbox.wsl.installAction": "Cài đặt WSL", + "sandbox.wsl.installRetry": "Thử lại", + "sandbox.wsl.installHint": "CoDev có thể cài WSL cho bạn. Windows sẽ hỏi xin quyền, và dung lượng tải khoảng 250 MB.", + "sandbox.wsl.downloading": "Đang tải WSL... {{done}} MB / {{total}} MB", + "sandbox.wsl.installing": "Đang cài WSL. Hãy chấp nhận hộp thoại của Windows để tiếp tục.", + "sandbox.wsl.restartRequired": "Đã cài WSL. Khởi động lại Windows để hoàn tất, rồi thiết lập sandbox của bạn.", + "sandbox.wsl.restartAction": "Khởi động lại Windows", + "sandbox.wsl.restartConfirm": "Khởi động lại ngay", + "sandbox.wsl.restartConfirmHint": + "Windows sẽ đóng CoDev và mọi ứng dụng đang mở khác. Hãy lưu công việc của bạn trước.", + "sandbox.wsl.failed": "Không thể cài WSL: {{message}}", + "sandbox.mountPrompt.body": "Sandbox của bạn đã sẵn sàng. Mount một dự án để bắt đầu làm việc trong đó.", + "sandbox.mountPrompt.action": "Mount dự án", + "sandbox.server.add": "Thêm sandbox", + "sandbox.server.label": "Sandbox", + "sandbox.server.menu.label": "Sandbox", + "sandbox.server.start": "Khởi động", + "sandbox.server.stop": "Dừng", + "sandbox.server.reset": "Đặt lại", + "sandbox.server.mounts": "Mount dự án", + "sandbox.delete.title": "Xóa sandbox", + "sandbox.delete.confirm": "Xóa {{name}}? Hành động này không thể hoàn tác.", + "sandbox.delete.disk": + "Ổ đĩa của sandbox sẽ bị xóa sạch. Mọi session, terminal và tệp được tạo bên trong sandbox sẽ mất vĩnh viễn.", + "sandbox.delete.mounts": "Các thư mục mount từ máy này không bị xóa — sandbox chỉ mất quyền truy cập vào chúng.", + "sandbox.delete.button": "Xóa sandbox", + "sandbox.status.starting": "Đang khởi động...", + "sandbox.status.ready": "Sẵn sàng", + "sandbox.status.stopped": "Đã dừng", + "sandbox.status.failed": "Thất bại", + "sandbox.job.create": "Đang tạo...", + "sandbox.job.destroy": "Đang gỡ...", + "sandbox.job.reset": "Đang đặt lại...", + "sandbox.job.queued": "Đang chờ một sandbox khác...", + "sandbox.add.namePlaceholder": "Tên sandbox", + "sandbox.add.nameInvalid": "Bắt đầu bằng chữ hoặc số, sau đó dùng chữ, số, dấu chấm, gạch ngang hoặc gạch dưới.", + "sandbox.add.nameTooLong": "Giữ tên trong {{max}} ký tự trở xuống.", + "sandbox.add.nameDuplicate": "Bạn đã có một sandbox với tên này.", + "sandbox.arch.x64": "Intel/AMD (x86_64)", + "sandbox.arch.arm64": "ARM64 (Apple silicon)", + "sandbox.add.image": "Image của sandbox", + "sandbox.add.imagePlaceholder": "Tệp image sandbox (không bắt buộc)", + "sandbox.add.imageHint": "Để trống và image phù hợp sẽ được tải về cho bạn.", + "sandbox.add.imageClear": "Xóa", + "sandbox.add.imageChecking": "Đang kiểm tra image…", + "sandbox.add.imageOk": "Image này dùng được trên máy này.", + "sandbox.add.imageUnreadable": "Không đọc được tệp đó. Hãy kiểm tra tệp còn tồn tại và bạn có quyền mở nó.", + "sandbox.add.imageNotArchive": "Đó không phải image sandbox. Image sandbox là tệp .tar.gz.", + "sandbox.add.imageNotSandbox": "Tệp .tar.gz đó không phải image sandbox — nó không chứa môi trường sandbox.", + "sandbox.add.imageWrongOsMac": "Image đó dành cho sandbox macOS. Máy này cần image Windows/Linux.", + "sandbox.add.imageWrongOsOther": "Image đó dành cho sandbox Windows/Linux. Máy này cần image macOS.", + "sandbox.add.imageWrongArch": "Image đó được build cho máy {{found}}, không phải máy này.", + "sandbox.add.projectDir": "Thư mục dự án", + "sandbox.add.projectDirHint": + "Chọn thư mục bạn muốn làm việc. Sandbox chỉ truy cập được các thư mục bạn mount từ máy này, và bạn có thể mount thêm sau.", + "sandbox.add.choose": "Chọn...", + "sandbox.add.create": "Tạo", + "sandbox.add.cpus": "CPU (mặc định {{cpus}})", + "sandbox.add.memory": "Bộ nhớ (mặc định {{memory}} GB)", + "sandbox.add.resourcesHint": + "Mức tài nguyên của máy Mac này mà sandbox được phép dùng. Mỗi sandbox là một máy ảo, nên bộ nhớ của nó bị giữ trong suốt thời gian chạy.", + "sandbox.add.resourcesInvalid": + "CPU phải trong khoảng {{minCpus}}–{{maxCpus}} và bộ nhớ {{minMemory}}–{{maxMemory}} GB.", + "sandbox.add.downloadProgress": "Đang tải image… {{done}}/{{total}} MB", + "sandbox.add.creating": "Đang tạo sandbox… quá trình này mất vài phút.", + "sandbox.mounts.title": "Mount dự án", + "sandbox.mounts.hint": "Các dự án mount ở đây là những thư mục duy nhất trên máy này mà sandbox truy cập được.", + "sandbox.mounts.primary": "Thư mục dự án", + "sandbox.mounts.empty": "Chưa có dự án nào được mount.", + "sandbox.mounts.add": "Thêm dự án...", + "sandbox.mounts.remove": "Gỡ dự án", + "sandbox.mounts.unmount": "Unmount", + "sandbox.mounts.save": "Lưu", + "sandbox.mounts.restartWarning": + "Lưu sẽ khởi động lại sandbox. Tệp trên đĩa được giữ nguyên; các terminal đang chạy và công việc dang dở trong sandbox sẽ dừng.", + "sandbox.mounts.imageHint": + "Nếu một thư mục không xuất hiện trong sandbox sau khi lưu, hãy đặt lại sandbox để cập nhật nó lên image hiện tại.", + "sandbox.mounts.invalid.duplicate": "Thư mục này đã được mount: {{path}}", + "sandbox.mounts.invalid.nested": "{{path}} chồng lấn với một thư mục đã được mount ({{other}}).", + "sandbox.mounts.invalid.unc": "Không thể mount đường dẫn mạng: {{path}}", + "wsl.onboarding.step.distro": "Chọn distro", + "wsl.onboarding.step.opencode": "CoDev", + "wsl.onboarding.checkingRuntime": "Đang kiểm tra WSL...", + "wsl.onboarding.restartRequired": "Windows cần khởi động lại để hoàn tất cài đặt WSL.", + "wsl.onboarding.ready": "WSL đã sẵn sàng.", + "wsl.onboarding.required": "Cần có WSL để tiếp tục.", + "wsl.onboarding.checkingDistros": "Đang kiểm tra các distro...", + "wsl.onboarding.installingDistro": "Đang cài {{distro}}...", + "wsl.onboarding.checkingDistro": "Đang kiểm tra {{distro}}...", + "wsl.onboarding.listingDistros": "Đang liệt kê các distro...", + "wsl.onboarding.distroReady": "{{distro}} đã sẵn sàng.", + "wsl.onboarding.distroNotInstalled": "{{distro}} chưa được cài.", + "wsl.onboarding.openDistroOnce": "Mở {{distro}} một lần để hoàn tất thiết lập.", + "wsl.onboarding.finishingDistro": "Đang hoàn tất thiết lập cho {{distro}}.", + "wsl.onboarding.pickDistro": "Chọn một distro hoặc cài mới bên dưới.", + "wsl.onboarding.checkingOpencode": "Đang kiểm tra CoDev...", + "wsl.onboarding.checkingOpencodeIn": "Đang kiểm tra CoDev trong {{distro}}...", + "wsl.onboarding.updatingOpencode": "Đang cập nhật CoDev...", + "wsl.onboarding.updatingOpencodeIn": "Đang cập nhật CoDev trong {{distro}}...", + "wsl.onboarding.updateOpencodeIn": "Cập nhật CoDev trong {{distro}}.", + "wsl.onboarding.updateOpencode": "Cập nhật CoDev", + "wsl.onboarding.opencodeReadyIn": "CoDev đã sẵn sàng trong {{distro}}.", + "wsl.onboarding.opencodeReady": "CoDev đã sẵn sàng.", + "wsl.onboarding.installCodevIn": "Cài CoDev trong {{distro}}.", + "wsl.onboarding.installCodev": "Cài CoDev", + "wsl.onboarding.chooseDistroFirst": "Hãy chọn một distro trước.", + "wsl.onboarding.loadFailed": "Tải trạng thái WSL thất bại.", + "wsl.onboarding.loading": "Đang tải...", + "wsl.onboarding.installedDistros": "Distro đã cài", + "wsl.onboarding.checkAgain": "Kiểm tra lại", + "wsl.onboarding.distroStatus.ready": "Sẵn sàng", + "wsl.onboarding.distroStatus.checking": "Đang kiểm tra...", + "wsl.onboarding.distroStatus.opencodeMissing": "Chưa cài CoDev", + "wsl.onboarding.distroStatus.missingTools": "Thiếu bash, curl", + "wsl.onboarding.distroStatus.unsupported": "Không hỗ trợ · Hãy dùng WSL 2", + "wsl.onboarding.needAnotherDistro": "Cần distro khác?", + "wsl.onboarding.needAnotherDistroHint": "Cài một bản phân phối Linux từ danh mục WSL", + "wsl.onboarding.wslNotInstalled.title": "Chưa cài WSL", + "wsl.onboarding.wslNotInstalled.description": + "Cần có WSL (Windows Subsystem for Linux) trước khi CoDev có thể thêm máy chủ WSL", + "wsl.onboarding.wslUnavailable.title": "WSL không khả dụng", + "wsl.onboarding.wslUnavailable.description": "CoDev không thể xác minh WSL trên máy này.", + "wsl.onboarding.installWsl": "Cài đặt WSL", + "wsl.onboarding.windowsRestartRequired": "Khởi động lại Windows để hoàn tất cài đặt WSL, rồi mở lại CoDev.", + "wsl.onboarding.next": "Tiếp theo", + "wsl.onboarding.refresh": "Làm mới", + "wsl.onboarding.allDistrosAdded": "Tất cả distro đã cài đều đã được thêm.", + "wsl.onboarding.noDistros": "Chưa phát hiện distro nào.", + "wsl.onboarding.install": "Cài đặt", + "wsl.onboarding.installing": "Đang cài...", + "wsl.onboarding.installDistro": "Cài distro", + "wsl.onboarding.searchDistros": "Tìm distro", + "wsl.onboarding.wsl2Required": "Cần WSL 2.", + "wsl.onboarding.toolsRequired": "Distro này cần bash và curl.", + "wsl.onboarding.openTerminal": "Mở terminal", + "wsl.onboarding.path": "Đường dẫn: {{path}}", + "wsl.onboarding.notFound": "không tìm thấy", + "wsl.onboarding.version": "Phiên bản: {{version}}", + "wsl.onboarding.unknown": "không rõ", + "wsl.onboarding.desktopVersion": "desktop {{version}}", + "wsl.onboarding.versionMismatch": "Phiên bản đã cài không khớp với phiên bản ứng dụng desktop.", + "wsl.onboarding.adding": "Đang thêm...", + "server.row.noUsername": "chưa có tên đăng nhập", + + "dialog.project.edit.title": "Sửa dự án", + "dialog.project.edit.name": "Tên", + "dialog.project.edit.icon": "Biểu tượng", + "dialog.project.edit.icon.alt": "Biểu tượng dự án", + "dialog.project.edit.icon.hint": "Nhấn hoặc kéo một hình ảnh vào", + "dialog.project.edit.icon.recommended": "Khuyến nghị: 128x128px", + "dialog.project.edit.color": "Màu", + "dialog.project.edit.color.select": "Chọn màu {{color}}", + "dialog.project.edit.worktree.startup": "Script khởi tạo workspace", + "dialog.project.edit.worktree.startup.description": "Chạy sau khi tạo một workspace (worktree) mới.", + "dialog.project.edit.worktree.startup.placeholder": "vd: bun install", + + "dialog.releaseNotes.action.getStarted": "Bắt đầu", + "dialog.releaseNotes.action.next": "Tiếp theo", + "dialog.releaseNotes.action.hideFuture": "Không hiện lại các mục này nữa", + "dialog.releaseNotes.media.alt": "Xem trước bản phát hành", + + "context.breakdown.title": "Phân tích context", + "context.breakdown.note": 'Phân tích ước lượng các token đầu vào. "Khác" gồm định nghĩa tool và phần phụ trợ.', + "context.breakdown.system": "Hệ thống", + "context.breakdown.user": "Người dùng", + "context.breakdown.assistant": "Trợ lý", + "context.breakdown.tool": "Lệnh gọi tool", + "context.breakdown.other": "Khác", + + "context.systemPrompt.title": "System prompt", + "context.rawMessages.title": "Tin nhắn thô", + + "context.stats.session": "Session", + "context.stats.messages": "Tin nhắn", + "context.stats.provider": "Provider", + "context.stats.model": "Model", + "context.stats.limit": "Giới hạn context", + "context.stats.totalTokens": "Tổng token", + "context.stats.usage": "Mức dùng", + "context.stats.inputTokens": "Token đầu vào", + "context.stats.outputTokens": "Token đầu ra", + "context.stats.reasoningTokens": "Token suy luận", + "context.stats.cacheTokens": "Token cache (đọc/ghi)", + "context.stats.userMessages": "Tin nhắn người dùng", + "context.stats.assistantMessages": "Tin nhắn trợ lý", + "context.stats.totalCost": "Tổng chi phí", + "context.stats.sessionCreated": "Session tạo lúc", + "context.stats.lastActivity": "Hoạt động gần nhất", + + "context.usage.tokens": "Token", + "context.usage.usage": "Mức dùng", + "context.usage.cost": "Chi phí", + "context.usage.clickToView": "Nhấn để xem context", + "context.usage.view": "Xem mức dùng context", + + "language.en": "English", + "language.zh": "简体中文", + "language.zht": "繁體中文", + "language.ko": "한국어", + "language.de": "Deutsch", + "language.es": "Español", + "language.vi": "Tiếng Việt", + "language.fr": "Français", + "language.da": "Dansk", + "language.ja": "日本語", + "language.pl": "Polski", + "language.ru": "Русский", + "language.ar": "العربية", + "language.no": "Norsk", + "language.br": "Português (Brasil)", + "language.bs": "Bosanski", + "language.uk": "Українська", + "language.th": "ไทย", + "language.tr": "Türkçe", + + "toast.language.title": "Ngôn ngữ", + "toast.language.description": "Đã chuyển sang {{language}}", + + "toast.theme.title": "Đã đổi chủ đề", + "toast.scheme.title": "Chế độ màu", + + "toast.workspace.enabled.title": "Đã bật workspace", + "toast.workspace.enabled.description": "Nhiều worktree giờ được hiển thị trong thanh bên", + "toast.workspace.disabled.title": "Đã tắt workspace", + "toast.workspace.disabled.description": "Chỉ worktree chính được hiển thị trong thanh bên", + + "toast.permissions.autoaccept.on.title": "Đang tự động chấp nhận quyền", + "toast.permissions.autoaccept.on.description": "Các yêu cầu quyền sẽ được tự động phê duyệt", + "toast.permissions.autoaccept.off.title": "Đã dừng tự động chấp nhận quyền", + "toast.permissions.autoaccept.off.description": "Các yêu cầu quyền sẽ cần được phê duyệt", + + "toast.model.none.title": "Chưa chọn model", + "toast.model.none.description": "Kết nối một provider để tóm tắt session này", + + "toast.file.loadFailed.title": "Không tải được tệp", + "toast.file.listFailed.title": "Không liệt kê được tệp", + + "toast.context.noLineSelection.title": "Chưa chọn dòng", + "toast.context.noLineSelection.description": "Hãy chọn một vùng dòng trong tab tệp trước.", + + "toast.session.share.copyFailed.title": "Không sao chép được URL vào clipboard", + "toast.session.share.success.title": "Đã chia sẻ session", + "toast.session.share.success.description": "Đã sao chép URL chia sẻ vào clipboard!", + "toast.session.share.failed.title": "Không chia sẻ được session", + "toast.session.share.failed.description": "Đã xảy ra lỗi khi chia sẻ session", + + "toast.session.unshare.success.title": "Đã hủy chia sẻ session", + "toast.session.unshare.success.description": "Đã hủy chia sẻ session thành công!", + "toast.session.unshare.failed.title": "Không hủy chia sẻ được session", + "toast.session.unshare.failed.description": "Đã xảy ra lỗi khi hủy chia sẻ session", + + "toast.session.listFailed.title": "Không tải được danh sách session của {{project}}", + "toast.project.reloadFailed.title": "Không tải lại được {{project}}", + + "toast.update.title": "Có bản cập nhật", + "toast.update.description": "Phiên bản mới của CoDev ({{version}}) đã sẵn sàng để cài đặt.", + "toast.update.description.available": "Phiên bản mới của CoDev ({{version}}) đã sẵn sàng để tải xuống.", + "toast.update.action.installRestart": "Cài đặt và khởi động lại", + "toast.update.action.notYet": "Để sau", + + "error.page.title": "Đã xảy ra sự cố", + "error.page.description": "Đã xảy ra lỗi khi tải ứng dụng.", + "error.page.description.localServerStartup": "Đã xảy ra lỗi khi khởi động server cục bộ.", + "error.page.details.label": "Chi tiết lỗi", + "error.page.action.restart": "Khởi động lại", + "error.page.action.report": "Báo lỗi", + "error.page.action.reported": "Đã báo lỗi", + "error.page.action.exportLogs": "Xuất log", + "error.page.action.checking": "Đang kiểm tra...", + "error.page.action.checkUpdates": "Kiểm tra cập nhật", + "error.page.action.updateTo": "Cập nhật lên {{version}}", + "error.page.circular": "[Circular]", + "error.page.report.prefix": "Vui lòng báo lỗi này cho đội ngũ CoDev", + "error.page.report.discord": "trên GitHub", + "error.page.version": "Phiên bản: {{version}}", + + "error.dev.rootNotFound": + "Không tìm thấy phần tử root. Bạn có quên thêm nó vào index.html không? Hoặc thuộc tính id có thể đã bị gõ sai?", + + "error.serverSync.connectFailed": "Không thể kết nối đến server. Có server nào đang chạy tại `{{url}}` không?", + "error.serverSDK.noServerAvailable": "Không có server khả dụng", + "error.serverSDK.serverNotAvailable": "Server không khả dụng", + "error.childStore.persistedCacheCreateFailed": "Không tạo được cache đã lưu", + "error.childStore.persistedProjectMetadataCreateFailed": "Không tạo được metadata dự án đã lưu", + "error.childStore.persistedProjectIconCreateFailed": "Không tạo được biểu tượng dự án đã lưu", + "error.childStore.storeCreateFailed": "Không tạo được store", + "directory.error.invalidUrl": "Thư mục trong URL không hợp lệ.", + + "error.chain.unknown": "Lỗi không xác định", + "error.server.invalidConfiguration": "Cấu hình không hợp lệ", + "error.chain.causedBy": "Nguyên nhân:", + "error.chain.apiError": "Lỗi API", + "error.chain.status": "Trạng thái: {{status}}", + "error.chain.retryable": "Có thể thử lại: {{retryable}}", + "error.chain.responseBody": "Nội dung phản hồi:\n{{body}}", + "error.chain.didYouMean": "Có phải bạn muốn: {{suggestions}}", + "error.chain.modelNotFound": "Không tìm thấy model: {{provider}}/{{model}}", + "error.chain.checkConfig": "Kiểm tra tên provider/model trong config của bạn (codev.json)", + "error.chain.mcpFailed": 'MCP server "{{name}}" gặp lỗi. Lưu ý, CoDev chưa hỗ trợ xác thực MCP.', + "error.chain.providerAuthFailed": "Xác thực provider thất bại ({{provider}}): {{message}}", + "error.chain.providerInitFailed": + 'Không khởi tạo được provider "{{provider}}". Hãy kiểm tra thông tin đăng nhập và cấu hình.', + "error.chain.configJsonInvalid": "Tệp cấu hình tại {{path}} không phải JSON(C) hợp lệ", + "error.chain.configJsonInvalidWithMessage": "Tệp cấu hình tại {{path}} không phải JSON(C) hợp lệ: {{message}}", + "error.chain.configDirectoryTypo": + 'Thư mục "{{dir}}" trong {{path}} không hợp lệ. Hãy đổi tên thư mục thành "{{suggestion}}" hoặc xóa nó. Đây là lỗi gõ nhầm thường gặp.', + "error.chain.configFrontmatterError": "Không phân tích được frontmatter trong {{path}}:\n{{message}}", + "error.chain.configInvalid": "Tệp cấu hình tại {{path}} không hợp lệ", + "error.chain.configInvalidWithMessage": "Tệp cấu hình tại {{path}} không hợp lệ: {{message}}", + + "notification.permission.title": "Cần cấp quyền", + "notification.permission.description": "{{sessionTitle}} trong {{projectName}} cần được cấp quyền", + "notification.question.title": "Câu hỏi", + "notification.question.description": "{{sessionTitle}} trong {{projectName}} có một câu hỏi", + "notification.action.goToSession": "Đi tới session", + + "notification.session.responseReady.title": "Phản hồi đã sẵn sàng", + "notification.session.error.title": "Lỗi session", + "notification.session.error.fallbackDescription": "Đã xảy ra lỗi", + + "home.recentProjects": "Dự án gần đây", + "home.empty.title": "Chưa có dự án gần đây", + "home.empty.description": "Bắt đầu bằng cách mở một dự án trên máy", + "home.title": "Trang chủ", + "home.projects": "Dự án", + "home.project.add": "Thêm dự án", + "home.recentlyClosed": "Đã đóng gần đây", + "home.server.collapse": "Thu gọn dự án trên server", + "home.server.expand": "Mở rộng dự án trên server", + "home.sessions.search.placeholder": "Tìm kiếm session", + "home.sessions.search.placeholder.scoped": "Tìm kiếm session trong {{scope}}", + "home.sessions.search.sessions": "Session", + "home.sessions.search.noResults": "Không tìm thấy session nào cho {{query}}", + "home.sessions.empty": "Chưa có gì ở đây", + "home.sessions.empty.description": "Tạo một session để bắt đầu", + "home.sessions.group.pinned": "Đã ghim", + "home.sessions.group.today": "Hôm nay", + "home.sessions.group.yesterday": "Hôm qua", + "home.sessions.group.older": "Cũ hơn", + "home.providerTip": "Kết nối với hơn 75 provider để dùng các model khác, bao gồm Claude, GPT, Gemini, v.v.", + + "session.tab.session": "Session", + "session.tab.review": "Review", + "session.tab.context": "Context", + "session.tab.unknown": "Session không xác định", + "session.panel.reviewAndFiles": "Review và tệp", + "session.error.notFound": "Không tìm thấy session này", + "session.error.notFound.description": "Tab này trỏ đến một session không còn tồn tại trên server.", + "session.error.notFound.closeTab": "Đóng tab", + // Fork: a sandbox is a server that comes and goes — it leaves while it + // restarts to apply a new mounted folder — and its tabs stay open across + // that gap, so its sessions need something truer to say than "not found". + "session.error.disconnected": "Đang chờ server này", + "session.error.disconnected.description": + "Tab này thuộc một server hiện không chạy. Nó sẽ tự trở lại khi server chạy lại.", + "session.error.serverConnection": "Không thể kết nối đến server này", + "session.review.filesChanged": "Tệp đã thay đổi {{count}}", + "session.review.change.one": "Thay đổi", + "session.review.change.other": "Thay đổi", + "session.review.loadingChanges": "Đang tải các thay đổi...", + "session.review.empty": "Session này chưa có thay đổi nào", + "session.review.noVcs": "Không phát hiện hệ thống quản lý phiên bản Git, các thay đổi không được hiển thị", + "session.review.noVcs.createGit.title": "Tạo Git repository", + "session.review.noVcs.createGit.description": "Theo dõi, review và hoàn tác các thay đổi trong dự án này", + "session.review.noVcs.createGit.actionLoading": "Đang tạo Git repository...", + "session.review.noVcs.createGit.action": "Tạo Git repository", + "session.review.noSnapshot": "Theo dõi snapshot đang tắt trong cấu hình, nên không xem được thay đổi của session", + "session.review.noChanges": "Không có thay đổi", + "session.review.noUncommittedChanges": "Chưa có thay đổi nào chưa commit", + "session.review.noBranchChanges": "Chưa có thay đổi nào trên branch", + + "session.files.selectToOpen": "Chọn một tệp để mở", + "session.files.all": "Tất cả tệp", + "session.files.empty": "Không có tệp", + "session.files.binaryContent": "Tệp nhị phân (không hiển thị được nội dung)", + + "session.messages.renderEarlier": "Hiển thị tin nhắn cũ hơn", + "session.messages.loadingEarlier": "Đang tải tin nhắn cũ hơn...", + "session.messages.loadEarlier": "Tải tin nhắn cũ hơn", + "session.messages.loading": "Đang tải tin nhắn...", + "session.messages.jumpToLatest": "Nhảy tới mới nhất", + + "session.context.addToContext": "Thêm {{selection}} vào context", + "session.todo.title": "Việc cần làm", + "session.todo.collapse": "Thu gọn", + "session.todo.expand": "Mở rộng", + "session.todo.progress": "Đã hoàn thành {{done}} / {{total}} việc", + "session.question.progress": "Câu hỏi {{current}} / {{total}}", + "session.question.minimize": "Thu nhỏ câu hỏi", + "session.question.restore": "Khôi phục câu hỏi", + "session.question.pending.one": "{{count}} câu hỏi đang chờ", + "session.question.pending.other": "{{count}} câu hỏi đang chờ", + "session.followupDock.summary.one": "{{count}} tin nhắn trong hàng đợi", + "session.followupDock.summary.other": "{{count}} tin nhắn trong hàng đợi", + "session.followupDock.sendNow": "Gửi ngay", + "session.followupDock.edit": "Chỉnh sửa", + "session.followupDock.collapse": "Thu gọn tin nhắn trong hàng đợi", + "session.followupDock.expand": "Mở rộng tin nhắn trong hàng đợi", + "session.revertDock.summary.one": "{{count}} tin nhắn đã hoàn tác", + "session.revertDock.summary.other": "{{count}} tin nhắn đã hoàn tác", + "session.revertDock.collapse": "Thu gọn tin nhắn đã hoàn tác", + "session.revertDock.expand": "Mở rộng tin nhắn đã hoàn tác", + "session.revertDock.restore": "Khôi phục tin nhắn", + + "session.new.title": "Xây dựng mọi thứ", + "session.new.project.new": "Dự án mới", + "session.new.project.search": "Tìm kiếm dự án", + "session.new.project.add": "Thêm dự án", + "session.new.worktree.main": "Branch chính", + "session.new.worktree.mainWithBranch": "Branch chính ({{branch}})", + "session.new.worktree.create": "Tạo worktree mới", + "session.new.workspace.runIn": "Chạy session trong", + "session.new.workspace.triggerLocal": "Cục bộ", + "session.new.workspace.local": "Repository cục bộ", + "session.new.workspace.existing": "Workspace…", + "session.new.git.none": "Không có Git", + "session.new.lastModified": "Sửa đổi lần cuối", + + "session.header.search.placeholder": "Tìm kiếm {{project}}", + "session.header.searchFiles": "Tìm kiếm tệp", + "session.header.openIn": "Mở trong", + "session.header.open.action": "Mở {{app}}", + "session.header.open.ariaLabel": "Mở trong {{app}}", + "session.header.open.menu": "Tùy chọn mở", + "session.header.open.copyPath": "Sao chép đường dẫn", + "session.header.open.finder": "Finder", + "session.header.open.fileExplorer": "File Explorer", + "session.header.open.fileManager": "Trình quản lý tệp", + "session.header.reveal.finder": "Hiện trong Finder", + "session.header.reveal.fileExplorer": "Hiện trong File Explorer", + "session.header.reveal.containingFolder": "Mở thư mục chứa tệp", + "session.header.open.app.vscode": "VS Code", + "session.header.open.app.cursor": "Cursor", + "session.header.open.app.zed": "Zed", + "session.header.open.app.textmate": "TextMate", + "session.header.open.app.antigravity": "Antigravity", + "session.header.open.app.terminal": "Terminal", + "session.header.open.app.iterm2": "iTerm2", + "session.header.open.app.ghostty": "Ghostty", + "session.header.open.app.warp": "Warp", + "session.header.open.app.xcode": "Xcode", + "session.header.open.app.androidStudio": "Android Studio", + "session.header.open.app.powershell": "PowerShell", + "session.header.open.app.sublimeText": "Sublime Text", + + "status.popover.trigger": "Trạng thái", + "status.popover.ariaLabel": "Cấu hình server", + "status.popover.tab.servers": "Server", + "status.popover.tab.mcp": "MCP", + "status.popover.tab.lsp": "LSP", + "status.popover.tab.plugins": "Plugin", + "status.popover.action.manageServers": "Quản lý server", + + "session.share.popover.title": "Đăng lên web", + "session.share.popover.description.shared": + "Session này đang công khai trên web. Bất kỳ ai có liên kết đều xem được.", + "session.share.popover.description.unshared": + "Chia sẻ session công khai trên web. Bất kỳ ai có liên kết đều xem được.", + "session.share.action.share": "Chia sẻ", + "session.share.action.publish": "Đăng", + "session.share.action.publishing": "Đang đăng...", + "session.share.action.unpublish": "Gỡ đăng", + "session.share.action.unpublishing": "Đang gỡ...", + "session.share.action.view": "Xem", + "session.share.copy.copied": "Đã sao chép", + "session.share.copy.copyLink": "Sao chép liên kết", + + "lsp.tooltip.none": "Không có LSP server", + "lsp.label.connected": "{{count}} LSP", + + "prompt.loading": "Đang tải prompt...", + "terminal.loading": "Đang tải terminal...", + "terminal.title": "Terminal", + "terminal.title.numbered": "Terminal {{number}}", + "terminal.close": "Đóng terminal", + "terminal.connectionLost.title": "Mất kết nối", + "terminal.connectionLost.abnormalClose": "WebSocket đóng bất thường: {{code}}", + "terminal.connectionLost.description": + "Kết nối terminal bị gián đoạn. Điều này có thể xảy ra khi server khởi động lại.", + + "common.closeTab": "Đóng tab", + "common.dismiss": "Bỏ qua", + "common.moreCountSuffix": " (+{{count}} nữa)", + "common.requestFailed": "Yêu cầu thất bại", + "common.moreOptions": "Tùy chọn khác", + "common.learnMore": "Tìm hiểu thêm", + "common.rename": "Đổi tên", + "common.reset": "Đặt lại", + "common.archive": "Lưu trữ", + "common.pin": "Ghim", + "common.unpin": "Bỏ ghim", + "common.delete": "Xóa", + "common.close": "Đóng", + "common.edit": "Chỉnh sửa", + "common.loadMore": "Tải thêm", + "common.key.esc": "ESC", + "common.key.ctrl": "Ctrl", + "common.key.alt": "Alt", + "common.key.shift": "Shift", + "common.key.meta": "Meta", + "common.key.space": "Space", + "common.key.backspace": "Backspace", + "common.key.enter": "Enter", + "common.key.tab": "Tab", + "common.key.delete": "Delete", + "common.key.home": "Home", + "common.key.end": "End", + "common.key.pageUp": "Page Up", + "common.key.pageDown": "Page Down", + "common.key.insert": "Insert", + "common.unknown": "không xác định", + + "common.time.justNow": "Vừa xong", + "common.time.minutesAgo.short": "{{count}} phút trước", + "common.time.hoursAgo.short": "{{count}} giờ trước", + "common.time.daysAgo.short": "{{count}} ngày trước", + + "sidebar.menu.toggle": "Bật/tắt menu", + "sidebar.nav.projectsAndSessions": "Dự án và session", + "sidebar.settings": "Cài đặt", + "sidebar.help": "Trợ giúp", + "sidebar.pinned": "Đã ghim", + "sidebar.recents": "Gần đây", + "sidebar.recents.new": "Session mới", + "sidebar.recents.viewAll": "Xem tất cả", + "sidebar.workspaces.enable": "Bật workspace", + "sidebar.workspaces.disable": "Tắt workspace", + "gateway.configure.title": "Thiết lập CoDev", + "gateway.configure.description": "Kết nối một model provider để bắt đầu dùng CoDev.", + "gateway.configure.option.sso.title": "Đăng nhập bằng SSO", + "gateway.configure.option.sso.description": "Đăng nhập bằng tài khoản CoDev của bạn", + "gateway.configure.option.custom.title": "Provider tùy chỉnh", + "gateway.configure.option.custom.description": "Endpoint tương thích OpenAI", + "gateway.configure.option.connect.title": "Kết nối provider", + "gateway.configure.option.connect.description": "Anthropic, OpenAI và các provider khác", + "gateway.configure.sso.pending": "Hoàn tất đăng nhập trong trình duyệt của bạn.", + "gateway.configure.sso.open": "Mở trang đăng nhập", + "gateway.configure.sso.copy": "Sao chép liên kết", + "gateway.configure.sso.copied": "Đã sao chép", + "gateway.configure.sso.paste.label": "URL callback hoặc mã", + "gateway.configure.sso.paste.placeholder": "Dán URL callback hoặc mã nếu trình duyệt không kết nối được tới máy này", + "gateway.configure.apikey.title": "API key của gateway", + "gateway.configure.apikey.description": "Tài khoản của bạn chưa có API key gateway. Hãy nhập thủ công.", + "gateway.configure.apikey.label": "API key", + "gateway.configure.model.title": "Chọn model mặc định", + "gateway.configure.status.setup": "Đang lấy cấu hình gateway...", + "gateway.configure.status.models": "Đang lấy danh sách model...", + "gateway.configure.status.configuring": "Đang cấu hình {{model}}...", + "gateway.configure.success.title": "Đã cấu hình CoDev", + "gateway.configure.success.description": "Đang dùng {{model}}", + "gateway.configure.error.title": "Thiết lập thất bại", + "gateway.configure.back": "Quay lại", + "sidebar.gettingStarted.title": "Bắt đầu", + "sidebar.gettingStarted.line1": "Thiết lập một provider để bắt đầu dùng CoDev.", + "sidebar.gettingStarted.line2": "Kết nối provider bất kỳ để dùng các model, gồm Claude, GPT, Gemini, v.v.", + "sidebar.project.recentSessions": "Session gần đây", + "sidebar.project.viewAllSessions": "Xem tất cả session", + "sidebar.project.clearNotifications": "Xóa thông báo", + "sidebar.empty.title": "Chưa mở dự án nào", + "sidebar.empty.description": "Mở một dự án để bắt đầu", + + "debugBar.ariaLabel": "Chẩn đoán hiệu năng khi phát triển", + "debugBar.na": "n/a", + "debugBar.nav.label": "NAV", + "debugBar.nav.tip": + "Lần chuyển route gần nhất chạm tới trang session, đo từ lúc router bắt đầu đến lần vẽ đầu tiên sau khi ổn định.", + "debugBar.fps.label": "FPS", + "debugBar.fps.tip": "Số khung hình mỗi giây trung bình trong 5 giây gần nhất.", + "debugBar.frame.label": "FRAME", + "debugBar.frame.tip": "Thời gian khung hình tệ nhất trong 5 giây gần nhất.", + "debugBar.jank.label": "JANK", + "debugBar.jank.tip": "Số khung hình vượt 32ms trong 5 giây gần nhất.", + "debugBar.long.label": "LONG", + "debugBar.long.tip": "Thời gian bị chặn và số long task trong 5 giây gần nhất. Task dài nhất: {{max}}.", + "debugBar.delay.label": "DELAY", + "debugBar.delay.tip": "Độ trễ đầu vào tệ nhất quan sát được trong 5 giây gần nhất.", + "debugBar.inp.label": "INP", + "debugBar.inp.tip": + "Thời lượng tương tác xấp xỉ trong 5 giây gần nhất. Chỉ số kiểu INP, không phải INP chính thức của Web Vitals.", + "debugBar.cls.label": "CLS", + "debugBar.cls.tip": "Tổng dịch chuyển bố cục (CLS) trong suốt vòng đời hiện tại của ứng dụng.", + "debugBar.mem.label": "MEM", + "debugBar.mem.tipUnavailable": "Heap JS đã dùng so với giới hạn heap. Chỉ có trên Chromium.", + "debugBar.mem.tip": "Heap JS đã dùng so với giới hạn heap. {{used}} trên {{limit}}.", + + "app.name.desktop": "CoDev Desktop", + + "settings.section.desktop": "Desktop", + "settings.section.server": "Server", + "settings.tab.general": "Chung", + "settings.tab.shortcuts": "Phím tắt", + "settings.desktop.section.wsl": "WSL", + "settings.desktop.wsl.title": "Tích hợp WSL", + "settings.desktop.wsl.description": "Chạy CoDev server bên trong WSL trên Windows.", + + "settings.general.section.appearance": "Giao diện", + "settings.general.section.advanced": "Nâng cao", + "settings.general.section.notifications": "Thông báo hệ thống", + "settings.general.section.updates": "Cập nhật", + "settings.general.section.sounds": "Hiệu ứng âm thanh", + "settings.general.section.feed": "Feed", + "settings.general.section.display": "Hiển thị", + + "settings.general.row.language.title": "Ngôn ngữ", + "settings.general.row.language.description": "Thay đổi ngôn ngữ hiển thị của CoDev", + "settings.general.row.shell.title": "Shell của terminal", + "settings.general.row.shell.description": "Shell được terminal và các công cụ của agent sử dụng", + "settings.general.row.shell.autoDefault": "Tự động (mặc định)", + "settings.general.row.shell.terminalOnly": "chỉ terminal", + "settings.general.row.appearance.title": "Giao diện", + "settings.general.row.appearance.description": "Tùy chỉnh cách CoDev hiển thị trên thiết bị của bạn", + "settings.general.row.colorScheme.title": "Chế độ màu", + "settings.general.row.colorScheme.description": "Chọn CoDev theo giao diện hệ thống, sáng hay tối", + "settings.general.row.theme.title": "Chủ đề", + "settings.general.row.theme.description": "Tùy chỉnh chủ đề màu của CoDev.", + "settings.general.row.font.title": "Phông chữ code", + "settings.general.row.font.description": "Tùy chỉnh phông chữ trong các khối code", + "settings.general.row.terminalFont.title": "Phông chữ terminal", + "settings.general.row.terminalFont.description": "Tùy chỉnh phông chữ trong terminal", + "settings.general.row.uiFont.title": "Phông chữ giao diện", + "settings.general.row.uiFont.description": "Tùy chỉnh phông chữ dùng trong toàn bộ giao diện", + "settings.general.row.followup.title": "Hành vi tin nhắn tiếp theo", + "settings.general.row.followup.description": "Chọn prompt gửi tiếp sẽ điều hướng agent ngay hay chờ trong hàng đợi", + "settings.general.row.followup.option.queue": "Hàng đợi", + "settings.general.row.followup.option.steer": "Điều hướng ngay", + "settings.general.row.showFileTree.title": "Cây tệp", + "settings.general.row.showFileTree.description": "Hiển thị bảng cây tệp trong session", + "settings.general.row.showNavigation.title": "Nút điều hướng", + "settings.general.row.showNavigation.description": "Hiển thị nút quay lại và tiến tới trên thanh tiêu đề desktop", + "settings.general.row.showSearch.title": "Bảng lệnh", + "settings.general.row.showSearch.description": "Hiển thị nút tìm kiếm và bảng lệnh trên thanh tiêu đề", + "settings.general.row.showTerminal.title": "Terminal", + "settings.general.row.showTerminal.description": "Hiển thị nút terminal trên thanh tiêu đề desktop", + "settings.general.row.showStatus.title": "Trạng thái server", + "settings.general.row.showStatus.description": "Hiển thị nút trạng thái server trên thanh tiêu đề", + "settings.general.row.mobileTitlebarBottom.title": "Điều hướng ở dưới", + "settings.general.row.mobileTitlebarBottom.description": + "Đặt thanh tiêu đề và các tab session xuống cuối màn hình trên di động", + "settings.general.row.showCustomAgents.title": "Hiện agent", + "settings.general.row.showCustomAgents.description": + "Chuyển đổi giữa các agent trong khung soạn thảo. Khi ẩn, mặc định dùng agent Build.", + "settings.general.row.showWork.title": "Hiện tab Work", + "settings.general.row.showWork.description": + "Luôn hiển thị tab Code/Work kể cả khi không có sandbox. Tạo sandbox sẽ luôn hiển thị Work.", + "settings.general.row.reasoningSummaries.title": "Hiện tóm tắt suy luận", + "settings.general.row.reasoningSummaries.description": "Hiển thị tóm tắt suy luận của model trong dòng thời gian", + "settings.general.row.shellToolPartsExpanded.title": "Mở rộng phần công cụ shell", + "settings.general.row.shellToolPartsExpanded.description": + "Mặc định hiển thị các phần công cụ shell ở dạng mở rộng trong dòng thời gian", + "settings.general.row.editToolPartsExpanded.title": "Mở rộng phần công cụ chỉnh sửa", + "settings.general.row.editToolPartsExpanded.description": + "Mặc định hiển thị các phần công cụ edit, write và patch ở dạng mở rộng trong dòng thời gian", + "settings.general.row.newInterface.title": "Bố cục mới", + "settings.general.row.newInterface.badge": "Mới", + "settings.general.row.newInterface.description": + "Dùng bố cục tab và trang chủ mới. Bạn có thể chuyển qua lại giữa hai bố cục trong thời gian giới hạn.", + "settings.general.row.newInterfaceNotice.title": "Bạn đang dùng bố cục mới", + "settings.general.row.newInterfaceNotice.description": "Bố cục cũ không còn khả dụng", + "settings.general.row.newInterfaceNotice.dismiss": "Bỏ qua", + "settings.general.row.pinchZoom.title": "Chụm để thu phóng", + "settings.general.row.pinchZoom.description": "Cho phép thu phóng bằng cử chỉ chụm trên trackpad và Ctrl-cuộn", + + "settings.general.row.wayland.title": "Dùng Wayland gốc", + "settings.general.row.wayland.description": "Tắt chế độ dự phòng X11 trên Wayland. Cần khởi động lại.", + "settings.general.row.wayland.tooltip": + "Trên Linux với nhiều màn hình khác tần số quét, Wayland gốc có thể ổn định hơn.", + + "settings.general.row.releaseNotes.title": "Ghi chú phát hành", + "settings.general.row.releaseNotes.description": "Hiện cửa sổ Có gì mới sau khi cập nhật", + + "settings.updates.row.autoCheck.title": "Tự động kiểm tra cập nhật", + "settings.updates.row.autoCheck.description": "Tìm bản cập nhật khi CoDev khởi chạy và mỗi mười phút khi đang chạy", + "settings.updates.row.autoDownload.title": "Tự động tải bản cập nhật", + "settings.updates.row.autoDownload.description": + "Tải bản cập nhật ngay khi phát hiện. Tắt tùy chọn này để được hỏi trước — bản tải nặng vài trăm megabyte", + "settings.updates.action.download": "Tải xuống", + "settings.updates.row.check.title": "Kiểm tra cập nhật", + "settings.updates.row.check.description": "Kiểm tra cập nhật thủ công và cài đặt nếu có", + "settings.updates.action.checkNow": "Kiểm tra ngay", + "settings.updates.action.checking": "Đang kiểm tra...", + "settings.updates.action.downloading": "Đang tải xuống...", + "settings.updates.action.installing": "Đang cài đặt...", + "settings.updates.toast.latest.title": "Bạn đang dùng bản mới nhất", + "settings.updates.toast.latest.description": "Bạn đang chạy phiên bản mới nhất của CoDev.", + "sound.option.none": "Không", + "sound.option.alert01": "Alert 01", + "sound.option.alert02": "Alert 02", + "sound.option.alert03": "Alert 03", + "sound.option.alert04": "Alert 04", + "sound.option.alert05": "Alert 05", + "sound.option.alert06": "Alert 06", + "sound.option.alert07": "Alert 07", + "sound.option.alert08": "Alert 08", + "sound.option.alert09": "Alert 09", + "sound.option.alert10": "Alert 10", + "sound.option.bipbop01": "Bip-bop 01", + "sound.option.bipbop02": "Bip-bop 02", + "sound.option.bipbop03": "Bip-bop 03", + "sound.option.bipbop04": "Bip-bop 04", + "sound.option.bipbop05": "Bip-bop 05", + "sound.option.bipbop06": "Bip-bop 06", + "sound.option.bipbop07": "Bip-bop 07", + "sound.option.bipbop08": "Bip-bop 08", + "sound.option.bipbop09": "Bip-bop 09", + "sound.option.bipbop10": "Bip-bop 10", + "sound.option.staplebops01": "Staplebops 01", + "sound.option.staplebops02": "Staplebops 02", + "sound.option.staplebops03": "Staplebops 03", + "sound.option.staplebops04": "Staplebops 04", + "sound.option.staplebops05": "Staplebops 05", + "sound.option.staplebops06": "Staplebops 06", + "sound.option.staplebops07": "Staplebops 07", + "sound.option.nope01": "Nope 01", + "sound.option.nope02": "Nope 02", + "sound.option.nope03": "Nope 03", + "sound.option.nope04": "Nope 04", + "sound.option.nope05": "Nope 05", + "sound.option.nope06": "Nope 06", + "sound.option.nope07": "Nope 07", + "sound.option.nope08": "Nope 08", + "sound.option.nope09": "Nope 09", + "sound.option.nope10": "Nope 10", + "sound.option.nope11": "Nope 11", + "sound.option.nope12": "Nope 12", + "sound.option.yup01": "Yup 01", + "sound.option.yup02": "Yup 02", + "sound.option.yup03": "Yup 03", + "sound.option.yup04": "Yup 04", + "sound.option.yup05": "Yup 05", + "sound.option.yup06": "Yup 06", + + "settings.general.notifications.agent.title": "Agent", + "settings.general.notifications.agent.description": "Hiện thông báo hệ thống khi agent hoàn thành hoặc cần chú ý", + "settings.general.notifications.permissions.title": "Quyền", + "settings.general.notifications.permissions.description": "Hiện thông báo hệ thống khi cần cấp quyền", + "settings.general.notifications.errors.title": "Lỗi", + "settings.general.notifications.errors.description": "Hiện thông báo hệ thống khi xảy ra lỗi", + + "settings.general.sounds.agent.title": "Agent", + "settings.general.sounds.agent.description": "Phát âm thanh khi agent hoàn thành hoặc cần chú ý", + "settings.general.sounds.permissions.title": "Quyền", + "settings.general.sounds.permissions.description": "Phát âm thanh khi cần cấp quyền", + "settings.general.sounds.errors.title": "Lỗi", + "settings.general.sounds.errors.description": "Phát âm thanh khi xảy ra lỗi", + + "settings.shortcuts.title": "Phím tắt", + "settings.shortcuts.reset.button": "Đặt lại mặc định", + "settings.shortcuts.reset.toast.title": "Đã đặt lại phím tắt", + "settings.shortcuts.reset.toast.description": "Phím tắt đã được đặt lại về mặc định.", + "settings.shortcuts.conflict.title": "Phím tắt đã được sử dụng", + "settings.shortcuts.conflict.description": "{{keybind}} đã được gán cho {{titles}}.", + "settings.shortcuts.unassigned": "Chưa gán", + "settings.shortcuts.pressKeys": "Nhấn phím", + "settings.shortcuts.search.placeholder": "Tìm kiếm phím tắt", + "settings.shortcuts.search.empty": "Không tìm thấy phím tắt", + + "settings.shortcuts.group.general": "Chung", + "settings.shortcuts.group.session": "Session", + "settings.shortcuts.group.navigation": "Điều hướng", + "settings.shortcuts.group.modelAndAgent": "Model và agent", + "settings.shortcuts.group.terminal": "Terminal", + "settings.shortcuts.group.prompt": "Prompt", + + "settings.providers.title": "Provider", + "settings.providers.description": "Cài đặt provider sẽ được cấu hình tại đây.", + "settings.providers.section.connected": "Provider đã kết nối", + "settings.providers.connected.empty": "Chưa kết nối provider nào", + "settings.providers.connected.environmentDescription": "Kết nối từ biến môi trường của bạn", + "settings.providers.section.popular": "Provider phổ biến", + "settings.providers.custom.description": "Thêm provider tương thích OpenAI bằng base URL.", + "settings.providers.tag.environment": "Môi trường", + "settings.providers.tag.config": "Cấu hình", + "settings.providers.tag.custom": "Tùy chỉnh", + "settings.providers.tag.other": "Khác", + "settings.models.title": "Model", + "settings.models.description": "Cài đặt model sẽ được cấu hình tại đây.", + "settings.agents.title": "Agent", + "settings.agents.description": "Cài đặt agent sẽ được cấu hình tại đây.", + "settings.commands.title": "Lệnh", + "settings.commands.description": "Cài đặt lệnh sẽ được cấu hình tại đây.", + "settings.mcp.title": "MCP", + "settings.mcp.description": "Cài đặt MCP sẽ được cấu hình tại đây.", + + "settings.permissions.title": "Quyền", + "settings.permissions.description": "Kiểm soát các công cụ server được phép dùng theo mặc định.", + "settings.permissions.section.tools": "Công cụ", + "settings.permissions.toast.updateFailed.title": "Không cập nhật được quyền", + + "settings.permissions.action.allow": "Cho phép", + "settings.permissions.action.ask": "Hỏi", + "settings.permissions.action.deny": "Từ chối", + + "settings.permissions.tool.read.title": "Read", + "settings.permissions.tool.read.description": "Đọc một tệp (khớp theo đường dẫn tệp)", + "settings.permissions.tool.edit.title": "Edit", + "settings.permissions.tool.edit.description": "Sửa đổi tệp, bao gồm edit, write và patch", + "settings.permissions.tool.glob.title": "Glob", + "settings.permissions.tool.glob.description": "Khớp tệp bằng mẫu glob", + "settings.permissions.tool.grep.title": "Grep", + "settings.permissions.tool.grep.description": "Tìm kiếm nội dung tệp bằng biểu thức chính quy", + "settings.permissions.tool.list.title": "List", + "settings.permissions.tool.list.description": "Liệt kê tệp trong một thư mục", + "settings.permissions.tool.bash.title": "Bash", + "settings.permissions.tool.bash.description": "Chạy lệnh shell", + "settings.permissions.tool.task.title": "Task", + "settings.permissions.tool.task.description": "Khởi chạy các agent con", + "settings.permissions.tool.skill.title": "Skill", + "settings.permissions.tool.skill.description": "Tải một skill theo tên", + "settings.permissions.tool.lsp.title": "LSP", + "settings.permissions.tool.lsp.description": "Chạy truy vấn language server", + "settings.permissions.tool.todowrite.title": "Todo Write", + "settings.permissions.tool.todowrite.description": "Cập nhật danh sách việc cần làm", + "settings.permissions.tool.webfetch.title": "Web Fetch", + "settings.permissions.tool.webfetch.description": "Tải nội dung từ một URL", + "settings.permissions.tool.websearch.title": "Web Search", + "settings.permissions.tool.websearch.description": "Tìm kiếm trên web", + "settings.permissions.tool.external_directory.title": "External Directory", + "settings.permissions.tool.external_directory.description": "Truy cập tệp bên ngoài thư mục dự án", + "settings.permissions.tool.doom_loop.title": "Doom Loop", + "settings.permissions.tool.doom_loop.description": "Phát hiện các lần gọi công cụ lặp lại với đầu vào giống hệt", + + "session.delete.failed.title": "Không xóa được session", + "session.delete.title": "Xóa session", + "session.delete.confirm": 'Xóa session "{{name}}"?', + "session.delete.button": "Xóa session", + + "workspace.new": "Workspace mới", + "workspace.type.local": "cục bộ", + "workspace.type.sandbox": "sandbox", + "workspace.create.failed.title": "Không tạo được workspace", + "workspace.delete.failed.title": "Không xóa được workspace", + "workspace.resetting.title": "Đang đặt lại workspace", + "workspace.resetting.description": "Có thể mất một phút.", + "workspace.reset.failed.title": "Không đặt lại được workspace", + "workspace.reset.success.title": "Đã đặt lại workspace", + "workspace.reset.success.description": "Workspace giờ khớp với branch mặc định.", + "workspace.error.stillPreparing": "Workspace vẫn đang chuẩn bị", + "workspace.status.checking": "Đang kiểm tra thay đổi chưa merge...", + "workspace.status.error": "Không kiểm tra được trạng thái git.", + "workspace.status.clean": "Không phát hiện thay đổi chưa merge.", + "workspace.status.dirty": "Phát hiện thay đổi chưa merge trong workspace này.", + "workspace.delete.title": "Xóa workspace", + "workspace.delete.confirm": 'Xóa workspace "{{name}}"?', + "workspace.delete.button": "Xóa workspace", + "workspace.reset.title": "Đặt lại workspace", + "workspace.reset.confirm": 'Đặt lại workspace "{{name}}"?', + "workspace.reset.button": "Đặt lại workspace", + "workspace.reset.archived.none": "Không session đang hoạt động nào sẽ bị lưu trữ.", + "workspace.reset.archived.one": "1 session sẽ được lưu trữ.", + "workspace.reset.archived.many": "{{count}} session sẽ được lưu trữ.", + "workspace.reset.note": "Thao tác này sẽ đặt lại workspace về branch mặc định.", +} diff --git a/packages/console/app/src/i18n/index.ts b/packages/console/app/src/i18n/index.ts index 6b97d22dfc..3b3b3b051d 100644 --- a/packages/console/app/src/i18n/index.ts +++ b/packages/console/app/src/i18n/index.ts @@ -1,6 +1,7 @@ import type { Locale } from "~/lib/language" import { dict as en } from "~/i18n/en" import { dict as es } from "~/i18n/es" +import { dict as vi } from "~/i18n/vi" export type Key = keyof typeof en export type Dict = Record @@ -9,5 +10,6 @@ const base = en satisfies Dict export function i18n(locale: Locale): Dict { if (locale === "en") return base + if (locale === "vi") return { ...base, ...vi } return { ...base, ...es } } diff --git a/packages/console/app/src/i18n/vi.ts b/packages/console/app/src/i18n/vi.ts new file mode 100644 index 0000000000..b78ee01dd8 --- /dev/null +++ b/packages/console/app/src/i18n/vi.ts @@ -0,0 +1,839 @@ +import type { Dict } from "./en" +import { dict as en } from "./en" + +export const dict = { + ...en, + "nav.github": "GitHub", + "nav.docs": "Tài liệu", + "nav.data": "Dữ liệu", + "nav.changelog": "Nhật ký thay đổi", + "nav.discord": "Discord", + "nav.x": "X", + "nav.enterprise": "Doanh nghiệp", + "nav.zen": "Zen", + "nav.go": "Go", + "nav.login": "Đăng nhập", + "nav.free": "Tải xuống", + "nav.home": "Trang chủ", + "nav.openMenu": "Mở menu", + "nav.getStartedFree": "Bắt đầu miễn phí", + "nav.logoAlt": "OpenCode", + + "nav.context.copyLogo": "Sao chép logo dạng SVG", + "nav.context.copyWordmark": "Sao chép wordmark dạng SVG", + "nav.context.brandAssets": "Tài sản thương hiệu", + + "footer.github": "GitHub", + "footer.docs": "Tài liệu", + "footer.changelog": "Nhật ký thay đổi", + "footer.feishu": "Feishu", + "footer.discord": "Discord", + "footer.x": "X", + + "legal.brand": "Thương hiệu", + "legal.privacy": "Quyền riêng tư", + "legal.terms": "Điều khoản", + + "email.title": "Hãy là người đầu tiên biết khi chúng tôi ra mắt sản phẩm mới", + "email.subtitle": "Tham gia danh sách chờ để được truy cập sớm.", + "email.placeholder": "Địa chỉ email", + "email.subscribe": "Đăng ký nhận tin", + "email.success": "Sắp xong rồi, hãy kiểm tra hộp thư và xác nhận địa chỉ email của bạn", + + "notFound.title": "Không tìm thấy | opencode", + "notFound.heading": "404 - Không tìm thấy trang", + "notFound.home": "Trang chủ", + "notFound.docs": "Tài liệu", + "notFound.github": "GitHub", + "notFound.discord": "Discord", + "notFound.logoLightAlt": "logo opencode sáng", + "notFound.logoDarkAlt": "logo opencode tối", + + "user.logout": "Đăng xuất", + + "auth.callback.error.codeMissing": "Không tìm thấy mã ủy quyền.", + + "workspace.select": "Chọn workspace", + "workspace.createNew": "+ Tạo workspace mới", + "workspace.modal.title": "Tạo workspace mới", + "workspace.modal.placeholder": "Nhập tên workspace", + + "common.cancel": "Hủy", + "common.creating": "Đang tạo...", + "common.create": "Tạo", + "common.contactUs": "Liên hệ với chúng tôi", + + "common.videoUnsupported": "Trình duyệt của bạn không hỗ trợ thẻ video.", + "common.figure": "Hình {{n}}.", + "common.faq": "FAQ", + "common.learnMore": "Tìm hiểu thêm", + + "error.invalidPlan": "Gói không hợp lệ", + "error.workspaceRequired": "Cần có ID workspace", + "error.alreadySubscribed": "Workspace này đã có gói đăng ký", + "error.limitRequired": "Cần nhập giới hạn.", + "error.monthlyLimitInvalid": "Hãy đặt một giới hạn hằng tháng hợp lệ.", + "error.workspaceNameRequired": "Cần nhập tên workspace.", + "error.nameTooLong": "Tên phải có tối đa 255 ký tự.", + "error.emailRequired": "Cần nhập email", + "error.roleRequired": "Cần chọn vai trò", + "error.idRequired": "Cần nhập ID", + "error.nameRequired": "Cần nhập tên", + "error.providerRequired": "Cần chọn provider", + "error.apiKeyRequired": "Cần nhập API key", + "error.modelRequired": "Cần chọn model", + "error.reloadAmountMin": "Số tiền nạp phải ít nhất ${{amount}}", + "error.reloadTriggerMin": "Ngưỡng số dư phải ít nhất ${{amount}}", + + "app.meta.description": "OpenCode - Agent lập trình mã nguồn mở.", + + "home.title": "OpenCode | Agent lập trình AI mã nguồn mở", + + "temp.title": "opencode | Agent lập trình AI dành cho terminal", + "temp.hero.title": "Agent lập trình AI dành cho terminal", + "temp.zen": "opencode zen", + "temp.getStarted": "Bắt đầu", + "temp.feature.native.title": "TUI native", + "temp.feature.native.body": "Giao diện terminal native, phản hồi nhanh và có thể tùy biến chủ đề", + "temp.feature.zen.beforeLink": "Một", + "temp.feature.zen.link": "danh sách model được tuyển chọn", + "temp.feature.zen.afterLink": "do opencode cung cấp", + "temp.feature.models.beforeLink": "Hỗ trợ hơn 75 LLM provider thông qua", + "temp.feature.models.afterLink": ", kể cả model local", + "temp.screenshot.caption": "TUI của opencode với chủ đề tokyonight", + "temp.screenshot.alt": "TUI của opencode với chủ đề tokyonight", + "temp.logoLightAlt": "logo opencode sáng", + "temp.logoDarkAlt": "logo opencode tối", + + "home.banner.badge": "Mới", + "home.banner.text": "Ra mắt tab cho desktop.", + "home.banner.platforms": "Có mặt trên macOS, Windows và Linux", + "home.banner.downloadNow": "Tải xuống ngay", + "home.banner.downloadBetaNow": "Tải bản beta cho desktop ngay", + + "home.promo.title": "Ra mắt tab cho desktop", + "home.promo.body": "Sắp xếp công việc và các session đang hoạt động bằng tab.", + "home.promo.cta": "Tải phiên bản mới nhất để bắt đầu.", + "home.promo.close": "Đóng thông báo ứng dụng desktop", + + "home.hero.title": "Agent lập trình AI mã nguồn mở", + "home.hero.subtitle.a": "Có sẵn model miễn phí, hoặc kết nối bất kỳ model nào từ bất kỳ provider nào,", + "home.hero.subtitle.b": "bao gồm Claude, GPT, Gemini và nhiều hơn nữa.", + + "home.install.ariaLabel": "Tùy chọn cài đặt", + + "home.what.title": "OpenCode là gì?", + "home.what.body": "OpenCode là một agent mã nguồn mở giúp bạn viết code trong terminal, IDE hoặc trên desktop.", + "home.what.lsp.title": "Hỗ trợ LSP", + "home.what.lsp.body": "Tự động nạp đúng LSP cho LLM", + "home.what.multiSession.title": "Đa session", + "home.what.multiSession.body": "Chạy nhiều agent song song trên cùng một dự án", + "home.what.shareLinks.title": "Liên kết chia sẻ", + "home.what.shareLinks.body": "Chia sẻ liên kết tới bất kỳ session nào để tham khảo hoặc debug", + "home.what.copilot.title": "GitHub Copilot", + "home.what.copilot.body": "Đăng nhập bằng GitHub để dùng tài khoản Copilot của bạn", + "home.what.chatgptPlus.title": "ChatGPT Plus/Pro", + "home.what.chatgptPlus.body": "Đăng nhập bằng OpenAI để dùng tài khoản ChatGPT Plus hoặc Pro của bạn", + "home.what.anyModel.title": "Mọi model", + "home.what.anyModel.body": "Hơn 75 LLM provider thông qua Models.dev, kể cả model local", + "home.what.anyEditor.title": "Mọi trình soạn thảo", + "home.what.anyEditor.body": "Có mặt dưới dạng giao diện terminal, ứng dụng desktop và tiện ích IDE", + "home.what.readDocs": "Đọc tài liệu", + + "home.growth.title": "Agent lập trình AI mã nguồn mở", + "home.growth.body": + "Với hơn {{stars}} sao trên GitHub, {{contributors}} người đóng góp và hơn {{commits}} commit, OpenCode được hơn {{monthlyUsers}} lập trình viên tin dùng mỗi tháng.", + "home.growth.githubStars": "Sao trên GitHub", + "home.growth.contributors": "Người đóng góp", + "home.growth.monthlyDevs": "Dev hằng tháng", + + "home.privacy.title": "Ưu tiên quyền riêng tư hàng đầu", + "home.privacy.body": + "OpenCode không lưu trữ code hay dữ liệu ngữ cảnh của bạn, nên có thể hoạt động trong những môi trường nhạy cảm về quyền riêng tư.", + "home.privacy.learnMore": "Tìm hiểu thêm về", + "home.privacy.link": "quyền riêng tư", + + "home.faq.q1": "OpenCode là gì?", + "home.faq.a1": + "OpenCode là một agent mã nguồn mở giúp bạn viết và chạy code với bất kỳ model AI nào. Có mặt dưới dạng giao diện terminal, ứng dụng desktop hoặc tiện ích IDE.", + "home.faq.q2": "Làm sao để dùng OpenCode?", + "home.faq.a2.before": "Cách dễ nhất để bắt đầu là đọc", + "home.faq.a2.link": "bài giới thiệu", + "home.faq.q3": "Tôi có cần thêm gói đăng ký AI để dùng OpenCode không?", + "home.faq.a3.p1": "Không hẳn — OpenCode đi kèm một bộ model miễn phí mà bạn có thể dùng mà không cần tạo tài khoản.", + "home.faq.a3.p2.beforeZen": + "Ngoài những model này, bạn có thể dùng các model lập trình phổ biến bằng cách tạo tài khoản", + "home.faq.a3.p2.afterZen": ".", + "home.faq.a3.p3": + "Dù chúng tôi khuyến khích dùng Zen, OpenCode cũng hoạt động với mọi provider phổ biến như OpenAI, Anthropic, xAI, v.v.", + "home.faq.a3.p4.beforeLocal": "Bạn thậm chí có thể kết nối", + "home.faq.a3.p4.localLink": "các model local của mình", + "home.faq.q4": "Tôi có thể dùng các gói đăng ký AI hiện có với OpenCode không?", + "home.faq.a4.p1": + "Có, OpenCode hỗ trợ gói đăng ký từ mọi provider lớn. Bạn có thể dùng gói Claude Pro/Max, ChatGPT Plus/Pro hoặc GitHub Copilot.", + "home.faq.q5": "Tôi chỉ dùng được OpenCode trong terminal thôi sao?", + "home.faq.a5.beforeDesktop": "Không còn nữa! OpenCode giờ đã có ứng dụng cho", + "home.faq.a5.desktop": "desktop", + "home.faq.a5.and": "và", + "home.faq.a5.web": "web", + "home.faq.q6": "OpenCode giá bao nhiêu?", + "home.faq.a6": + "OpenCode miễn phí 100%. Nó cũng đi kèm một bộ model miễn phí. Có thể phát sinh chi phí nếu bạn kết nối provider khác.", + "home.faq.q7": "Còn dữ liệu và quyền riêng tư thì sao?", + "home.faq.a7.p1": + "Dữ liệu và thông tin của bạn chỉ được lưu khi bạn dùng model miễn phí của chúng tôi hoặc tạo liên kết chia sẻ.", + "home.faq.a7.p2.beforeModels": "Tìm hiểu thêm về", + "home.faq.a7.p2.modelsLink": "các model của chúng tôi", + "home.faq.a7.p2.and": "và", + "home.faq.a7.p2.shareLink": "trang chia sẻ", + "home.faq.q8": "OpenCode có phải mã nguồn mở không?", + "home.faq.a8.p1": "Có, OpenCode hoàn toàn mã nguồn mở. Mã nguồn được công khai trên", + "home.faq.a8.p2": "theo", + "home.faq.a8.mitLicense": "Giấy phép MIT", + "home.faq.a8.p3": + ", nghĩa là bất kỳ ai cũng có thể dùng, chỉnh sửa hoặc đóng góp vào quá trình phát triển. Bất kỳ ai trong cộng đồng đều có thể tạo issue, gửi pull request và mở rộng tính năng.", + + "home.zenCta.title": "Truy cập các model tối ưu, đáng tin cậy cho agent lập trình", + "home.zenCta.body": + "Zen cho bạn truy cập một bộ model AI chọn lọc đã được OpenCode kiểm thử và benchmark riêng cho agent lập trình. Không cần lo về hiệu năng và chất lượng thiếu nhất quán giữa các provider — hãy dùng những model đã được kiểm chứng.", + "home.zenCta.link": "Tìm hiểu về Zen", + + "zen.title": "OpenCode Zen | Bộ model tối ưu, đáng tin cậy được tuyển chọn cho agent lập trình", + "zen.hero.title": "Model tối ưu, đáng tin cậy cho agent lập trình", + "zen.hero.body": + "Zen cho bạn truy cập một bộ model AI được tuyển chọn, đã được OpenCode kiểm thử và benchmark riêng cho agent lập trình. Không cần lo về hiệu năng và chất lượng thiếu nhất quán — hãy dùng những model đã được kiểm chứng.", + + "zen.faq.q1": "OpenCode Zen là gì?", + "zen.faq.a1": + "Zen là bộ model AI được tuyển chọn, kiểm thử và benchmark cho agent lập trình, do đội ngũ đứng sau OpenCode tạo ra.", + "zen.faq.q2": "Điều gì khiến Zen chính xác hơn?", + "zen.faq.a2": + "Zen chỉ cung cấp những model đã được kiểm thử và benchmark riêng cho agent lập trình. Bạn sẽ không dùng dao phết bơ để cắt bít tết — đừng dùng model kém để viết code.", + "zen.faq.q3": "Zen có rẻ hơn không?", + "zen.faq.a3": + "Zen không vì lợi nhuận. Zen chuyển nguyên chi phí từ các provider model sang bạn. Zen càng được dùng nhiều, OpenCode càng đàm phán được giá tốt hơn và chuyển phần lợi đó cho bạn.", + "zen.faq.q4": "Zen giá bao nhiêu?", + "zen.faq.a4.p1.beforePricing": "Zen", + "zen.faq.a4.p1.pricingLink": "tính phí theo từng yêu cầu", + "zen.faq.a4.p1.afterPricing": "mà không cộng thêm giá, nên bạn trả đúng số tiền provider của model thu.", + "zen.faq.a4.p2.beforeAccount": + "Tổng chi phí phụ thuộc mức sử dụng, và bạn có thể đặt giới hạn chi tiêu hằng tháng trong", + "zen.faq.a4.p2.accountLink": "tài khoản", + "zen.faq.a4.p3": "Để bù chi phí, OpenCode chỉ thu một khoản phí xử lý thanh toán nhỏ $1.23 cho mỗi lần nạp $20.", + "zen.faq.q5": "Còn dữ liệu và quyền riêng tư thì sao?", + "zen.faq.a5.beforeExceptions": + "Tất cả model Zen được lưu trữ tại Mỹ. Các provider tuân thủ chính sách không lưu giữ dữ liệu và không dùng dữ liệu của bạn để huấn luyện model, trừ", + "zen.faq.a5.exceptionsLink": "các ngoại lệ sau", + "zen.faq.q6": "Tôi có thể đặt giới hạn chi tiêu không?", + "zen.faq.a6": "Có, bạn có thể đặt giới hạn chi tiêu hằng tháng trong tài khoản của mình.", + "zen.faq.q7": "Tôi có thể hủy không?", + "zen.faq.a7": "Có, bạn có thể tắt thanh toán bất cứ lúc nào và dùng nốt số dư còn lại.", + "zen.faq.q8": "Tôi có thể dùng Zen với agent lập trình khác không?", + "zen.faq.a8": + "Zen hoạt động tuyệt vời với OpenCode, nhưng bạn có thể dùng Zen với bất kỳ agent nào. Làm theo hướng dẫn thiết lập trong agent lập trình bạn ưa dùng.", + + "zen.cta.start": "Bắt đầu với Zen", + "zen.pricing.title": "Nạp $20 số dư trả theo mức dùng", + "zen.pricing.fee": "(+$1.23 phí xử lý thẻ)", + "zen.pricing.body": "Dùng với bất kỳ agent nào. Đặt giới hạn chi tiêu hằng tháng. Hủy bất cứ lúc nào.", + "zen.problem.title": "Zen giải quyết vấn đề gì?", + "zen.problem.body": + "Có rất nhiều model, nhưng chỉ số ít hoạt động tốt với agent lập trình. Hầu hết provider cấu hình chúng khác nhau với kết quả không đồng đều.", + "zen.problem.subtitle": "Chúng tôi đang khắc phục điều này cho tất cả mọi người, không chỉ người dùng OpenCode.", + "zen.problem.item1": "Kiểm thử các model chọn lọc và tham vấn đội ngũ phát triển chúng", + "zen.problem.item2": "Làm việc với các provider để bảo đảm model được cung cấp đúng cách", + "zen.problem.item3": "Benchmark mọi tổ hợp model-provider mà chúng tôi khuyên dùng", + "zen.how.title": "Zen hoạt động thế nào", + "zen.how.body": "Chúng tôi khuyên bạn dùng Zen với OpenCode, nhưng bạn có thể dùng Zen với bất kỳ agent nào.", + "zen.how.step1.title": "Đăng ký và nạp $20", + "zen.how.step1.beforeLink": "làm theo", + "zen.how.step1.link": "hướng dẫn thiết lập", + "zen.how.step2.title": "Dùng Zen với giá minh bạch", + "zen.how.step2.link": "trả theo từng yêu cầu", + "zen.how.step2.afterLink": "không cộng thêm giá", + "zen.how.step3.title": "Tự động nạp thêm", + "zen.how.step3.body": "khi số dư còn $5, chúng tôi sẽ tự động nạp thêm $20", + "zen.privacy.title": "Quyền riêng tư của bạn rất quan trọng với chúng tôi", + "zen.privacy.beforeExceptions": + "Tất cả model Zen được lưu trữ tại Mỹ. Các provider tuân thủ chính sách không lưu giữ dữ liệu và không dùng dữ liệu của bạn để huấn luyện model, trừ", + "zen.privacy.exceptionsLink": "các ngoại lệ sau", + + "go.title": "OpenCode Go | Model lập trình chi phí thấp cho mọi người", + "go.banner.text": "Kimi K3 được nhân đôi giới hạn sử dụng trong thời gian có hạn", + "go.meta.description": + "Go khởi điểm $5 cho tháng đầu, sau đó $10/tháng, với giới hạn yêu cầu 5 giờ hào phóng cho Grok 4.5, GLM-5.2, GLM-5.1, Kimi K3, Kimi K2.7 Code, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.7 Plus, Qwen3.6 Plus, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro và DeepSeek V4 Flash.", + "go.hero.title": "Model lập trình chi phí thấp cho mọi người", + "go.hero.body": + "Go mang lập trình bằng agent đến với lập trình viên khắp thế giới. Cung cấp giới hạn hào phóng và truy cập ổn định tới những model mã nguồn mở mạnh nhất, để bạn xây dựng với các agent mạnh mẽ mà không phải lo về chi phí hay tính sẵn sàng.", + + "go.cta.start": "Đăng ký Go", + "go.cta.template": "{{text}} {{price}}", + "go.cta.text": "Đăng ký Go", + "go.cta.price": "$10/tháng", + "go.cta.promo": "$5 tháng đầu", + "go.pricing.body": "Dùng với bất kỳ agent nào. $5 tháng đầu, sau đó $10/tháng. Nạp thêm khi cần. Hủy bất cứ lúc nào.", + "go.graph.free": "Miễn phí", + "go.graph.freePill": "Big Pickle và các model miễn phí", + "go.graph.go": "Go", + "go.graph.label": "Yêu cầu mỗi 5 giờ", + "go.graph.usageLimits": "Giới hạn sử dụng", + "go.graph.aria": "Yêu cầu mỗi 5 giờ: {{free}} so với {{go}}", + + "go.testimonials.brand.zen": "Zen", + "go.testimonials.brand.go": "Go", + "go.testimonials.handle": "@OpenCode", + "go.testimonials.dax.name": "Dax Raad", + "go.testimonials.dax.title": "cựu CEO, Terminal Products", + "go.testimonials.dax.quoteAfter": "đã thay đổi hoàn toàn cách tôi làm việc, chọn dùng nó là điều hiển nhiên.", + "go.testimonials.jay.name": "Jay V", + "go.testimonials.jay.title": "cựu nhà sáng lập, SEED, PM, Melt, Pop, Dapt, Cadmus và ViewPoint", + "go.testimonials.jay.quoteBefore": "4 trên 5 người trong đội chúng tôi thích dùng", + "go.testimonials.jay.quoteAfter": ".", + "go.testimonials.adam.name": "Adam Elmore", + "go.testimonials.adam.title": "cựu Hero, AWS", + "go.testimonials.adam.quoteBefore": "Tôi không thể giới thiệu", + "go.testimonials.adam.quoteAfter": "nhiều hơn được nữa. Nghiêm túc đấy, nó thực sự rất tốt.", + "go.testimonials.david.name": "David Hill", + "go.testimonials.david.title": "cựu Trưởng nhóm thiết kế, Laravel", + "go.testimonials.david.quoteBefore": "Với", + "go.testimonials.david.quoteAfter": "tôi biết mọi model đều đã được kiểm thử và hoàn hảo cho agent lập trình.", + "go.testimonials.frank.name": "Frank Wang", + "go.testimonials.frank.title": "cựu thực tập sinh, Nvidia (4 lần)", + "go.testimonials.frank.quote": "Ước gì tôi vẫn còn ở Nvidia.", + "go.problem.title": "Go giải quyết vấn đề gì?", + "go.problem.body": + "Chúng tôi tập trung mang trải nghiệm OpenCode đến với càng nhiều người càng tốt. OpenCode Go là gói đăng ký chi phí thấp: $5 cho tháng đầu, sau đó $10/tháng. Gói cung cấp giới hạn hào phóng và truy cập ổn định tới những model mã nguồn mở mạnh nhất.", + "go.problem.subtitle": " ", + "go.problem.item1": "Giá gói đăng ký chi phí thấp", + "go.problem.item2": "Giới hạn hào phóng và truy cập ổn định", + "go.problem.item3": "Xây dựng cho càng nhiều lập trình viên càng tốt", + "go.problem.item4": + "Bao gồm Grok 4.5, GLM-5.2, GLM-5.1, Kimi K3, Kimi K2.7 Code, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.7 Plus, Qwen3.6 Plus, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro và DeepSeek V4 Flash", + "go.how.title": "Go hoạt động thế nào", + "go.how.body": "Go khởi điểm $5 cho tháng đầu, sau đó $10/tháng. Bạn có thể dùng với OpenCode hoặc bất kỳ agent nào.", + "go.how.step1.title": "Tạo tài khoản", + "go.how.step1.beforeLink": "làm theo", + "go.how.step1.link": "hướng dẫn thiết lập", + "go.how.step2.title": "Đăng ký Go", + "go.how.step2.link": "$5 tháng đầu", + "go.how.step2.afterLink": "sau đó $10/tháng với giới hạn hào phóng", + "go.how.step3.title": "Bắt đầu code", + "go.how.step3.body": "với truy cập ổn định tới các model mã nguồn mở", + "go.privacy.title": "Quyền riêng tư của bạn rất quan trọng với chúng tôi", + "go.privacy.body": + "Gói này được thiết kế chủ yếu cho người dùng quốc tế, với model được lưu trữ tại Mỹ, EU và Singapore để truy cập toàn cầu ổn định.", + "go.privacy.contactAfter": "nếu bạn có bất kỳ câu hỏi nào.", + "go.privacy.beforeExceptions": + "Các model Go được lưu trữ tại Mỹ. Provider tuân thủ chính sách không lưu giữ dữ liệu và không dùng dữ liệu của bạn để huấn luyện model, trừ", + "go.privacy.exceptionsLink": "các ngoại lệ sau", + "go.faq.q1": "OpenCode Go là gì?", + "go.faq.a1": + "Go là gói đăng ký chi phí thấp cho bạn truy cập ổn định tới các model mã nguồn mở mạnh mẽ phục vụ lập trình bằng agent.", + "go.faq.q2": "Go gồm những model nào?", + "go.faq.a2": "Go bao gồm các model liệt kê bên dưới, với giới hạn hào phóng và truy cập ổn định.", + "go.faq.q3": "Go có giống Zen không?", + "go.faq.a3": + "Không. Zen trả theo mức dùng, còn Go khởi điểm $5 cho tháng đầu, sau đó $10/tháng, với giới hạn hào phóng và truy cập ổn định tới các model mã nguồn mở Grok 4.5, GLM-5.2, GLM-5.1, Kimi K3, Kimi K2.7 Code, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.7 Plus, Qwen3.6 Plus, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro và DeepSeek V4 Flash.", + "go.faq.q4": "Go giá bao nhiêu?", + "go.faq.a4.p1.beforePricing": "Go có giá", + "go.faq.a4.p1.pricingLink": "$5 tháng đầu", + "go.faq.a4.p1.afterPricing": "sau đó $10/tháng với giới hạn hào phóng.", + "go.faq.a4.p2.beforeAccount": "Bạn có thể quản lý gói đăng ký trong", + "go.faq.a4.p2.accountLink": "tài khoản", + "go.faq.a4.p3": "Hủy bất cứ lúc nào.", + "go.faq.q5": "Còn dữ liệu và quyền riêng tư thì sao?", + "go.faq.a5.body": + "Gói này được thiết kế chủ yếu cho người dùng quốc tế, với model được lưu trữ tại Mỹ, EU và Singapore để truy cập toàn cầu ổn định. Các provider của chúng tôi tuân thủ chính sách không lưu giữ dữ liệu và không dùng dữ liệu của bạn để huấn luyện model.", + + "go.faq.a5.beforeExceptions": + "Các model Go được lưu trữ tại Mỹ. Provider tuân thủ chính sách không lưu giữ dữ liệu và không dùng dữ liệu của bạn để huấn luyện model, trừ", + "go.faq.a5.exceptionsLink": "các ngoại lệ sau", + "go.faq.q6": "Tôi có thể nạp thêm không?", + "go.faq.a6": "Nếu cần dùng nhiều hơn, bạn có thể nạp thêm trong tài khoản của mình.", + "go.faq.q7": "Tôi có thể hủy không?", + "go.faq.a7": "Có, bạn có thể hủy bất cứ lúc nào.", + "go.faq.q8": "Tôi có thể dùng Go với agent lập trình khác không?", + "go.faq.a8": + "Có, bạn có thể dùng Go với bất kỳ agent nào. Làm theo hướng dẫn thiết lập trong agent lập trình bạn ưa dùng.", + + "go.faq.q9": "Model miễn phí khác gì Go?", + "go.faq.a9": + "Model miễn phí gồm Big Pickle cùng các model khuyến mãi tại từng thời điểm, với hạn mức 200 yêu cầu/ngày. Go bao gồm Grok 4.5, GLM-5.2, GLM-5.1, Kimi K3, Kimi K2.7 Code, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.7 Plus, Qwen3.6 Plus, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro và DeepSeek V4 Flash với hạn mức yêu cầu cao hơn, áp dụng theo các khung thời gian trượt (5 giờ, tuần và tháng), tương đương khoảng $12 mỗi 5 giờ, $30 mỗi tuần và $60 mỗi tháng (số yêu cầu thực tế thay đổi theo model và mức sử dụng).", + + "zen.api.error.rateLimitExceeded": "Đã vượt giới hạn tốc độ. Vui lòng thử lại sau.", + "zen.api.error.modelNotSupported": "Model {{model}} không được hỗ trợ", + "zen.api.error.modelFormatNotSupported": "Model {{model}} không được hỗ trợ cho định dạng {{format}}", + "zen.api.error.noProviderAvailable": "Không có provider khả dụng", + "zen.api.error.providerNotSupported": "Provider {{provider}} không được hỗ trợ", + "zen.api.error.missingApiKey": "Thiếu API key.", + "zen.api.error.invalidApiKey": "API key không hợp lệ.", + "zen.api.error.subscriptionQuotaExceeded": "Đã vượt hạn mức của gói đăng ký. Thử lại sau {{retryIn}}.", + "zen.api.error.goSubscriptionRollingLimitExceeded": + "Đã chạm giới hạn sử dụng 5 giờ. Đặt lại sau {{retryIn}}. Để tiếp tục dùng model này ngay, hãy bật sử dụng từ số dư khả dụng: {{consoleGoUrl}}", + "zen.api.error.goSubscriptionWeeklyLimitExceeded": + "Đã chạm giới hạn sử dụng theo tuần. Đặt lại sau {{retryIn}}. Để tiếp tục dùng model này ngay, hãy bật sử dụng từ số dư khả dụng: {{consoleGoUrl}}", + "zen.api.error.goSubscriptionMonthlyLimitExceeded": + "Đã chạm giới hạn sử dụng theo tháng. Đặt lại sau {{retryIn}}. Để tiếp tục dùng model này ngay, hãy bật sử dụng từ số dư khả dụng: {{consoleGoUrl}}", + "zen.api.error.noPaymentMethod": + "Chưa có phương thức thanh toán. Thêm phương thức thanh toán tại đây: {{billingUrl}}", + "zen.api.error.insufficientBalance": "Số dư không đủ. Quản lý thanh toán tại đây: {{billingUrl}}", + "zen.api.error.workspaceMonthlyLimitReached": + "Workspace của bạn đã chạm giới hạn chi tiêu hằng tháng ${{amount}}. Quản lý giới hạn tại đây: {{billingUrl}}", + "zen.api.error.userMonthlyLimitReached": + "Bạn đã chạm giới hạn chi tiêu hằng tháng ${{amount}}. Quản lý giới hạn tại đây: {{membersUrl}}", + "zen.api.error.modelDisabled": "Model đang bị tắt", + "zen.api.error.regionNotAllowed": + "Model này được lưu trữ tại Trung Quốc. Nếu muốn dùng model này, hãy bật trong phần cài đặt: {{consoleGoUrl}}", + "zen.api.error.trialEnded": + "Khuyến mãi miễn phí cho {{model}} đã kết thúc. Bạn có thể tiếp tục dùng model bằng cách đăng ký OpenCode Go - {{link}}", + + "black.meta.title": "OpenCode Black | Truy cập mọi model lập trình tốt nhất thế giới", + "black.meta.description": "Truy cập Claude, GPT, Gemini và nhiều hơn nữa với các gói đăng ký OpenCode Black.", + "black.hero.title": "Truy cập mọi model lập trình tốt nhất thế giới", + "black.hero.subtitle": "Bao gồm Claude, GPT, Gemini và nhiều hơn nữa", + "black.title": "OpenCode Black | Bảng giá", + "black.paused": "Việc đăng ký gói Black đang tạm dừng.", + "black.plan.icon20": "Gói Black 20", + "black.plan.icon100": "Gói Black 100", + "black.plan.icon200": "Gói Black 200", + "black.plan.multiplier100": "Mức sử dụng gấp 5 lần Black 20", + "black.plan.multiplier200": "Mức sử dụng gấp 20 lần Black 20", + "black.price.perMonth": "mỗi tháng", + "black.price.perPersonBilledMonthly": "mỗi người, thanh toán hằng tháng", + "black.terms.1": "Gói đăng ký của bạn sẽ không bắt đầu ngay lập tức", + "black.terms.2": "Bạn sẽ được thêm vào danh sách chờ và kích hoạt sớm", + "black.terms.3": "Thẻ của bạn chỉ bị trừ tiền khi gói đăng ký được kích hoạt", + "black.terms.4": "Áp dụng giới hạn sử dụng; dùng tự động hóa nhiều có thể chạm giới hạn sớm hơn", + "black.terms.5": "Gói đăng ký dành cho cá nhân; đội nhóm vui lòng liên hệ Enterprise", + "black.terms.6": "Giới hạn có thể được điều chỉnh và các gói có thể ngừng cung cấp trong tương lai", + "black.terms.7": "Hủy gói đăng ký bất cứ lúc nào", + "black.action.continue": "Tiếp tục", + "black.finePrint.beforeTerms": "Giá hiển thị chưa bao gồm thuế", + "black.finePrint.terms": "Điều khoản dịch vụ", + "black.workspace.title": "OpenCode Black | Chọn workspace", + "black.workspace.selectPlan": "Chọn workspace cho gói này", + "black.workspace.name": "Workspace {{n}}", + "black.subscribe.title": "Đăng ký OpenCode Black", + "black.subscribe.paymentMethod": "Phương thức thanh toán", + "black.subscribe.loadingPaymentForm": "Đang tải biểu mẫu thanh toán...", + "black.subscribe.selectWorkspaceToContinue": "Chọn một workspace để tiếp tục", + "black.subscribe.failurePrefix": "Rất tiếc!", + "black.subscribe.error.generic": "Đã xảy ra lỗi", + "black.subscribe.error.invalidPlan": "Gói không hợp lệ", + "black.subscribe.error.workspaceRequired": "Cần có ID workspace", + "black.subscribe.error.alreadySubscribed": "Workspace này đã có gói đăng ký", + "black.subscribe.processing": "Đang xử lý...", + "black.subscribe.submit": "Đăng ký ${{plan}}", + "black.subscribe.form.chargeNotice": "Bạn chỉ bị trừ tiền khi gói đăng ký được kích hoạt", + "black.subscribe.success.title": "Bạn đã vào danh sách chờ OpenCode Black", + "black.subscribe.success.subscriptionPlan": "Gói đăng ký", + "black.subscribe.success.planName": "OpenCode Black {{plan}}", + "black.subscribe.success.amount": "Số tiền", + "black.subscribe.success.amountValue": "${{plan}} mỗi tháng", + "black.subscribe.success.paymentMethod": "Phương thức thanh toán", + "black.subscribe.success.dateJoined": "Ngày tham gia", + "black.subscribe.success.chargeNotice": "Thẻ của bạn sẽ bị trừ tiền khi gói đăng ký được kích hoạt", + + "workspace.nav.zen": "Zen", + "workspace.nav.go": "Go", + "workspace.nav.usage": "Mức sử dụng", + "workspace.nav.apiKeys": "API key", + "workspace.nav.members": "Thành viên", + "workspace.nav.billing": "Thanh toán", + "workspace.nav.settings": "Cài đặt", + + "workspace.home.banner.beforeLink": "Model tối ưu, đáng tin cậy cho agent lập trình.", + "workspace.lite.banner.beforeLink": "Model lập trình chi phí thấp cho mọi người.", + "workspace.home.billing.loading": "Đang tải...", + "workspace.home.billing.enable": "Bật thanh toán", + "workspace.home.billing.currentBalance": "Số dư hiện tại", + + "workspace.newUser.feature.tested.title": "Model đã kiểm thử và xác minh", + "workspace.newUser.feature.tested.body": + "Chúng tôi đã benchmark và kiểm thử model riêng cho agent lập trình để bảo đảm hiệu năng tốt nhất.", + "workspace.newUser.feature.quality.title": "Chất lượng cao nhất", + "workspace.newUser.feature.quality.body": + "Truy cập model được cấu hình cho hiệu năng tối ưu - không hạ cấp hay định tuyến sang provider rẻ hơn.", + "workspace.newUser.feature.lockin.title": "Không ràng buộc", + "workspace.newUser.feature.lockin.body": + "Dùng Zen với bất kỳ agent lập trình nào, và tiếp tục dùng provider khác với opencode bất cứ khi nào bạn muốn.", + "workspace.newUser.copyApiKey": "Sao chép API key", + "workspace.newUser.copyKey": "Sao chép key", + "workspace.newUser.copied": "Đã sao chép!", + "workspace.newUser.step.enableBilling": "Bật thanh toán", + "workspace.newUser.step.login.before": "Chạy", + "workspace.newUser.step.login.after": "và chọn opencode", + "workspace.newUser.step.pasteKey": "Dán API key của bạn", + "workspace.newUser.step.models.before": "Khởi động opencode và chạy", + "workspace.newUser.step.models.after": "để chọn model", + + "workspace.models.title": "Model", + "workspace.models.subtitle.beforeLink": "Quản lý những model mà thành viên workspace có thể truy cập.", + "workspace.models.table.model": "Model", + "workspace.models.table.enabled": "Đã bật", + + "workspace.providers.title": "Dùng API key của riêng bạn", + "workspace.providers.subtitle": "Cấu hình API key của riêng bạn từ các provider AI.", + "workspace.providers.placeholder": "Nhập API key {{provider}} ({{prefix}}...)", + "workspace.providers.configure": "Cấu hình", + "workspace.providers.edit": "Sửa", + "workspace.providers.delete": "Xóa", + "workspace.providers.saving": "Đang lưu...", + "workspace.providers.save": "Lưu", + "workspace.providers.table.provider": "Provider", + "workspace.providers.table.apiKey": "API key", + + "workspace.usage.title": "Lịch sử sử dụng", + "workspace.usage.subtitle": "Mức dùng API và chi phí gần đây.", + "workspace.usage.empty": "Thực hiện lệnh gọi API đầu tiên để bắt đầu.", + "workspace.usage.table.date": "Ngày", + "workspace.usage.table.model": "Model", + "workspace.usage.table.input": "Input", + "workspace.usage.table.output": "Output", + "workspace.usage.table.cost": "Chi phí", + "workspace.usage.table.session": "Session", + "workspace.usage.breakdown.input": "Input", + "workspace.usage.breakdown.cacheRead": "Đọc cache", + "workspace.usage.breakdown.cacheWrite": "Ghi cache", + "workspace.usage.breakdown.output": "Output", + "workspace.usage.breakdown.reasoning": "Suy luận", + "workspace.usage.subscription": "Black (${{amount}})", + "workspace.usage.lite": "Go (${{amount}})", + "workspace.usage.byok": "BYOK (${{amount}})", + + "workspace.cost.title": "Chi phí", + "workspace.cost.subtitle": "Chi phí sử dụng phân theo model.", + "workspace.cost.allModels": "Tất cả model", + "workspace.cost.allKeys": "Tất cả key", + "workspace.cost.deletedSuffix": "(đã xóa)", + "workspace.cost.empty": "Không có dữ liệu sử dụng cho khoảng thời gian đã chọn.", + "workspace.cost.subscriptionShort": "sub", + + "workspace.keys.title": "API key", + "workspace.keys.subtitle": "Quản lý API key của bạn để truy cập các dịch vụ opencode.", + "workspace.keys.create": "Tạo API key", + "workspace.keys.placeholder": "Nhập tên key", + "workspace.keys.empty": "Tạo một API key cho opencode Gateway", + "workspace.keys.table.name": "Tên", + "workspace.keys.table.key": "Key", + "workspace.keys.table.createdBy": "Người tạo", + "workspace.keys.table.lastUsed": "Dùng lần cuối", + "workspace.keys.copyApiKey": "Sao chép API key", + "workspace.keys.delete": "Xóa", + + "workspace.members.title": "Thành viên", + "workspace.members.subtitle": "Quản lý thành viên workspace và quyền của họ.", + "workspace.members.invite": "Mời thành viên", + "workspace.members.inviting": "Đang mời...", + "workspace.members.beta.beforeLink": "Workspace miễn phí cho đội nhóm trong giai đoạn beta.", + "workspace.members.form.invitee": "Người được mời", + "workspace.members.form.emailPlaceholder": "Nhập email", + "workspace.members.form.role": "Vai trò", + "workspace.members.form.monthlyLimit": "Giới hạn chi tiêu hằng tháng", + "workspace.members.noLimit": "Không giới hạn", + "workspace.members.noLimitLowercase": "không giới hạn", + "workspace.members.invited": "đã mời", + "workspace.members.edit": "Sửa", + "workspace.members.delete": "Xóa", + "workspace.members.saving": "Đang lưu...", + "workspace.members.save": "Lưu", + "workspace.members.table.email": "Email", + "workspace.members.table.role": "Vai trò", + "workspace.members.table.monthLimit": "Giới hạn tháng", + "workspace.members.role.admin": "Quản trị viên", + "workspace.members.role.adminDescription": "Có thể quản lý model, thành viên và thanh toán", + "workspace.members.role.member": "Thành viên", + "workspace.members.role.memberDescription": "Chỉ có thể tạo API key cho chính mình", + + "workspace.settings.title": "Cài đặt", + "workspace.settings.subtitle": "Cập nhật tên workspace và các tùy chọn.", + "workspace.settings.workspaceName": "Tên workspace", + "workspace.settings.defaultName": "Mặc định", + "workspace.settings.updating": "Đang cập nhật...", + "workspace.settings.save": "Lưu", + "workspace.settings.edit": "Sửa", + + "workspace.billing.title": "Thanh toán", + "workspace.billing.subtitle.beforeLink": "Quản lý phương thức thanh toán.", + "workspace.billing.contactUs": "Liên hệ với chúng tôi", + "workspace.billing.subtitle.afterLink": "nếu bạn có bất kỳ câu hỏi nào.", + "workspace.billing.currentBalance": "Số dư hiện tại", + "workspace.billing.add": "Nạp $", + "workspace.billing.enterAmount": "Nhập số tiền", + "workspace.billing.loading": "Đang tải...", + "workspace.billing.addAction": "Nạp", + "workspace.billing.addBalance": "Nạp số dư", + "workspace.billing.alipay": "Alipay", + "workspace.billing.wechat": "WeChat Pay", + "workspace.billing.linkedToStripe": "Đã liên kết với Stripe", + "workspace.billing.manage": "Quản lý", + "workspace.billing.enable": "Bật thanh toán", + + "workspace.monthlyLimit.title": "Giới hạn hằng tháng", + "workspace.monthlyLimit.subtitle": "Đặt giới hạn sử dụng hằng tháng cho tài khoản của bạn.", + "workspace.monthlyLimit.placeholder": "50", + "workspace.monthlyLimit.setting": "Đang đặt...", + "workspace.monthlyLimit.set": "Đặt", + "workspace.monthlyLimit.edit": "Sửa giới hạn", + "workspace.monthlyLimit.noLimit": "Chưa đặt giới hạn sử dụng.", + "workspace.monthlyLimit.currentUsage.beforeMonth": "Mức sử dụng hiện tại của", + "workspace.monthlyLimit.currentUsage.beforeAmount": "là $", + + "workspace.redeem.title": "Đổi mã khuyến mãi", + "workspace.redeem.subtitle": "Đổi mã khuyến mãi để nhận credit hoặc ưu đãi.", + "workspace.redeem.placeholder": "Nhập mã khuyến mãi", + "workspace.redeem.redeem": "Đổi mã", + "workspace.redeem.redeeming": "Đang đổi...", + "workspace.redeem.success": "Đổi mã thành công.", + + "workspace.reload.title": "Tự động nạp tiền", + "workspace.reload.disabled.before": "Tự động nạp tiền đang", + "workspace.reload.disabled.state": "tắt", + "workspace.reload.disabled.after": "Bật để tự động nạp khi số dư xuống thấp.", + "workspace.reload.enabled.before": "Tự động nạp tiền đang", + "workspace.reload.enabled.state": "bật", + "workspace.reload.enabled.middle": "Chúng tôi sẽ nạp", + "workspace.reload.processingFee": "phí xử lý", + "workspace.reload.enabled.after": "khi số dư chạm mức", + "workspace.reload.edit": "Sửa", + "workspace.reload.enable": "Bật", + "workspace.reload.enableAutoReload": "Bật tự động nạp tiền", + "workspace.reload.reloadAmount": "Nạp $", + "workspace.reload.whenBalanceReaches": "Khi số dư chạm $", + "workspace.reload.saving": "Đang lưu...", + "workspace.reload.save": "Lưu", + "workspace.reload.failedAt": "Nạp tiền thất bại lúc", + "workspace.reload.reason": "Lý do:", + "workspace.reload.updatePaymentMethod": "Vui lòng cập nhật phương thức thanh toán và thử lại.", + "workspace.reload.retrying": "Đang thử lại...", + "workspace.reload.retry": "Thử lại", + "workspace.reload.error.paymentFailed": "Thanh toán thất bại.", + + "workspace.payments.title": "Lịch sử thanh toán", + "workspace.payments.subtitle": "Các giao dịch thanh toán gần đây.", + "workspace.payments.table.date": "Ngày", + "workspace.payments.table.paymentId": "Mã thanh toán", + "workspace.payments.table.amount": "Số tiền", + "workspace.payments.table.receipt": "Biên lai", + "workspace.payments.type.credit": "nạp tiền", + "workspace.payments.type.subscription": "gói đăng ký", + "workspace.payments.view": "Xem", + + "workspace.black.loading": "Đang tải...", + "workspace.black.time.day": "ngày", + "workspace.black.time.days": "ngày", + "workspace.black.time.hour": "giờ", + "workspace.black.time.hours": "giờ", + "workspace.black.time.minute": "phút", + "workspace.black.time.minutes": "phút", + "workspace.black.time.fewSeconds": "vài giây", + "workspace.black.subscription.title": "Gói đăng ký", + "workspace.black.subscription.message": "Bạn đang đăng ký OpenCode Black với giá ${{plan}} mỗi tháng.", + "workspace.black.subscription.manage": "Quản lý gói đăng ký", + "workspace.black.subscription.rollingUsage": "Mức sử dụng 5 giờ", + "workspace.black.subscription.weeklyUsage": "Mức sử dụng theo tuần", + "workspace.black.subscription.resetsIn": "Đặt lại sau", + "workspace.black.subscription.useBalance": "Dùng số dư khả dụng sau khi chạm giới hạn sử dụng", + "workspace.black.waitlist.title": "Danh sách chờ", + "workspace.black.waitlist.joined": "Bạn đang trong danh sách chờ gói OpenCode Black ${{plan}} mỗi tháng.", + "workspace.black.waitlist.ready": "Chúng tôi đã sẵn sàng kích hoạt gói OpenCode Black ${{plan}} mỗi tháng cho bạn.", + "workspace.black.waitlist.leave": "Rời danh sách chờ", + "workspace.black.waitlist.leaving": "Đang rời...", + "workspace.black.waitlist.left": "Đã rời", + "workspace.black.waitlist.enroll": "Kích hoạt", + "workspace.black.waitlist.enrolling": "Đang kích hoạt...", + "workspace.black.waitlist.enrolled": "Đã kích hoạt", + "workspace.black.waitlist.enrollNote": + "Khi bạn bấm Kích hoạt, gói đăng ký bắt đầu ngay lập tức và thẻ của bạn sẽ bị trừ tiền.", + + "workspace.lite.loading": "Đang tải...", + "workspace.lite.time.day": "ngày", + "workspace.lite.time.days": "ngày", + "workspace.lite.time.hour": "giờ", + "workspace.lite.time.hours": "giờ", + "workspace.lite.time.minute": "phút", + "workspace.lite.time.minutes": "phút", + "workspace.lite.time.fewSeconds": "vài giây", + "workspace.lite.subscription.message": "Bạn đang đăng ký OpenCode Go.", + "workspace.lite.subscription.manage": "Quản lý gói đăng ký", + "workspace.lite.subscription.rollingUsage": "Mức sử dụng trượt", + "workspace.lite.subscription.weeklyUsage": "Mức sử dụng theo tuần", + "workspace.lite.subscription.monthlyUsage": "Mức sử dụng theo tháng", + "workspace.lite.subscription.resetsIn": "Đặt lại sau", + "workspace.lite.subscription.useBalance": "Dùng số dư khả dụng sau khi chạm giới hạn sử dụng", + "workspace.lite.subscription.selectProvider": + 'Chọn "OpenCode Go" làm provider trong cấu hình opencode của bạn để dùng các model Go.', + "workspace.lite.providers.title": "Provider", + "workspace.lite.providers.description": "Kiểm soát những provider được dùng để định tuyến.", + "workspace.lite.providers.useChina": "Bật các model lưu trữ tại Trung Quốc", + "workspace.lite.black.message": + "Bạn đang đăng ký OpenCode Black hoặc đang trong danh sách chờ. Vui lòng hủy đăng ký trước nếu muốn chuyển sang Go.", + "workspace.lite.other.message": + "Một thành viên khác trong workspace này đã đăng ký OpenCode Go. Mỗi workspace chỉ một thành viên được đăng ký.", + "workspace.lite.promo.description": + "OpenCode Go khởi điểm {{price}}, sau đó $10/tháng, cung cấp truy cập ổn định tới các model lập trình mở phổ biến với giới hạn sử dụng hào phóng.", + "workspace.lite.promo.price": "$5 cho tháng đầu", + "workspace.lite.promo.modelsTitle": "Bao gồm những gì", + "workspace.lite.promo.footer": + "Gói này được thiết kế chủ yếu cho người dùng quốc tế, với model lưu trữ tại Mỹ, EU và Singapore để truy cập toàn cầu ổn định. Giá và giới hạn sử dụng có thể thay đổi khi chúng tôi học hỏi từ mức dùng và phản hồi ban đầu.", + "workspace.lite.promo.subscribe": "Đăng ký Go", + "workspace.lite.promo.subscribing": "Đang chuyển hướng...", + "workspace.lite.promo.otherMethods": "Phương thức thanh toán khác", + "workspace.lite.promo.selectMethod": "Chọn phương thức thanh toán", + + "workspace.referral.copyLink": "Sao chép liên kết", + "workspace.referral.copied": "Đã sao chép", + "workspace.referral.overview.title": "Mời bạn bè", + "workspace.referral.overview.subtitle": "Nhận $5 khi một người bạn đăng ký. Họ cũng nhận được $5.", + "workspace.referral.instructions.share": "Chia sẻ liên kết giới thiệu của bạn", + "workspace.referral.instructions.subscribe": "Bạn của bạn tham gia và đăng ký Go", + "workspace.referral.instructions.claim": "Cả hai bạn đều nhận $5 credit để áp dụng vào hạn mức sử dụng Go", + "workspace.referral.rewards.title": "Thưởng giới thiệu", + "workspace.referral.rewards.description": "Áp dụng credit giới thiệu khả dụng vào mức sử dụng Go của bạn.", + "workspace.referral.rewards.subtitle": "{{applied}} / {{total}} phần thưởng đã áp dụng.", + "workspace.referral.rewards.empty": "Chưa có thưởng giới thiệu nào.", + "workspace.referral.table.reward": "Phần thưởng", + "workspace.referral.table.referral": "Mô tả", + "workspace.referral.table.date": "Ngày", + "workspace.referral.reward.description.inviter": "Đã mời {{email}}", + "workspace.referral.reward.description.invitee": "Được mời bởi {{email}}", + "workspace.referral.reward.action.subscribeUnlock": "Đăng ký để mở khóa", + "workspace.referral.reward.action.view": "Xem phần thưởng", + "workspace.referral.reward.action.applied": "Đã áp dụng phần thưởng", + "workspace.referral.reward.source.pendingInviter": "Đang chờ họ đăng ký", + "workspace.referral.reward.source.pendingInvitee": "Đăng ký để mở khóa phần thưởng", + "workspace.referral.reward.source.available": "Phần thưởng sẵn sàng để áp dụng", + "workspace.referral.reward.source.applied": "Đã áp dụng phần thưởng", + "workspace.referral.reward.status.applied": "Đã áp dụng phần thưởng", + "workspace.referral.reward.status.pendingInviter": "Đăng ký để mở khóa", + "workspace.referral.reward.status.pendingInvitee": "Đăng ký để mở khóa", + "workspace.referral.apply.noGo": "Đăng ký để mở khóa", + "workspace.referral.apply.preview": "Xem phần thưởng", + "workspace.referral.apply.action": "Áp dụng", + "workspace.referral.apply.confirmTitle": "Áp dụng phần thưởng", + "workspace.referral.apply.confirmBody": "Áp dụng {{amount}} để giảm mức sử dụng hiện tại của workspace này.", + "workspace.referral.apply.confirmAction": "Áp dụng", + + "download.title": "OpenCode | Tải xuống", + "download.meta.description": "Tải OpenCode cho macOS, Windows và Linux", + "download.hero.title": "Tải OpenCode Desktop", + "download.hero.subtitle": "Bản Beta có mặt cho macOS, Windows và Linux", + "download.hero.button": "Tải xuống cho {{os}}", + "download.section.terminal": "OpenCode Terminal", + "download.section.desktop": "OpenCode Desktop", + "download.section.extensions": "Tiện ích mở rộng OpenCode", + "download.section.integrations": "Tích hợp OpenCode", + "download.action.download": "Tải xuống", + "download.action.install": "Cài đặt", + + "download.platform.macosAppleSilicon": "macOS (Apple Silicon)", + "download.platform.macosIntel": "macOS (Intel)", + "download.platform.windowsX64": "Windows (x64)", + "download.platform.linuxDeb": "Linux (.deb)", + "download.platform.linuxRpm": "Linux (.rpm)", + + "download.faq.a3.beforeLocal": + "Không hẳn, nhưng nhiều khả năng là có. Bạn sẽ cần gói đăng ký AI nếu muốn kết nối OpenCode với provider trả phí, mặc dù bạn có thể làm việc với", + "download.faq.a3.localLink": "model local", + "download.faq.a3.afterLocal.beforeZen": "miễn phí. Dù chúng tôi khuyến khích dùng", + "download.faq.a3.afterZen": ", OpenCode hoạt động với mọi provider phổ biến như OpenAI, Anthropic, xAI, v.v.", + + "download.faq.a5.p1": "OpenCode miễn phí 100%.", + "download.faq.a5.p2.beforeZen": + "Chi phí phát sinh (nếu có) đến từ gói đăng ký của bạn với provider model. Dù OpenCode hoạt động với mọi provider model, chúng tôi khuyên dùng", + "download.faq.a5.p2.afterZen": ".", + + "download.faq.a6.p1": "Dữ liệu và thông tin của bạn chỉ được lưu khi bạn tạo liên kết chia sẻ trong OpenCode.", + "download.faq.a6.p2.beforeShare": "Tìm hiểu thêm về", + "download.faq.a6.shareLink": "trang chia sẻ", + + "enterprise.title": "OpenCode | Giải pháp doanh nghiệp cho tổ chức của bạn", + "enterprise.meta.description": "Liên hệ OpenCode để nhận giải pháp doanh nghiệp", + "enterprise.hero.title": "Code của bạn là của bạn", + "enterprise.hero.body1": + "OpenCode hoạt động an toàn bên trong tổ chức của bạn: không lưu dữ liệu hay ngữ cảnh, không ràng buộc giấy phép hay tuyên bố sở hữu. Hãy bắt đầu dùng thử với đội của bạn, rồi triển khai toàn tổ chức bằng cách tích hợp với SSO và AI gateway nội bộ.", + "enterprise.hero.body2": "Hãy cho chúng tôi biết có thể giúp gì cho bạn.", + "enterprise.form.name.label": "Họ và tên", + "enterprise.form.name.placeholder": "Jeff Bezos", + "enterprise.form.role.label": "Vai trò", + "enterprise.form.role.placeholder": "Executive Chairman", + "enterprise.form.company.label": "Công ty", + "enterprise.form.company.placeholder": "Acme Inc", + "enterprise.form.email.label": "Email công ty", + "enterprise.form.email.placeholder": "jeff@amazon.com", + "enterprise.form.phone.label": "Số điện thoại", + "enterprise.form.phone.placeholder": "+1 234 567 8900", + "enterprise.form.message.label": "Bạn đang cần giải quyết vấn đề gì?", + "enterprise.form.message.placeholder": "Chúng tôi cần hỗ trợ về...", + "enterprise.form.send": "Gửi", + "enterprise.form.sending": "Đang gửi...", + "enterprise.form.success": "Đã gửi tin nhắn, chúng tôi sẽ sớm liên hệ.", + "enterprise.form.success.submitted": "Gửi biểu mẫu thành công.", + "enterprise.form.error.allFieldsRequired": "Vui lòng điền đầy đủ các trường.", + "enterprise.form.error.invalidEmailFormat": "Định dạng email không hợp lệ.", + "enterprise.form.error.internalServer": "Lỗi máy chủ nội bộ.", + "enterprise.faq.title": "FAQ", + "enterprise.faq.q1": "OpenCode Enterprise là gì?", + "enterprise.faq.a1": + "OpenCode Enterprise dành cho những tổ chức muốn bảo đảm code và dữ liệu của họ không bao giờ rời khỏi hạ tầng của mình. Điều này thực hiện được nhờ cấu hình tập trung tích hợp với SSO và AI gateway nội bộ của bạn.", + "enterprise.faq.q2": "Bắt đầu với OpenCode Enterprise thế nào?", + "enterprise.faq.a2": + "Đơn giản là bắt đầu dùng thử nội bộ với đội của bạn. Mặc định OpenCode không lưu code hay dữ liệu ngữ cảnh, nên rất dễ bắt đầu. Sau đó liên hệ với chúng tôi để trao đổi về giá và phương án triển khai.", + "enterprise.faq.q3": "Giá cho doanh nghiệp tính thế nào?", + "enterprise.faq.a3": + "Chúng tôi tính giá doanh nghiệp theo mỗi người dùng. Nếu bạn có LLM gateway riêng, chúng tôi không thu phí token sử dụng. Để biết thêm chi tiết, hãy liên hệ để nhận báo giá theo nhu cầu của tổ chức bạn.", + "enterprise.faq.q4": "Dữ liệu của tôi có an toàn với OpenCode Enterprise không?", + "enterprise.faq.a4": + "Có. OpenCode không lưu code hay dữ liệu ngữ cảnh của bạn. Mọi xử lý diễn ra cục bộ hoặc qua lệnh gọi API trực tiếp tới provider AI của bạn. Với cấu hình tập trung và tích hợp SSO, dữ liệu của bạn luôn an toàn trong hạ tầng của tổ chức.", + "enterprise.faq.q5": "Tài liệu bảo mật và tuân thủ của các bạn ở đâu?", + "enterprise.faq.a5.before": + "Trust Center của chúng tôi có đầy đủ: báo cáo SOC 2 Type 2, chính sách bảo mật, danh sách bên xử lý phụ và giải đáp các câu hỏi bảo mật thường gặp. Truy cập", + "enterprise.faq.a5.after": "để xem hoặc yêu cầu tài liệu theo NDA.", + + "brand.title": "OpenCode | Thương hiệu", + "brand.meta.description": "Hướng dẫn thương hiệu OpenCode", + "brand.heading": "Hướng dẫn thương hiệu", + "brand.subtitle": "Tài nguyên và tài sản giúp bạn làm việc với thương hiệu OpenCode.", + "brand.downloadAll": "Tải tất cả tài sản", + + "changelog.title": "OpenCode | Nhật ký thay đổi", + "changelog.meta.description": "Ghi chú phát hành và nhật ký thay đổi của OpenCode", + "changelog.hero.title": "Nhật ký thay đổi", + "changelog.hero.subtitle": "Các cập nhật và cải tiến mới của OpenCode", + "changelog.empty": "Không tìm thấy mục nào trong nhật ký thay đổi.", + "changelog.viewJson": "Xem JSON", + + "bench.list.title": "Benchmark", + "bench.list.heading": "Benchmark", + "bench.list.table.agent": "Agent", + "bench.list.table.model": "Model", + "bench.list.table.score": "Điểm", + "bench.submission.error.allFieldsRequired": "Vui lòng điền đầy đủ các trường.", + + "bench.detail.title": "Benchmark - {{task}}", + "bench.detail.notFound": "Không tìm thấy tác vụ", + "bench.detail.na": "N/A", + "bench.detail.labels.agent": "Agent", + "bench.detail.labels.model": "Model", + "bench.detail.labels.task": "Tác vụ", + "bench.detail.labels.repo": "Repo", + "bench.detail.labels.from": "Từ", + "bench.detail.labels.to": "Đến", + "bench.detail.labels.prompt": "Prompt", + "bench.detail.labels.commit": "Commit", + "bench.detail.labels.averageDuration": "Thời lượng trung bình", + "bench.detail.labels.averageScore": "Điểm trung bình", + "bench.detail.labels.averageCost": "Chi phí trung bình", + "bench.detail.labels.summary": "Tóm tắt", + "bench.detail.labels.runs": "Số lần chạy", + "bench.detail.labels.score": "Điểm", + "bench.detail.labels.base": "Điểm gốc", + "bench.detail.labels.penalty": "Điểm trừ", + "bench.detail.labels.weight": "trọng số", + "bench.detail.table.run": "Lần chạy", + "bench.detail.table.score": "Điểm (Gốc - Trừ)", + "bench.detail.table.cost": "Chi phí", + "bench.detail.table.duration": "Thời lượng", + "bench.detail.run.title": "Lần chạy {{n}}", + "bench.detail.rawJson": "JSON thô", +} as const satisfies Dict diff --git a/packages/console/app/src/lib/language.ts b/packages/console/app/src/lib/language.ts index d2a2f89319..77c457fa21 100644 --- a/packages/console/app/src/lib/language.ts +++ b/packages/console/app/src/lib/language.ts @@ -1,4 +1,4 @@ -export const LOCALES = ["en", "es"] as const +export const LOCALES = ["en", "es", "vi"] as const export type Locale = (typeof LOCALES)[number] @@ -13,23 +13,27 @@ function fix(pathname: string) { const LABEL = { en: "English", es: "Español", + vi: "Tiếng Việt", } satisfies Record const TAG = { en: "en", es: "es", + vi: "vi", } satisfies Record const DOCS = { en: "root", es: "es", + vi: "vi", } satisfies Record -const DOCS_SEGMENT = new Set(["es"]) +const DOCS_SEGMENT = new Set(["es", "vi"]) const DOCS_LOCALE = { en: "en", es: "es", + vi: "vi", root: "en", } as const satisfies Record @@ -130,6 +134,7 @@ function match(input: string): Locale | null { if (!value) return null if (value.startsWith("es")) return "es" + if (value.startsWith("vi")) return "vi" if (value.startsWith("en")) return "en" return null } diff --git a/packages/desktop/src/renderer/i18n/index.ts b/packages/desktop/src/renderer/i18n/index.ts index 2b1b05a3f9..e69dedbd19 100644 --- a/packages/desktop/src/renderer/i18n/index.ts +++ b/packages/desktop/src/renderer/i18n/index.ts @@ -2,16 +2,18 @@ import * as i18n from "@solid-primitives/i18n" import { dict as desktopEn } from "./en" import { dict as desktopEs } from "./es" +import { dict as desktopVi } from "./vi" import { dict as appEn } from "../../../../app/src/i18n/en" import { dict as appEs } from "../../../../app/src/i18n/es" +import { dict as appVi } from "../../../../app/src/i18n/vi" -export type Locale = "en" | "es" +export type Locale = "en" | "es" | "vi" type RawDictionary = typeof appEn & typeof desktopEn type Dictionary = i18n.Flatten -const LOCALES: readonly Locale[] = ["en", "es"] +const LOCALES: readonly Locale[] = ["en", "es", "vi"] function detectLocale(): Locale { if (typeof navigator !== "object") return "en" @@ -21,6 +23,7 @@ function detectLocale(): Locale { if (!language) continue if (language.toLowerCase().startsWith("en")) return "en" if (language.toLowerCase().startsWith("es")) return "es" + if (language.toLowerCase().startsWith("vi")) return "vi" } return "en" @@ -62,6 +65,7 @@ const base = i18n.flatten({ ...appEn, ...desktopEn }) function build(locale: Locale): Dictionary { if (locale === "en") return base + if (locale === "vi") return { ...base, ...i18n.flatten(appVi), ...i18n.flatten(desktopVi) } return { ...base, ...i18n.flatten(appEs), ...i18n.flatten(desktopEs) } } diff --git a/packages/desktop/src/renderer/i18n/vi.ts b/packages/desktop/src/renderer/i18n/vi.ts new file mode 100644 index 0000000000..d4da43795f --- /dev/null +++ b/packages/desktop/src/renderer/i18n/vi.ts @@ -0,0 +1,28 @@ +export const dict = { + "desktop.menu.checkForUpdates": "Kiểm tra cập nhật...", + "desktop.menu.installCli": "Cài đặt CLI...", + "desktop.menu.reloadWebview": "Tải lại webview", + "desktop.menu.restart": "Khởi động lại", + + "desktop.dialog.chooseFolder": "Chọn một thư mục", + "desktop.dialog.chooseFile": "Chọn một tệp", + "desktop.dialog.saveFile": "Lưu tệp", + + "desktop.updater.checkFailed.title": "Kiểm tra cập nhật thất bại", + "desktop.updater.checkFailed.message": "Không thể kiểm tra cập nhật", + "desktop.updater.none.title": "Không có bản cập nhật", + "desktop.updater.none.message": "Bạn đang dùng phiên bản mới nhất của CoDev", + "desktop.updater.downloadFailed.title": "Cập nhật thất bại", + "desktop.updater.downloadFailed.message": "Không thể tải xuống bản cập nhật", + "desktop.updater.downloaded.title": "Đã tải xuống bản cập nhật", + "desktop.updater.downloaded.prompt": + "Phiên bản {{version}} của CoDev đã được tải xuống, bạn có muốn cài đặt và khởi động lại không?", + "desktop.updater.installFailed.title": "Cập nhật thất bại", + "desktop.updater.installFailed.message": "Không thể cài đặt bản cập nhật", + + "desktop.cli.installed.title": "Đã cài đặt CLI", + "desktop.cli.installed.message": + "CLI đã được cài vào {{path}}\n\nKhởi động lại terminal của bạn để dùng lệnh 'codev'.", + "desktop.cli.failed.title": "Cài đặt thất bại", + "desktop.cli.failed.message": "Không thể cài đặt CLI: {{error}}", +} diff --git a/packages/enterprise/src/app.tsx b/packages/enterprise/src/app.tsx index 8fbdc62cf9..7e76cf47f8 100644 --- a/packages/enterprise/src/app.tsx +++ b/packages/enterprise/src/app.tsx @@ -7,6 +7,7 @@ import { DialogProvider } from "@opencode-ai/ui/context/dialog" import { I18nProvider, type UiI18nParams } from "@opencode-ai/ui/context" import { dict as uiEn } from "@opencode-ai/ui/i18n/en" import { dict as uiEs } from "@opencode-ai/ui/i18n/es" +import { dict as uiVi } from "@opencode-ai/ui/i18n/vi" import { createEffect, createMemo, Suspense, type ParentProps } from "solid-js" import { getRequestEvent } from "solid-js/web" import "./app.css" @@ -27,6 +28,7 @@ function detectLocaleFromHeader(header: string | null | undefined) { const value = item.trim().split(";")[0]?.toLowerCase() if (!value) continue if (value.startsWith("es")) return "es" as const + if (value.startsWith("vi")) return "vi" as const if (value.startsWith("en")) return "en" as const } } @@ -40,6 +42,7 @@ function detectLocale() { if (typeof document === "object") { const value = document.documentElement.lang?.toLowerCase() ?? "" if (value.startsWith("es")) return "es" as const + if (value.startsWith("vi")) return "vi" as const if (value.startsWith("en")) return "en" as const } @@ -48,6 +51,7 @@ function detectLocale() { for (const language of languages) { if (!language) continue if (language.toLowerCase().startsWith("es")) return "es" as const + if (language.toLowerCase().startsWith("vi")) return "vi" as const } } @@ -57,8 +61,9 @@ function detectLocale() { function UiI18nBridge(props: ParentProps) { const locale = createMemo(() => detectLocale()) const es = uiEs as Partial> + const vi = uiVi as Partial> const t = (key: keyof typeof uiEn, params?: UiI18nParams) => { - const value = locale() === "es" ? (es[key] ?? uiEn[key]) : uiEn[key] + const value = locale() === "es" ? (es[key] ?? uiEn[key]) : locale() === "vi" ? (vi[key] ?? uiEn[key]) : uiEn[key] const text = value ?? String(key) return resolveTemplate(text, params) } diff --git a/packages/stats/app/src/i18n.ts b/packages/stats/app/src/i18n.ts index b0b123cecc..c3485d9b94 100644 --- a/packages/stats/app/src/i18n.ts +++ b/packages/stats/app/src/i18n.ts @@ -1,5 +1,6 @@ import { type Locale } from "./lib/language" import { dict as es } from "./i18n/es" +import { dict as vi } from "./i18n/vi" const en = { "app.title": "AI Model Usage Rankings | OpenCode Data", @@ -242,6 +243,7 @@ export type Dict = Record const dictionaries = { en, es, + vi, } satisfies Record export function dict(locale: Locale) { diff --git a/packages/stats/app/src/i18n/vi.ts b/packages/stats/app/src/i18n/vi.ts new file mode 100644 index 0000000000..61d9bf93e6 --- /dev/null +++ b/packages/stats/app/src/i18n/vi.ts @@ -0,0 +1,235 @@ +export const dict = { + "app.title": "Bảng xếp hạng mức dùng model AI | OpenCode Data", + "app.description": + "Khám phá mức dùng OpenCode Go theo từng model AI, gồm khối lượng token, xếp hạng, thị phần, giá token, chi phí mỗi session, tỷ lệ cache và phân bố theo khu vực.", + "app.unfurlAlt": "Logo chữ OpenCode Data trên nền tối có hoa văn", + "nav.dataHome": "Trang dữ liệu", + "nav.topModels": "Model hàng đầu", + "nav.leaderboard": "Bảng xếp hạng", + "nav.uniqueUsers": "Người dùng riêng biệt", + "nav.sessionCost": "Chi phí mỗi session", + "nav.tokenCost": "Chi phí token", + "nav.cacheRatio": "Tỷ lệ cache", + "nav.marketShare": "Thị phần", + "nav.geoBreakdown": "Phân bố khu vực", + "nav.overview": "Tổng quan", + "nav.usage": "Mức dùng", + "nav.users": "Người dùng", + "nav.efficiency": "Hiệu quả", + "nav.peers": "Model tương đương", + "nav.models": "Model", + "header.brandLabel": "Trang dữ liệu", + "header.sectionNavLabel": "Các mục dữ liệu", + "header.githubAria": "Gắn sao OpenCode trên GitHub", + "header.github": "GitHub", + "header.tryOpenCode": "Dùng thử OpenCode", + "header.openNav": "Mở điều hướng", + "header.closeNav": "Đóng điều hướng", + "theme.groupLabel": "Giao diện", + "theme.dark": "Tối", + "theme.light": "Sáng", + "theme.system": "Hệ thống", + "footer.modelData": "Dữ liệu model", + "footer.legal": "Pháp lý", + "footer.connect": "Kết nối", + "footer.newsletter": "Bản tin", + "footer.newsletterBody": "Là người đầu tiên biết về các bản phát hành mới.", + "footer.language": "Ngôn ngữ", + "footer.subscribe": "Đăng ký", + "footer.terms": "Điều khoản dịch vụ", + "footer.privacy": "Chính sách quyền riêng tư", + "footer.contact": "Liên hệ", + "footer.community": "Cộng đồng", + "footer.youtube": "YouTube", + "footer.homeAria": "Trang chủ OpenCode", + "footer.status": "Tất cả hệ thống hoạt động bình thường", + "footer.copyright": "© 2026 Anomaly Innovations Inc.", + "bridge.learnMore": "TÌM HIỂU THÊM", + "modal.closeNewsletter": "Đóng đăng ký bản tin", + "modal.title": "Bản tin OpenCode", + "modal.body": "Là người đầu tiên biết\nvề các bản phát hành mới.", + "modal.email": "Địa chỉ email", + "modal.subscribe": "Đăng ký", + "modal.subscribing": "Đang đăng ký...", + "modal.success": "Bạn đã đăng ký.", + "modal.error": "Đăng ký thất bại", + "product.allUsers": "Tất cả người dùng", + "product.zen": "Zen", + "product.go": "Go", + "range.1D": "1 ngày", + "range.1W": "1 tuần", + "range.2W": "2 tuần", + "range.1M": "1 tháng", + "range.2M": "2 tháng", + "chart.daily": "Theo ngày", + "chart.input": "Đầu vào", + "chart.output": "Đầu ra", + "chart.cached": "Có cache", + "chart.uncached": "Không cache", + "chart.ratio": "Tỷ lệ", + "chart.model": "Model", + "chart.cacheRatio": "Tỷ lệ cache", + "chart.costSession": "Chi phí/Session", + "chart.tokensSession": "Token/Session", + "chart.costPerSession": "CHI PHÍ / SESSION", + "chart.tokensPerSession": "TOKEN / SESSION", + "chart.live": "Trực tiếp", + "chart.percent": "phần trăm", + "chart.leaderboardAria": "Bảng xếp hạng token theo model", + "chart.scrollableLeaderboardAria": "Bảng xếp hạng token theo model có thể cuộn", + "chart.byAuthor": "của {{author}}", + "home.updated": "Cập nhật", + "home.noRows": "Chưa có dữ liệu", + "home.justNow": "vừa xong", + "home.heroCopy": + "Xem model nào đang thắng về mức dùng thực tế, cơ cấu đang dịch chuyển ra sao và điều đó có ý nghĩa gì với chi phí.", + "home.loadingTitle": "Đang tải dữ liệu", + "home.loadingDescription": "Đang đọc dữ liệu tổng hợp theo model.", + "home.usageTitle": "Mức dùng", + "home.topModelsDescription": "Mức dùng các model trên OpenCode Go.", + "home.noUsageTitle": "Không có dữ liệu mức dùng", + "home.noUsageDescription": "Không có model nào khớp với sản phẩm và khoảng thời gian này.", + "home.noLeaderboardTitle": "Không có dữ liệu xếp hạng", + "home.noLeaderboardDescription": "Không có model nào khớp với sản phẩm và khoảng thời gian này.", + "home.productFilter": "Bộ lọc sản phẩm", + "home.dateRange": "Khoảng thời gian", + "home.stackedUsageChart": "Biểu đồ chồng mức dùng các model hàng đầu", + "home.total": "tổng", + "home.modelUsers": "người dùng model", + "home.new": "Mới", + "home.uniqueUsersTitle": "Người dùng riêng biệt", + "home.uniqueUsersDescription": "Người dùng OpenCode Go riêng biệt mỗi ngày theo model.", + "home.noUserDataTitle": "Không có dữ liệu người dùng", + "home.noUserDataDescription": "Không có dữ liệu người dùng nào khớp với khoảng thời gian này.", + "home.uniqueUsersChart": "Biểu đồ chồng người dùng riêng biệt theo model", + "home.marketShareTitle": "Thị phần", + "home.marketShareDescription": "So sánh tỷ trọng token theo nhà phát triển model.", + "home.noMarketTitle": "Không có dữ liệu thị trường", + "home.noMarketDescription": "Không có model nào khớp với khoảng thời gian này.", + "home.marketChart": "Thị phần theo nhà phát triển model", + "home.noData": "Không có dữ liệu", + "home.geoTitle": "Phân bố khu vực", + "home.geoDescription": "Token được dùng theo quốc gia.", + "home.noGeoTitle": "Không có dữ liệu khu vực", + "home.noGeoDescription": "Không có dữ liệu khu vực nào khớp với khoảng thời gian này.", + "home.worldMap": "Bản đồ thế giới về mức dùng token theo quốc gia", + "home.geoMapTitle": "Bản đồ phân bố khu vực", + "home.unknown": "Không rõ", + "home.tokenCostTitle": "Chi phí token", + "home.tokenCostDescription": "Giá mỗi 1M token.", + "home.noTokenCostTitle": "Không có dữ liệu chi phí token", + "home.noTokenCostDescription": "Không có model kèm chi phí nào khớp với sản phẩm này.", + "home.cacheRatioTitle": "Tỷ lệ cache", + "home.cacheRatioDescription": "Tỷ trọng token đầu vào được phục vụ từ cache.", + "home.noCacheTitle": "Không có dữ liệu tỷ lệ cache", + "home.noCacheDescription": "Không có model kèm token đầu vào nào khớp với sản phẩm này.", + "home.sessionCostTitle": "Chi phí mỗi session", + "home.sessionCostDescription": "Chi phí trung bình mỗi session.", + "home.noSessionCostTitle": "Không có dữ liệu chi phí session", + "home.noSessionCostDescription": "Không có model kèm session nào khớp với sản phẩm này.", + "home.costSession": "Chi phí/Session", + "lab.title": "Mức dùng & xếp hạng model AI của {{lab}} | OpenCode Data", + "lab.description": + "So sánh các model {{lab}} được dùng trong OpenCode Go, gồm mức dùng token, xếp hạng model, cửa sổ ngữ cảnh, ngày phát hành, chi phí và dữ liệu riêng của từng model.", + "lab.loadingTitle": "Phòng model", + "lab.loadingDescription": "Đang đọc danh sách model khả dụng và mức dùng OpenCode gần đây.", + "lab.notFound": "Không có model nào khớp với nhà phát triển này.", + "lab.heroPrefix": "Khám phá {{count}} model {{lab}} được dùng trong OpenCode", + "lab.heroIncluding": "bao gồm {{models}}", + "lab.heroSuffix": "So sánh mức dùng token gần đây, cửa sổ ngữ cảnh, ngày phát hành và dữ liệu model.", + "lab.tokensProcessed": "Token đã xử lý", + "lab.pending": "Đang chờ", + "lab.usageAfterActivity": "Mức dùng sẽ hiển thị sau khi model có hoạt động", + "lab.latestRelease": "Bản phát hành mới nhất {{date}}", + "lab.shareOfUsage": "{{share}} mức dùng OpenCode gần đây", + "lab.usageTitle": "Mức dùng token của {{lab}}", + "lab.usageDescription": "Khối lượng token theo ngày.", + "lab.noUsageTitle": "Chưa có mức dùng", + "lab.noUsageDescription": "Mức dùng token gần đây sẽ hiển thị ở đây khi nhà phát triển này có hoạt động.", + "lab.dailyTokenChart": "Biểu đồ mức dùng token theo ngày của {{lab}}", + "lab.dailyTokens": "Token theo ngày", + "lab.tokens": "token", + "lab.modelsTitle": "Các model của {{lab}}", + "lab.modelsDescription": "Thông tin danh mục kèm mức dùng gần đây.", + "lab.recentUsageAndLimits": "Mức dùng gần đây và giới hạn.", + "lab.usage": "Mức dùng", + "lab.share": "Tỷ trọng", + "model.title": "Mức dùng, chi phí & xếp hạng {{model}} | OpenCode Data", + "model.description": + "Xem dữ liệu mức dùng {{model}} trên OpenCode Go, gồm khối lượng token, xếp hạng tuần, cơ cấu token, chi phí, tỷ lệ cache, session, phân bố khu vực và các model tương đương.", + "model.loadingTitle": "Dữ liệu model", + "model.loadingDescription": "Đang đọc dữ liệu tổng hợp theo model.", + "model.loadingProfile": "Đang đọc hồ sơ model.", + "model.noDataTitle": "Không có dữ liệu model", + "model.noDataDescription": "Hãy thử mở một model từ bảng xếp hạng.", + "model.noMatched": "Không có thông tin hay dữ liệu mức dùng nào khớp với {{id}}.", + "model.fallback": "Model", + "model.catalogFallback": + "Thông tin model từ chỉ mục model dùng chung. Mức dùng OpenCode Go sẽ hiển thị khi model này có hoạt động.", + "model.unranked": "Chưa xếp hạng trong mức dùng OpenCode Go tuần trước", + "model.ranked": "Xếp hạng #{{rank}} trong mức dùng OpenCode Go tuần trước", + "model.observedVolume": "với {{share}} khối lượng 2M quan sát được.", + "model.weights": "Trọng số model: {{label}}", + "model.rank": "Hạng 7 ngày", + "model.profile": "Hồ sơ model", + "model.listed": "Đã niêm yết", + "model.noCurrentUsage": "Không có mức dùng OpenCode Go trong khoảng dữ liệu hiện tại.", + "model.facts": "Thông tin model", + "model.context": "Ngữ cảnh", + "model.output": "Đầu ra", + "model.knowledge": "Kiến thức", + "model.release": "Phát hành", + "model.inputs": "Đầu vào", + "model.overviewDescription": "Token gần đây, người dùng riêng biệt và vị thế thị trường.", + "model.noSummaryTitle": "Không có tóm tắt mức dùng", + "model.noSummaryDescription": "Model này chưa có dữ liệu mức dùng OpenCode Go.", + "model.tokens": "Token", + "model.uniqueUsers": "Người dùng riêng biệt", + "model.lastTwoMonths": "hai tháng gần nhất", + "model.sessions": "Session", + "model.completedSessions": "session đã hoàn thành", + "model.tokenShare": "Tỷ trọng token", + "model.totalModels": "{{count}} model", + "model.momentum": "Đà tăng trưởng", + "model.vsPreviousWindow": "so với khoảng trước", + "model.usageDescription": "Khối lượng token theo ngày.", + "model.noUsageTitle": "Không có mức dùng", + "model.noUsageDescription": "Không có mức dùng nào trong khoảng hiện tại.", + "model.dailyTokenChart": "Biểu đồ mức dùng token theo ngày", + "model.usersDescription": "Người dùng riêng biệt theo ngày.", + "model.noUsersTitle": "Không có dữ liệu người dùng", + "model.noUsersDescription": "Không có dữ liệu người dùng nào trong khoảng hiện tại.", + "model.dailyUserChart": "Biểu đồ người dùng riêng biệt theo ngày", + "model.efficiencyDescription": "Chi phí, hành vi cache và hình dạng session trung bình.", + "model.noEfficiencyTitle": "Không có dữ liệu hiệu quả", + "model.noEfficiencyDescription": "Dữ liệu hiệu quả sẽ hiển thị sau khi có mức dùng.", + "model.totalSpendLabel": "Tổng chi tiêu", + "model.cost": "Chi phí", + "model.totalSpend": "tổng chi tiêu", + "model.costPerMillion": "Chi phí / 1M", + "model.costInput": "Chi phí đầu vào", + "model.costOutput": "Chi phí đầu ra", + "model.inputOutput": "đầu vào / đầu ra", + "model.observedTokens": "quan sát trên tất cả token", + "model.costSession": "Chi phí / Session", + "model.averageCostSession": "Chi phí trung bình / Session", + "model.average": "trung bình", + "model.tokensSession": "Token / Session", + "model.averageTokensSession": "Token trung bình / session", + "model.cacheRatio": "Tỷ lệ cache", + "model.inputTokens": "token đầu vào", + "model.geoDescription": "Token model OpenCode Go được dùng theo quốc gia.", + "model.noGeoTitle": "Không có dữ liệu khu vực", + "model.noGeoDescription": "Không có dữ liệu khu vực OpenCode Go nào khớp với model này.", + "model.worldMap": "Bản đồ thế giới về mức dùng token của model theo quốc gia", + "model.peersDescription": "Các model gần kề theo khối lượng token OpenCode Go gần đây.", + "model.noPeersTitle": "Không có model tương đương", + "model.noPeersDescription": "Xếp hạng model tương đương sẽ hiển thị sau khi có mức dùng.", + "model.noUsageLastWeek": "Không có mức dùng tuần trước", + "model.newThisWeek": "Mới trong tuần này", + "model.sameAsPreviousWeek": "Bằng tuần trước", + "model.vsPreviousWeek": "{{change}} so với tuần trước", + "model.pdf": "PDF", + "format.users": "người dùng", + "format.tokens": "token", +} as const diff --git a/packages/ui/src/i18n/vi.ts b/packages/ui/src/i18n/vi.ts new file mode 100644 index 0000000000..4cbc6e435f --- /dev/null +++ b/packages/ui/src/i18n/vi.ts @@ -0,0 +1,197 @@ +export const dict = { + "ui.sessionReview.title": "Thay đổi của session", + "ui.sessionReview.title.git": "Thay đổi Git", + "ui.sessionReview.title.branch": "Thay đổi của branch", + "ui.sessionReview.title.lastTurn": "Thay đổi ở lượt cuối", + "ui.sessionReview.diffStyle.unified": "Gộp", + "ui.sessionReview.diffStyle.split": "Tách đôi", + "ui.sessionReview.expandAll": "Mở rộng tất cả", + "ui.sessionReview.collapseAll": "Thu gọn tất cả", + "ui.sessionReview.change.added": "Đã thêm", + "ui.sessionReview.change.removed": "Đã xóa", + "ui.sessionReview.change.modified": "Đã sửa", + "ui.sessionReview.image.loading": "Đang tải...", + "ui.sessionReview.image.placeholder": "Hình ảnh", + "ui.sessionReview.largeDiff.title": "Diff quá lớn để hiển thị", + "ui.sessionReview.largeDiff.meta": "Giới hạn: {{limit}} dòng thay đổi. Hiện tại: {{current}} dòng thay đổi.", + "ui.sessionReview.largeDiff.renderAnyway": "Vẫn hiển thị", + "ui.sessionReviewV2.expandMode": "Mở rộng hoặc thu gọn diff", + "ui.sessionReviewV2.filterFiles": "Lọc tệp", + "ui.sessionReviewV2.toggleSidebar": "Bật/tắt cây tệp", + "ui.sessionReviewV2.showAllLines": "Hiện tất cả các dòng", + "ui.sessionReviewV2.hideNonDiffLines": "Ẩn các dòng ngoài diff", + "ui.sessionReviewV2.unifiedDiff": "Diff gộp", + "ui.sessionReviewV2.splitDiff": "Diff tách đôi", + "ui.sessionReviewV2.previousFile": "Tệp trước", + "ui.sessionReviewV2.nextFile": "Tệp sau", + "ui.sessionReviewV2.diffView": "Chế độ xem diff", + "ui.sessionReviewV2.empty.noGit.title": "Chưa theo dõi thay đổi nào", + "ui.sessionReviewV2.empty.noGit.description": "Theo dõi, xem lại và hoàn tác thay đổi trong dự án này", + "ui.sessionReviewV2.empty.noGit.action": "Tạo Git repository", + "ui.sessionReviewV2.empty.noGit.actionLoading": "Đang tạo Git repository...", + "ui.sessionReviewV2.empty.changes.title": "Chưa có tệp nào thay đổi", + "ui.sessionReviewV2.empty.changes.description": "Thay đổi của dự án sẽ hiển thị ở đây", + + "ui.sessionReview.openFile": "Mở tệp", + "ui.sessionReview.selection.line": "dòng {{line}}", + "ui.sessionReview.selection.lines": "dòng {{start}}-{{end}}", + + "ui.fileMedia.kind.image": "hình ảnh", + "ui.fileMedia.kind.audio": "âm thanh", + "ui.fileMedia.state.removed": "Đã xóa tệp {{kind}}.", + "ui.fileMedia.state.loading": "Đang tải {{kind}}...", + "ui.fileMedia.state.error": "Không thể tải {{kind}}.", + "ui.fileMedia.state.unavailable": "Không có bản xem trước {{kind}}.", + "ui.fileMedia.binary.title": "Tệp nhị phân", + "ui.fileMedia.binary.description.path": "{{path}} là tệp nhị phân.", + "ui.fileMedia.binary.description.default": "Nội dung nhị phân", + + "ui.lineComment.label.prefix": "Bình luận về ", + "ui.lineComment.label.suffix": "", + "ui.lineComment.editorLabel.prefix": "Đang bình luận về ", + "ui.lineComment.editorLabel.suffix": "", + "ui.lineComment.placeholder": "Thêm bình luận", + "ui.lineComment.submit": "Bình luận", + "ui.lineComment.cancel": "Hủy", + + "ui.sessionTurn.steps.show": "Hiện các bước", + "ui.sessionTurn.steps.hide": "Ẩn các bước", + "ui.sessionTurn.summary.response": "Phản hồi", + "ui.sessionTurn.diff.showMore": "Hiện thêm thay đổi ({{count}})", + "ui.sessionTurn.diffs.changed.one": "{{count}} tệp thay đổi", + "ui.sessionTurn.diffs.changed.other": "{{count}} tệp thay đổi", + "ui.sessionTurn.diffs.showAll": "Hiện tất cả", + "ui.sessionTurn.diffs.showLess": "Thu gọn", + "ui.sessionTurn.diffs.more": "+{{count}} tệp nữa", + + "ui.sessionTurn.retry.retrying": "đang thử lại", + "ui.sessionTurn.retry.inSeconds": "sau {{seconds}}s", + "ui.sessionTurn.retry.attempt": "lần thử #{{attempt}}", + "ui.sessionTurn.retry.attemptLine": "{{line}} - lần thử #{{attempt}}", + "ui.sessionTurn.retry.geminiHot": "gemini đang quá tải ngay lúc này", + "ui.sessionTurn.error.freeUsageExceeded": "Đã vượt hạn mức miễn phí", + "ui.sessionTurn.error.addCredits": "Nạp thêm credit", + + "dialog.usageExceeded.freeTier.title": "Đã chạm giới hạn miễn phí", + "dialog.usageExceeded.freeTier.description": + "Đăng ký OpenCode Go để truy cập ổn định các model mã nguồn mở tốt nhất, chỉ từ $5/tháng.", + "dialog.usageExceeded.freeTier.actionLabel": "Đăng ký", + "dialog.usageExceeded.accountRateLimit.title": "Đã chạm giới hạn Go", + "dialog.usageExceeded.accountRateLimit.description": + "Đã chạm giới hạn sử dụng. Để tiếp tục dùng model này ngay, hãy bật sử dụng từ số dư khả dụng của bạn", + "dialog.usageExceeded.accountRateLimit.actionLabel": "Mở cài đặt", + + "ui.sessionTurn.status.delegating": "Đang giao việc", + "ui.sessionTurn.status.planning": "Đang lên kế hoạch các bước tiếp theo", + "ui.sessionTurn.status.gatheringContext": "Đang khám phá", + "ui.sessionTurn.status.gatheredContext": "Đã khám phá", + "ui.sessionTurn.status.searchingCodebase": "Đang tìm trong codebase", + "ui.sessionTurn.status.searchingWeb": "Đang tìm trên web", + "ui.sessionTurn.status.makingEdits": "Đang chỉnh sửa", + "ui.sessionTurn.status.runningCommands": "Đang chạy lệnh", + "ui.sessionTurn.status.thinking": "Đang suy nghĩ", + "ui.sessionTurn.status.thinkingWithTopic": "Đang suy nghĩ - {{topic}}", + "ui.sessionTurn.status.gatheringThoughts": "Đang sắp xếp suy nghĩ", + "ui.sessionTurn.status.consideringNextSteps": "Đang cân nhắc bước tiếp theo", + + "ui.messagePart.diagnostic.error": "Lỗi", + "ui.messagePart.title.edit": "Sửa", + "ui.messagePart.title.write": "Ghi", + "ui.messagePart.option.typeOwnAnswer": "Nhập câu trả lời của riêng bạn", + "ui.messagePart.review.title": "Xem lại câu trả lời của bạn", + "ui.messagePart.questions.dismissed": "Đã bỏ qua các câu hỏi", + "ui.messagePart.compaction": "Đã nén session", + "ui.messagePart.retry.retried": "đã thử lại", + "ui.messagePart.context.read.one": "{{count}} lượt đọc", + "ui.messagePart.context.read.other": "{{count}} lượt đọc", + "ui.messagePart.context.search.one": "{{count}} lượt tìm", + "ui.messagePart.context.search.other": "{{count}} lượt tìm", + "ui.messagePart.context.list.one": "{{count}} lượt liệt kê", + "ui.messagePart.context.list.other": "{{count}} lượt liệt kê", + + "ui.list.loading": "Đang tải", + "ui.list.empty": "Không có kết quả", + "ui.list.clearFilter": "Xóa bộ lọc", + "ui.list.emptyWithFilter.prefix": "Không có kết quả cho", + "ui.list.emptyWithFilter.suffix": "", + + "ui.fileSearch.placeholder": "Tìm", + "ui.fileSearch.previousMatch": "Kết quả trước", + "ui.fileSearch.nextMatch": "Kết quả sau", + "ui.fileSearch.close": "Đóng tìm kiếm", + + "ui.messageNav.newMessage": "Tin nhắn mới", + + "ui.textField.copyToClipboard": "Sao chép vào clipboard", + "ui.textField.copyLink": "Sao chép liên kết", + "ui.textField.copied": "Đã sao chép", + + "ui.imagePreview.alt": "Xem trước hình ảnh", + "ui.scrollView.ariaLabel": "nội dung cuộn được", + + "ui.tool.read": "Đọc", + "ui.tool.loaded": "Đã tải", + "ui.tool.list": "Liệt kê", + "ui.tool.glob": "Glob", + "ui.tool.grep": "Grep", + "ui.tool.task": "Tác vụ", + "ui.tool.webfetch": "Webfetch", + "ui.tool.websearch": "Tìm kiếm web", + "ui.tool.shell": "Shell", + "ui.tool.patch": "Patch", + "ui.tool.todos": "Việc cần làm", + "ui.tool.todos.read": "Đọc việc cần làm", + "ui.tool.questions": "Câu hỏi", + "ui.tool.agent": "Agent {{type}}", + "ui.tool.agent.default": "Agent", + "ui.tool.skill": "Skill", + + "ui.basicTool.called": "Đã gọi `{{tool}}`", + "ui.toolErrorCard.failed": "Thất bại", + "ui.toolErrorCard.copyError": "Sao chép lỗi", + + "ui.common.file.one": "tệp", + "ui.common.file.other": "tệp", + "ui.common.question.one": "câu hỏi", + "ui.common.question.other": "câu hỏi", + + "ui.common.add": "Thêm", + "ui.common.back": "Quay lại", + "ui.common.cancel": "Hủy", + "ui.common.confirm": "Xác nhận", + "ui.common.dismiss": "Bỏ qua", + "ui.common.close": "Đóng", + "ui.common.next": "Tiếp", + "ui.common.submit": "Gửi", + "ui.common.showMore": "Hiện thêm", + + "ui.permission.deny": "Từ chối", + "ui.permission.allowAlways": "Luôn cho phép", + "ui.permission.allowOnce": "Cho phép một lần", + + "ui.message.expand": "Mở rộng tin nhắn", + "ui.message.collapse": "Thu gọn tin nhắn", + "ui.message.copy": "Sao chép", + "ui.message.copyMessage": "Sao chép tin nhắn", + "ui.message.forkMessage": "Tách sang session mới", + "ui.message.revertMessage": "Hoàn tác về tin nhắn này", + "ui.message.copyResponse": "Sao chép phản hồi", + "ui.message.copied": "Đã sao chép", + "ui.message.duration.seconds": "{{count}}s", + "ui.message.duration.minutesSeconds": "{{minutes}}m {{seconds}}s", + "ui.message.interrupted": "Đã ngắt", + "ui.message.queued": "Đang chờ", + "ui.message.attachment.alt": "tệp đính kèm", + + "ui.patch.action.deleted": "Đã xóa", + "ui.patch.action.created": "Đã tạo", + "ui.patch.action.moved": "Đã di chuyển", + "ui.patch.action.patched": "Đã vá", + + "ui.question.subtitle.answered": "{{count}} đã trả lời", + "ui.question.answer.none": "(chưa có câu trả lời)", + "ui.question.review.notAnswered": "(chưa trả lời)", + "ui.question.multiHint": "Chọn tất cả câu trả lời phù hợp", + "ui.question.singleHint": "Chọn một câu trả lời", + "ui.question.custom.placeholder": "Nhập câu trả lời của bạn...", +} diff --git a/script/translate-app.test.ts b/script/translate-app.test.ts index 4eafbc580a..0756289122 100644 --- a/script/translate-app.test.ts +++ b/script/translate-app.test.ts @@ -69,9 +69,9 @@ describe("translate app", () => { }) test("maps product locale codes to their glossaries", () => { - expect(glossaryFile("fr")).toBe(".opencode/glossary/fr.md") - expect(glossaryFile("zh")).toBe(".opencode/glossary/zh-cn.md") - expect(glossaryFile("zht")).toBe(".opencode/glossary/zh-tw.md") + expect(glossaryFile("fr")).toBe(".codev/glossary/fr.md") + expect(glossaryFile("zh")).toBe(".codev/glossary/zh-cn.md") + expect(glossaryFile("zht")).toBe(".codev/glossary/zh-tw.md") }) test("finds key and placeholder drift", () => { diff --git a/script/translate-app.ts b/script/translate-app.ts index 86a397a990..e33b0dfd27 100644 --- a/script/translate-app.ts +++ b/script/translate-app.ts @@ -20,6 +20,7 @@ const locales = [ "uk", "th", "tr", + "vi", "zh", "zht", ] as const @@ -41,6 +42,7 @@ const languages = { uk: "Ukrainian", th: "Thai", tr: "Turkish", + vi: "Vietnamese", zh: "Simplified Chinese", zht: "Traditional Chinese", } as const satisfies Record @@ -92,9 +94,9 @@ export function targetFiles(locale: Locale) { } export function glossaryFile(locale: Locale) { - if (locale === "zh") return ".opencode/glossary/zh-cn.md" - if (locale === "zht") return ".opencode/glossary/zh-tw.md" - return `.opencode/glossary/${locale}.md` + if (locale === "zh") return ".codev/glossary/zh-cn.md" + if (locale === "zht") return ".codev/glossary/zh-tw.md" + return `.codev/glossary/${locale}.md` } export function findDrift(source: Dictionary, target: Dictionary) { From 5e8e1d4cc66b765ada2051555c57ca9fd8beb516 Mon Sep 17 00:00:00 2001 From: minhn4 Date: Sun, 23 Aug 2026 19:35:44 +0700 Subject: [PATCH 2/2] docs: link PR in changelog entry Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 423c46534e..bf16094092 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ file is the human-readable summary of what changed and why. Vietnamese developers expect: UI chrome is translated, while terms like session, prompt, token, commit, branch, and agent stay in English (the glossary lives in `.codev/glossary/vi.md`). + ([#307](https://github.com/quickbeard/codev-code/pull/307)) ### Privacy