+
@@ -37,6 +37,29 @@ export default function QueueCards({ queueStatus, t }) {
{t('accountManager.accountsUnit')}
+
0
+ ? 'bg-destructive/5 border-destructive/30'
+ : 'bg-card border-border'
+ }`}>
+
+
0 ? 'text-destructive' : 'text-muted-foreground'
+ }`}>{t('accountManager.failedPool')}
+
+ 0 ? 'text-destructive' : 'text-foreground'
+ }`}>{queueStatus.failed ?? 0}
+ {t('accountManager.accountsUnit')}
+
+ {queueStatus.failed > 0 && (
+
+ {t('accountManager.failedPoolHint')}
+
+ )}
+
)
}
diff --git a/webui/src/i18n.jsx b/webui/src/i18n.jsx
index 5515b7f59..2f51e24ad 100644
--- a/webui/src/i18n.jsx
+++ b/webui/src/i18n.jsx
@@ -1,9 +1,10 @@
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
import en from './locales/en.json'
import zh from './locales/zh.json'
+import vi from './locales/vi.json'
const STORAGE_KEY = 'ds2api_lang'
-const translations = { en, zh }
+const translations = { en, zh, vi }
const I18nContext = createContext({
lang: 'zh',
@@ -13,7 +14,11 @@ const I18nContext = createContext({
const getBrowserLang = () => {
if (typeof navigator === 'undefined') return 'zh'
- return navigator.language?.toLowerCase().startsWith('zh') ? 'zh' : 'en'
+ const browserLang = navigator.language?.toLowerCase()
+ if (!browserLang) return 'zh'
+ if (browserLang.startsWith('zh')) return 'zh'
+ if (browserLang.startsWith('vi')) return 'vi'
+ return 'en'
}
const getValue = (obj, key) => {
diff --git a/webui/src/locales/en.json b/webui/src/locales/en.json
index c0db122b1..480c82b2b 100644
--- a/webui/src/locales/en.json
+++ b/webui/src/locales/en.json
@@ -2,7 +2,8 @@
"language": {
"label": "Language",
"english": "English",
- "chinese": "中文"
+ "chinese": "中文",
+ "vietnamese": "Vietnamese"
},
"nav": {
"accounts": {
@@ -109,6 +110,8 @@
"available": "Available",
"inUse": "In use",
"totalPool": "Total pool",
+ "failedPool": "Unavailable",
+ "failedPoolHint": "Refresh token to re-verify",
"accountsUnit": "accounts",
"threadsUnit": "threads",
"apiKeysTitle": "API Keys",
diff --git a/webui/src/locales/vi.json b/webui/src/locales/vi.json
new file mode 100644
index 000000000..8cf0c11d9
--- /dev/null
+++ b/webui/src/locales/vi.json
@@ -0,0 +1,491 @@
+{
+ "language": {
+ "label": "Ngôn ngữ",
+ "english": "Tiếng Anh",
+ "chinese": "Tiếng Trung",
+ "vietnamese": "Tiếng Việt"
+ },
+ "nav": {
+ "accounts": {
+ "label": "Quản lý tài khoản",
+ "desc": "Quản lý kho tài khoản DeepSeek"
+ },
+ "proxies": {
+ "label": "Proxy IPs",
+ "desc": "Quản lý các nút proxy cho tài khoản"
+ },
+ "test": {
+ "label": "Kiểm tra API",
+ "desc": "Kiểm tra kết nối và phản hồi API"
+ },
+ "history": {
+ "label": "Phản hồi",
+ "desc": "Xem lịch sử phản hồi từ server"
+ },
+ "import": {
+ "label": "Nhập hàng loạt",
+ "desc": "Nhập cấu hình tài khoản số lượng lớn"
+ },
+ "vercel": {
+ "label": "Đồng bộ Vercel",
+ "desc": "Đồng bộ cấu hình sang Vercel"
+ },
+ "settings": {
+ "label": "Cài đặt",
+ "desc": "Chỉnh sửa cài đặt hệ thống và bảo mật"
+ }
+ },
+ "sidebar": {
+ "onlineAdminConsole": "Bảng điều khiển trực tuyến",
+ "systemStatus": "Trạng thái hệ thống",
+ "statusOnline": "Trực tuyến",
+ "accounts": "Tài khoản",
+ "keys": "Khóa API",
+ "signOut": "Đăng xuất",
+ "version": "Phiên bản",
+ "updateAvailable": "Có bản cập nhật mới: {latest}"
+ },
+ "auth": {
+ "expired": "Phiên đăng nhập đã hết hạn. Vui lòng đăng nhập lại.",
+ "checking": "Đang kiểm tra trạng thái xác thực..."
+ },
+ "errors": {
+ "fetchConfig": "Lỗi khi lấy cấu hình: {error}"
+ },
+ "actions": {
+ "cancel": "Hủy",
+ "add": "Thêm",
+ "delete": "Xóa",
+ "copy": "Sao chép",
+ "generate": "Tạo mới",
+ "test": "Làm mới token",
+ "testing": "Đang làm mới...",
+ "loading": "Đang tải..."
+ },
+ "messages": {
+ "deleted": "Xóa thành công",
+ "deleteFailed": "Xóa thất bại",
+ "failedToAdd": "Thêm thất bại",
+ "networkError": "Lỗi mạng.",
+ "requestFailed": "Yêu cầu thất bại.",
+ "generationStopped": "Đã dừng tạo.",
+ "invalidJson": "Định dạng JSON không hợp lệ.",
+ "importFailed": "Nhập thất bại.",
+ "copyFailed": "Sao chép thất bại."
+ },
+ "landing": {
+ "adminConsole": "Bảng điều khiển Admin",
+ "apiStatus": "Trạng thái API",
+ "features": {
+ "compatibility": {
+ "title": "Tương thích hoàn toàn",
+ "desc": "Hỗ trợ định dạng OpenAI & Claude"
+ },
+ "loadBalancing": {
+ "title": "Cân bằng tải",
+ "desc": "Tự động xoay vòng tài khoản ổn định"
+ },
+ "reasoning": {
+ "title": "Suy luận sâu",
+ "desc": "Hiển thị quá trình suy luận khi được bật"
+ },
+ "search": {
+ "title": "Tìm kiếm Web",
+ "desc": "Tích hợp tìm kiếm web bản địa"
+ }
+ }
+ },
+ "accountManager": {
+ "addKeySuccess": "Đã thêm khóa API thành công.",
+ "updateKeySuccess": "Đã cập nhật khóa API thành công.",
+ "addAccountSuccess": "Đã thêm tài khoản thành công.",
+ "updateAccountSuccess": "Đã cập nhật thông tin tài khoản thành công.",
+ "requiredFields": "Yêu cầu mật khẩu và email/số điện thoại.",
+ "deleteKeyConfirm": "Bạn có chắc chắn muốn xóa khóa API này?",
+ "deleteAccountConfirm": "Bạn có chắc chắn muốn xóa tài khoản này?",
+ "invalidIdentifier": "Mã định danh tài khoản không hợp lệ. Đã hủy thao tác.",
+ "testAllConfirm": "Làm mới tất cả token tài khoản và xác minh đăng nhập?",
+ "testAllCompleted": "Hoàn tất: {success}/{total} đã được làm mới",
+ "testFailed": "Kiểm tra thất bại: {error}",
+ "available": "Sẵn sàng",
+ "inUse": "Đang sử dụng",
+ "totalPool": "Tổng cộng",
+ "failedPool": "Không dùng được",
+ "failedPoolHint": "Làm mới token để kiểm tra lại",
+ "accountsUnit": "tài khoản",
+ "threadsUnit": "luồng",
+ "apiKeysTitle": "Khóa API",
+ "apiKeysDesc": "Quản lý kho khóa truy cập API. Nhấp vào biểu tượng bút chì để chỉnh sửa tên và ghi chú.",
+ "addKey": "Thêm khóa",
+ "editKeyTitle": "Sửa khóa",
+ "editAccountTitle": "Sửa tài khoản",
+ "copied": "Đã sao chép",
+ "copyFailed": "Sao chép thất bại",
+ "copyKeyTitle": "Sao chép khóa",
+ "deleteKeyTitle": "Xóa khóa",
+ "noApiKeys": "Không tìm thấy khóa API nào.",
+ "accountsTitle": "Tài khoản DeepSeek",
+ "accountsDesc": "Quản lý kho tài khoản DeepSeek và chỉnh sửa tên/ghi chú.",
+ "testAll": "Làm mới tất cả token",
+ "addAccount": "Thêm tài khoản",
+ "testingAllAccounts": "Đang làm mới token cho tất cả tài khoản...",
+ "sessionActive": "Phiên hoạt động",
+ "reauthRequired": "Yêu cầu kiểm tra lại",
+ "runtimeStatusUnknown": "Sẽ được xác định sau khi đồng bộ",
+ "testStatusFailed": "Lần kiểm tra cuối thất bại",
+ "noAccounts": "Không tìm thấy tài khoản nào.",
+ "modalAddKeyTitle": "Thêm khóa API",
+ "modalEditKeyTitle": "Sửa khóa API",
+ "modalEditAccountTitle": "Sửa chi tiết tài khoản",
+ "newKeyLabel": "Giá trị khóa mới",
+ "newKeyPlaceholder": "Nhập khóa API tùy chỉnh",
+ "keyLabel": "Giá trị khóa",
+ "keyReadonlyPlaceholder": "Không thể thay đổi giá trị khóa",
+ "keyReadonlyHint": "Giá trị khóa là chỉ đọc. Vui lòng cập nhật tên và ghi chú.",
+ "generate": "Tạo mới",
+ "generateHint": "Nhấp Tạo mới để tạo khóa ngẫu nhiên.",
+ "addKeyLoading": "Đang thêm...",
+ "addKeyAction": "Thêm khóa",
+ "editKeyLoading": "Đang lưu...",
+ "editKeyAction": "Lưu thay đổi",
+ "editAccountHint": "Chỉ có thể thay đổi tên và ghi chú. Mã định danh tài khoản sẽ giữ nguyên.",
+ "accountIdentifierLabel": "Mã định danh tài khoản",
+ "editAccountLoading": "Đang lưu...",
+ "editAccountAction": "Lưu thay đổi",
+ "modalAddAccountTitle": "Thêm tài khoản DeepSeek",
+ "nameOptional": "Tên (tùy chọn)",
+ "namePlaceholder": "v.d. Tài khoản chính A",
+ "remarkOptional": "Ghi chú (tùy chọn)",
+ "remarkPlaceholder": "v.d. Dùng chung nhóm / chỉ thử nghiệm",
+ "emailOptional": "Email (tùy chọn)",
+ "mobileOptional": "Số điện thoại (tùy chọn)",
+ "passwordLabel": "Mật khẩu",
+ "passwordPlaceholder": "Mật khẩu tài khoản",
+ "addAccountLoading": "Đang thêm...",
+ "addAccountAction": "Thêm tài khoản",
+ "pageInfo": "Trang {current}/{total}, tổng cộng {count} tài khoản",
+ "searchPlaceholder": "Tìm kiếm tài khoản...",
+ "searchNoResults": "Không có tài khoản nào khớp với tìm kiếm",
+ "sessionCount": "Phiên: {count}",
+ "deleteAllSessions": "Xóa tất cả phiên",
+ "deleteAllSessionsConfirm": "Bạn có chắc chắn muốn xóa tất cả phiên của tài khoản này? Hành động này không thể hoàn tác.",
+ "deleteAllSessionsSuccess": "Đã xóa tất cả phiên thành công",
+ "accountProxyLabel": "Proxy tài khoản",
+ "proxyNone": "Kết nối trực tiếp",
+ "proxyBadge": "Proxy: {name}",
+ "proxyUpdateSuccess": "Đã cập nhật proxy cho tài khoản.",
+ "envModeRiskTitle": "Phát hiện chế độ biến môi trường (rủi ro lưu trữ)",
+ "envModeRiskDesc": "Phát hiện DS2API_CONFIG_JSON. Nếu không bật DS2API_ENV_WRITEBACK, các thay đổi trên UI chỉ có hiệu lực trong bộ nhớ và sẽ mất sau khi khởi động lại.",
+ "envModeWritebackPendingTitle": "Chế độ Env + tự động lưu trữ đã bật (đang chờ bàn giao file)",
+ "envModeWritebackActiveTitle": "Chế độ Env + tự động lưu trữ đang hoạt động",
+ "envModeWritebackDesc": "Ứng dụng sẽ tự động tạo/ghi file cấu hình và chuyển sang chế độ lưu trữ file. Đường dẫn lưu trữ hiện tại: {path}"
+ },
+ "proxyManager": {
+ "title": "Proxy IPs",
+ "desc": "Quản lý các nút SOCKS cho tài khoản và kiểm tra kết nối đến DeepSeek.",
+ "addProxy": "Thêm proxy",
+ "editProxy": "Sửa proxy",
+ "deleteProxy": "Xóa proxy",
+ "modalAddTitle": "Thêm nút proxy",
+ "modalEditTitle": "Sửa nút proxy",
+ "modalDesc": "Hỗ trợ socks5 và socks5h. Tài khoản sẽ sử dụng nút này làm đường truyền ra.",
+ "nameLabel": "Tên proxy",
+ "namePlaceholder": "Ví dụ: Hong Kong Exit A",
+ "typeLabel": "Loại proxy",
+ "hostLabel": "Máy chủ proxy",
+ "hostPlaceholder": "127.0.0.1 hoặc tên miền",
+ "portLabel": "Cổng",
+ "usernameLabel": "Tên người dùng (tùy chọn)",
+ "usernamePlaceholder": "Tên người dùng proxy",
+ "passwordLabel": "Mật khẩu (tùy chọn)",
+ "passwordPlaceholder": "Mật khẩu proxy",
+ "passwordKeepHint": "Để trống để giữ mật khẩu hiện tại.",
+ "typeHelp": "socks5 phân giải tên miền tại địa phương; socks5h chuyển tiếp tên miền cho proxy để phân giải DNS từ xa.",
+ "requiredFields": "Yêu cầu máy chủ và cổng.",
+ "saving": "Đang lưu...",
+ "testing": "Đang kiểm tra",
+ "testAction": "Kiểm tra proxy",
+ "untested": "Chưa kiểm tra",
+ "saveAdd": "Thêm proxy",
+ "saveEdit": "Lưu thay đổi",
+ "addSuccess": "Đã thêm proxy thành công.",
+ "updateSuccess": "Đã cập nhật proxy thành công.",
+ "deleteConfirm": "Xóa proxy {name}? Các tài khoản dùng nó sẽ quay về kết nối trực tiếp.",
+ "noProxies": "Chưa có nút proxy nào.",
+ "authEnabled": "Đã bật xác thực",
+ "testSuccessShort": "Kết nối được {time}ms",
+ "testFailedShort": "Kiểm tra thất bại",
+ "totalProxies": "Tổng số proxy",
+ "socks5hCount": "Số nút socks5h",
+ "authProxyCount": "Số nút có xác thực"
+ },
+ "apiTester": {
+ "defaultMessage": "Xin chào, hãy giới thiệu bản thân trong một câu.",
+ "models": {
+ "flash": "v4 Flash (mặc định bật suy luận)",
+ "pro": "v4 Pro (mặc định bật suy luận)",
+ "flashSearch": "v4 Flash (có tìm kiếm)",
+ "proSearch": "v4 Pro (có tìm kiếm)",
+ "vision": "v4 Vision (mặc định bật suy luận)",
+ "generic": "Mô hình tương thích",
+ "noThinking": "bắt buộc tắt suy luận"
+ },
+ "missingApiKey": "Vui lòng cung cấp khóa API.",
+ "requestFailed": "Yêu cầu thất bại.",
+ "networkError": "Lỗi mạng: {error}",
+ "requestSuccess": "{account}: Yêu cầu thành công ({time}ms)",
+ "testSuccess": "{account}: Làm mới token thành công ({time}ms)",
+ "config": "Cấu hình",
+ "modelLabel": "Mô hình",
+ "modelPickerHint": "Chọn mô hình từ danh sách.",
+ "loadingModels": "Đang tải danh sách mô hình...",
+ "loadingModelsHint": "Đang lấy danh sách mô hình từ /v1/models.",
+ "noModels": "Không có mô hình nào khả dụng",
+ "noModelsHint": "Endpoint /v1/models không trả về mô hình nào. Kiểm tra cấu hình backend hoặc trạng thái API.",
+ "noModelsMessagePlaceholder": "Hiện không có mô hình nào, không thể gửi yêu cầu.",
+ "streamMode": "Streaming",
+ "accountSelector": "Tài khoản",
+ "autoRandom": "🤖 Tự động / Ngẫu nhiên",
+ "apiKeyOptional": "Khóa API (tùy chọn)",
+ "apiKeyDefault": "Mặc định: {preview}",
+ "apiKeyPlaceholder": "Nhập khóa tùy chỉnh",
+ "modeManaged": "Chế độ khóa quản lý (dùng kho tài khoản).",
+ "modeDirect": "Chế độ token trực tiếp (yêu cầu token DeepSeek hợp lệ).",
+ "attachmentAccountHint": "Tệp đính kèm được gắn với tài khoản {account}. Gửi sẽ dùng cùng tài khoản này.",
+ "fileAccountConflict": "Các tệp đính kèm đến từ các tài khoản khác nhau. Hãy xóa và tải lên lại dưới một tài khoản.",
+ "fileAccountMismatch": "Tài khoản đã chọn không khớp với tài khoản của tệp đính kèm. Hãy chuyển sang tài khoản tương ứng hoặc xóa tệp đính kèm.",
+ "statusError": "Lỗi",
+ "reasoningTrace": "Quá trình suy luận",
+ "generating": "Đang tạo phản hồi...",
+ "enterMessage": "Nhập tin nhắn...",
+ "adminConsoleLabel": "Bảng điều khiển DeepSeek"
+ },
+ "chatHistory": {
+ "loading": "Đang tải lịch sử trò chuyện...",
+ "loadFailed": "Lỗi khi tải lịch sử trò chuyện.",
+ "retentionTitle": "Lưu trữ",
+ "retentionDesc": "Máy chủ chỉ giữ lại N bản ghi phản hồi DeepSeek mới nhất trên các giao diện OpenAI Chat, OpenAI Responses, Claude và Gemini.",
+ "off": "TẮT",
+ "refresh": "Làm mới",
+ "clearAll": "Xóa tất cả",
+ "clearSuccess": "Đã xóa lịch sử trò chuyện.",
+ "clearFailed": "Lỗi khi xóa lịch sử trò chuyện.",
+ "deleteSuccess": "Đã xóa cuộc trò chuyện.",
+ "deleteFailed": "Lỗi khi xóa cuộc trò chuyện.",
+ "updateLimitFailed": "Lỗi khi cập nhật giới hạn lưu trữ.",
+ "disabledSuccess": "Đã tắt lưu lịch sử trò chuyện.",
+ "limitUpdated": "Đã cập nhật giới hạn lưu trữ thành {limit}",
+ "listTitle": "Lịch sử",
+ "detailTitle": "Chi tiết",
+ "viewModeList": "Chế độ danh sách",
+ "viewModeMerged": "Chế độ hợp nhất",
+ "emptyTitle": "Chưa có lịch sử trò chuyện",
+ "emptyDesc": "Khi một giao diện được hỗ trợ gửi yêu cầu đến DeepSeek và nhận phản hồi, kết quả sẽ tự động được lưu tại đây.",
+ "untitled": "Cuộc trò chuyện không tên",
+ "noPreview": "Không có bản xem trước.",
+ "selectPrompt": "Chọn một bản ghi bên trái để xem chi tiết.",
+ "mergedInput": "Tin nhắn cuối cùng gửi đến DeepSeek",
+ "emptyMergedPrompt": "Không có lời nhắc hợp nhất.",
+ "copyHistory": "Sao chép LỊCH SỬ",
+ "downloadHistory": "Tải về LỊCH SỬ",
+ "copyMerged": "Sao chép lời nhắc hợp nhất",
+ "downloadMerged": "Tải về lời nhắc hợp nhất",
+ "copySuccess": "Sao chép thành công.",
+ "copyFailed": "Sao chép thất bại.",
+ "downloadSuccess": "Tải về thành công.",
+ "downloadFailed": "Tải về thất bại.",
+ "expand": "Mở rộng",
+ "collapse": "Thu gọn",
+ "reasoningTrace": "Quá trình suy luận",
+ "failedOutput": "Yêu cầu thất bại và không có phản hồi từ trợ lý.",
+ "emptyAssistantOutput": "Không có phản hồi từ trợ lý.",
+ "emptyUserInput": "Không có đầu vào từ người dùng.",
+ "confirmClearTitle": "Xóa tất cả bản ghi?",
+ "confirmClearDesc": "Hành động này sẽ xóa mọi bản ghi cuộc trò chuyện trên server và không thể hoàn tác.",
+ "confirmClearAction": "Xóa tất cả",
+ "metaTitle": "Siêu dữ liệu",
+ "metaAccount": "Tài khoản",
+ "metaElapsed": "Thời gian",
+ "metaSurface": "Giao diện",
+ "metaModel": "Mô hình",
+ "metaStatusCode": "Mã trạng thái",
+ "metaStream": "Chế độ đầu ra",
+ "metaCaller": "Mã định danh người gọi",
+ "metaTime": "Hoàn thành lúc",
+ "metaUnknown": "Không xác định",
+ "backToTop": "Về đầu trang",
+ "backToBottom": "Xuống cuối trang",
+ "streamMode": "Streaming",
+ "nonStreamMode": "Không streaming",
+ "status": {
+ "streaming": "Đang stream",
+ "success": "Thành công",
+ "error": "Lỗi",
+ "stopped": "Đã dừng"
+ },
+ "role": {
+ "user": "Người dùng",
+ "assistant": "Trợ lý",
+ "tool": "Công cụ",
+ "system": "Hệ thống"
+ }
+ },
+ "batchImport": {
+ "templates": {
+ "full": {
+ "name": "Mẫu cấu hình đầy đủ",
+ "desc": "Tải từ config.example.json với khóa, tài khoản và mặc định"
+ },
+ "emailOnly": {
+ "name": "Tài khoản dùng Email",
+ "desc": "Nhập hàng loạt tài khoản đăng nhập bằng Email"
+ },
+ "mobileOnly": {
+ "name": "Tài khoản dùng Số điện thoại",
+ "desc": "Nhập hàng loạt tài khoản đăng nhập bằng Số điện thoại"
+ },
+ "keysOnly": {
+ "name": "Chỉ khóa API",
+ "desc": "Chỉ thêm các khóa truy cập API"
+ }
+ },
+ "enterJson": "Vui lòng cung cấp nội dung cấu hình JSON.",
+ "importSuccess": "Nhập thành công: {keys} khóa, {accounts} tài khoản",
+ "templateLoaded": "Đã tải mẫu: {name}",
+ "currentConfigLoaded": "Đã tải cấu hình hiện tại.",
+ "fetchConfigFailed": "Lỗi khi lấy cấu hình.",
+ "copySuccess": "Đã sao chép cấu hình Base64 vào bộ nhớ tạm.",
+ "quickTemplates": "Mẫu nhanh",
+ "dataExport": "Xuất dữ liệu",
+ "dataExportDesc": "Sao chép cấu hình mã hóa Base64 cho biến môi trường Vercel.",
+ "copyBase64": "Sao chép cấu hình Base64",
+ "copied": "Đã sao chép",
+ "variableName": "Tên biến",
+ "jsonEditor": "Trình sửa JSON",
+ "loadCurrentConfig": "Tải cấu hình hiện tại",
+ "applyConfig": "Áp dụng cấu hình",
+ "importing": "Đang nhập...",
+ "importComplete": "Hoàn tất nhập",
+ "importSummary": "Đã nhập {keys} khóa API và cập nhật {accounts} tài khoản."
+ },
+ "settings": {
+ "loadFailed": "Lỗi khi tải cài đặt.",
+ "nonJsonResponse": "Phản hồi không phải JSON từ server (trạng thái: {status}).",
+ "save": "Lưu cài đặt",
+ "saving": "Đang lưu...",
+ "saveSuccess": "Đã lưu cài đặt và tải lại nóng.",
+ "saveFailed": "Lỗi khi lưu cài đặt.",
+ "securityTitle": "Bảo mật",
+ "jwtExpireHours": "Thời hạn JWT (giờ)",
+ "newPassword": "Mật khẩu admin mới",
+ "newPasswordPlaceholder": "Nhập mật khẩu mới (tối thiểu 4 ký tự)",
+ "updatePassword": "Cập nhật mật khẩu",
+ "updating": "Đang cập nhật...",
+ "passwordTooShort": "Mật khẩu phải có ít nhất 4 ký tự.",
+ "passwordUpdated": "Đã cập nhật mật khẩu. Vui lòng đăng nhập lại.",
+ "passwordUpdateFailed": "Lỗi khi cập nhật mật khẩu.",
+ "runtimeTitle": "Runtime",
+ "accountMaxInflight": "Số yêu cầu tối đa mỗi tài khoản",
+ "accountMaxQueue": "Kích thước hàng đợi mỗi tài khoản",
+ "globalMaxInflight": "Số yêu cầu tối đa toàn cục",
+ "tokenRefreshIntervalHours": "Khoảng thời gian làm mới token (giờ)",
+ "behaviorTitle": "Hành vi",
+ "responsesTTL": "Thời gian lưu trữ phản hồi (giây)",
+ "embeddingsProvider": "Nhà cung cấp Embeddings",
+ "thinkingInjectionEnabled": "Chèn định dạng suy luận",
+ "thinkingInjectionDesc": "Thêm danh sách kiểm tra
vào tin nhắn cuối của người dùng trước khi lắp ráp lời nhắc.",
+ "thinkingInjectionPrompt": "Lời nhắc chèn suy luận",
+ "thinkingInjectionPromptHelp": "Để trống để sử dụng lời nhắc mặc định tích hợp.",
+ "currentInputFileTitle": "Chia tách độc lập",
+ "currentInputFileEnabled": "Chia tách độc lập (theo kích thước)",
+ "currentInputFileDesc": "Mặc định bật. Khi đạt đến ngưỡng ký tự, sẽ tải lên toàn bộ ngữ cảnh dưới dạng tệp DS2API_HISTORY.txt.",
+ "currentInputFileMinChars": "Ngưỡng đầu vào hiện tại (ký tự)",
+ "currentInputFileHelp": "Mặc định là 0, sử dụng chia tách độc lập cho bất kỳ đầu vào nào không trống.",
+ "modelTitle": "Ánh xạ mô hình",
+ "modelAliases": "Biệt danh mô hình toàn cục (JSON)",
+ "autoDeleteTitle": "Chính sách dọn dép phiên",
+ "autoDeleteDesc": "Chọn cách dọn dép các bản ghi trò chuyện từ xa trên DeepSeek sau mỗi yêu cầu.",
+ "autoDeleteMode": "Chế độ xóa",
+ "autoDeleteNone": "Không xóa",
+ "autoDeleteSingle": "Xóa phiên hiện tại",
+ "autoDeleteAll": "Xóa tất cả các phiên",
+ "autoDeleteNoneDesc": "Giữ lại phiên từ xa sau khi yêu cầu hoàn thành.",
+ "autoDeleteSingleDesc": "Chỉ xóa phiên từ xa được tạo bởi yêu cầu này.",
+ "autoDeleteAllDesc": "Xóa mọi phiên từ xa của tài khoản sau khi yêu cầu hoàn thành.",
+ "autoDeleteWarning": "Chế độ này sẽ xóa các bản ghi trò chuyện từ xa. Hãy thận trọng.",
+ "backupTitle": "Sao lưu & Phục hồi",
+ "loadExport": "Tải bản xuất hiện tại",
+ "downloadExport": "Tải về tệp sao lưu",
+ "importModeMerge": "Nhập hợp nhất (mặc định)",
+ "importModeReplace": "Nhập thay thế tất cả",
+ "chooseImportFile": "Chọn tệp nhập",
+ "importNow": "Nhập ngay",
+ "importing": "Đang nhập...",
+ "importPlaceholder": "Dán JSON cấu hình để nhập",
+ "importEmpty": "Vui lòng nhập JSON.",
+ "importInvalidJson": "JSON không hợp lệ.",
+ "importFailed": "Nhập thất bại.",
+ "importSuccess": "Đã nhập cấu hình (chế độ: {mode}).",
+ "importFileLoaded": "Đã tải nội dung tệp nhập.",
+ "importFileReadFailed": "Lỗi khi đọc tệp nhập.",
+ "exportFailed": "Xuất thất bại.",
+ "exportLoaded": "Đã tải bản xuất hiện tại.",
+ "exportDownloaded": "Đã bắt đầu tải về tệp sao lưu.",
+ "exportJson": "Xuất JSON",
+ "invalidJsonField": "{field} không phải là đối tượng JSON hợp lệ.",
+ "defaultPasswordWarning": "Bạn đang sử dụng mật khẩu mặc định \"admin\". Vui lòng thay đổi nó.",
+ "vercelSyncHint": "Cấu hình đã thay đổi. Đối với triển khai Vercel, hãy đồng bộ thủ công trong Vercel Sync và triển khai lại.",
+ "autoFetchPaused": "Tự động tải tạm dừng sau {count} lần lỗi: {error}",
+ "retryLoad": "Thử lại ngay"
+ },
+ "login": {
+ "welcome": "Chào mừng trở lại",
+ "subtitle": "Nhập khóa quản trị để tiếp tục",
+ "adminKeyLabel": "Khóa quản trị",
+ "adminKeyPlaceholder": "Nhập khóa quản trị của bạn...",
+ "rememberSession": "Ghi nhớ phiên này",
+ "signIn": "Đăng nhập",
+ "secureConnection": "Kết nối an toàn",
+ "adminPortal": "Cổng quản trị DS2API",
+ "signInFailed": "Đăng nhập thất bại.",
+ "networkError": "Lỗi mạng: {error}"
+ },
+ "vercel": {
+ "tokenRequired": "Yêu cầu Vercel access token.",
+ "projectRequired": "Yêu cầu Project ID.",
+ "syncFailed": "Đồng bộ thất bại.",
+ "networkError": "Lỗi mạng.",
+ "title": "Triển khai Vercel",
+ "description": "Đồng bộ các khóa và tài khoản hiện tại trực tiếp với các biến môi trường Vercel.",
+ "tokenLabel": "Vercel Access Token",
+ "getToken": "Lấy token",
+ "tokenPlaceholderPreconfig": "Sử dụng token đã cấu hình trước",
+ "tokenPlaceholder": "Nhập Vercel access token",
+ "projectIdLabel": "Project ID",
+ "projectIdHint": "Tìm trong Project Settings → General.",
+ "teamIdLabel": "Team ID",
+ "optional": "tùy chọn",
+ "saveCredentials": "Ghi nhớ thông tin Vercel",
+ "saveCredentialsHint": "Lưu token, project ID và team ID cho lần đồng bộ tới.",
+ "syncing": "Đang đồng bộ...",
+ "syncRedeploy": "Đồng bộ & Triển khai lại",
+ "redeployHint": "Điều này sẽ kích hoạt triển khai lại Vercel và thường mất 30–60 giây.",
+ "syncSucceeded": "Đồng bộ thành công",
+ "syncFailedLabel": "Đồng bộ thất bại",
+ "openDeployment": "Mở bản triển khai",
+ "statusSynced": "Đã đồng bộ",
+ "statusNotSynced": "Chưa đồng bộ",
+ "statusNeverSynced": "Chưa từng đồng bộ",
+ "lastSyncTime": "Lần đồng bộ cuối: {time}",
+ "draftDiffers": "Bản nháp frontend khác với cấu hình env. Nhấp Đồng bộ & Triển khai lại.",
+ "pollPaused": "Tạm dừng lấy trạng thái sau {count} lần lỗi.",
+ "manualRefresh": "Làm mới thủ công",
+ "howItWorks": "Cách thức hoạt động",
+ "steps": {
+ "one": "Cấu hình hiện tại (khóa và tài khoản) được xuất dưới dạng JSON.",
+ "two": "JSON được mã hóa Base64 để định dạng an toàn.",
+ "three": "Cập nhật biến môi trường trên Vercel:",
+ "four": "Kích hoạt triển khai lại để áp dụng các biến môi trường đã cập nhật."
+ }
+ }
+}
diff --git a/webui/src/locales/zh.json b/webui/src/locales/zh.json
index 7508a392d..1e9f48a63 100644
--- a/webui/src/locales/zh.json
+++ b/webui/src/locales/zh.json
@@ -2,7 +2,8 @@
"language": {
"label": "语言",
"english": "English",
- "chinese": "中文"
+ "chinese": "中文",
+ "vietnamese": "越南语"
},
"nav": {
"accounts": {
@@ -109,6 +110,8 @@
"available": "可用",
"inUse": "正在使用",
"totalPool": "账号池总数",
+ "failedPool": "不可用",
+ "failedPoolHint": "刷新 Token 重新验证",
"accountsUnit": "个账号",
"threadsUnit": "线程",
"apiKeysTitle": "API 密钥",