From c2eea76de08e275492833ed3fa5456821ddba709 Mon Sep 17 00:00:00 2001 From: piexian <64474352+piexian@users.noreply.github.com> Date: Fri, 6 Mar 2026 21:57:59 +0800 Subject: [PATCH 1/5] feat: add admin log viewer and polish admin tables - add a dedicated admin log viewer page with file switching, level filtering, keyword search, raw log modal, and page-only /v1/admin route filtering - add backend log listing, reading, and deletion endpoints plus reverse-tail log parsing utilities - add mobile-first log file panel behavior, cyclic auto-refresh control, and fixed-height scrollable log cards/lists - polish cache management local and online asset tables with token-style pagination, fixed-height scroll containers, and bulk selection helpers - polish token table layout with a fixed-height scroll wrapper and better long-text handling for note cells - wire the new logs page into admin navigation and update related i18n strings --- _public/static/admin/css/cache.css | 38 ++ _public/static/admin/css/logs.css | 457 +++++++++++++++++++ _public/static/admin/css/token.css | 33 ++ _public/static/admin/js/cache.js | 218 ++++++++- _public/static/admin/js/logs.js | 582 +++++++++++++++++++++++++ _public/static/admin/js/token.js | 2 +- _public/static/admin/pages/cache.html | 143 ++++-- _public/static/admin/pages/logs.html | 167 +++++++ _public/static/admin/pages/token.html | 4 +- _public/static/common/html/header.html | 1 + _public/static/i18n/locales/en.json | 30 ++ _public/static/i18n/locales/zh.json | 30 ++ app/api/pages/admin.py | 5 + app/api/v1/admin/__init__.py | 2 + app/api/v1/admin/logs.py | 53 +++ app/core/log_viewer.py | 200 +++++++++ app/core/response_middleware.py | 1 + 17 files changed, 1906 insertions(+), 60 deletions(-) create mode 100644 _public/static/admin/css/logs.css create mode 100644 _public/static/admin/js/logs.js create mode 100644 _public/static/admin/pages/logs.html create mode 100644 app/api/v1/admin/logs.py create mode 100644 app/core/log_viewer.py diff --git a/_public/static/admin/css/cache.css b/_public/static/admin/css/cache.css index 2b372953..1cdd38f3 100644 --- a/_public/static/admin/css/cache.css +++ b/_public/static/admin/css/cache.css @@ -302,3 +302,41 @@ box-shadow: 0 0 0 2px #000; border-color: #000; } + +.cache-table-scroll { + max-height: 60vh; + overflow: auto; +} + +.cache-pagination-bar { + margin-top: 12px; +} + +.pagination-bar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + flex-wrap: wrap; +} + +@media (max-width: 768px) { + .cache-table-scroll { + max-height: 52vh; + } +} + +.online-table-scroll { + max-height: 60vh; +} + +@media (max-width: 768px) { + .online-table-scroll { + max-height: 52vh; + } +} + +.cache-page-size-select { + width: 96px; + min-width: 96px; +} diff --git a/_public/static/admin/css/logs.css b/_public/static/admin/css/logs.css new file mode 100644 index 00000000..e8723350 --- /dev/null +++ b/_public/static/admin/css/logs.css @@ -0,0 +1,457 @@ +.panel-card { + background: rgba(255, 255, 255, 0.88); + border: 1px solid var(--border); + border-radius: 18px; + padding: 18px; + box-shadow: 0 10px 30px rgba(15, 23, 42, 0.04); + backdrop-filter: blur(10px); +} + +.modal-overlay { + position: fixed; + inset: 0; + background: rgba(15, 23, 42, 0.45); + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + z-index: 50; +} + +.modal-overlay.hidden { + display: none; +} + +.modal-content { + width: min(1100px, 100%); + background: #fff; + border-radius: 24px; + border: 1px solid var(--border); + box-shadow: 0 24px 80px rgba(15, 23, 42, 0.2); + padding: 20px; +} + +.modal-xl { + width: min(1200px, 100%); +} + +.modal-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 16px; +} + +.modal-title { + font-size: 18px; + font-weight: 700; +} + +.modal-close { + border: 1px solid var(--border); + background: #fff; + border-radius: 999px; + width: 36px; + height: 36px; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.auto-refresh-btn { + min-width: 124px; +} + +.auto-refresh-btn.auto-refresh-active { + border-color: rgba(0, 0, 0, 0.18); + background: #f5f5f5; +} + +.log-stat-card { + background: linear-gradient(180deg, rgba(255, 255, 255, 0.94), rgba(248, 250, 252, 0.92)); + border: 1px solid var(--border); + border-radius: 18px; + padding: 18px; +} + +.log-stat-label, +.filter-label, +.panel-subtle { + font-size: 12px; + color: var(--accents-4); +} + +.log-stat-value { + margin-top: 10px; + font-size: 28px; + line-height: 1; + font-weight: 700; + letter-spacing: -0.03em; +} + +.log-stat-hint { + margin-top: 8px; + font-size: 12px; + color: var(--accents-4); +} + +.panel-title-row { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 14px; +} + +.panel-title { + font-size: 14px; + font-weight: 600; +} + +.file-panel-header { + margin-bottom: 0; +} + +.file-panel-toggle { + display: inline-flex; + align-items: center; + gap: 6px; + border: 1px solid var(--border); + background: #fff; + border-radius: 999px; + padding: 6px 10px; + font-size: 12px; + color: var(--accents-5); +} + +.file-panel-toggle-icon { + transition: transform 0.15s ease; +} + +.file-panel-toggle[aria-expanded="true"] .file-panel-toggle-icon { + transform: rotate(180deg); +} + +.desktop-hidden { + display: none; +} + +.file-panel-body { + margin-top: 14px; +} + +.file-panel-body.mobile-collapsed { + display: none; +} + +.file-toolbar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 12px; +} + +.log-file-list { + display: flex; + flex-direction: column; + gap: 8px; + max-height: 960px; + overflow: auto; +} + +.log-file-item { + border: 1px solid var(--border); + border-radius: 14px; + padding: 12px; + cursor: pointer; + transition: all 0.15s ease; + background: #fff; +} + +.log-file-item:hover, +.log-file-item.active { + border-color: rgba(0, 0, 0, 0.2); + transform: translateY(-1px); + box-shadow: 0 8px 22px rgba(15, 23, 42, 0.06); +} + +.log-file-row { + display: flex; + gap: 10px; + align-items: flex-start; +} + +.log-file-body { + min-width: 0; + flex: 1; +} + +.log-file-name { + font-size: 13px; + font-weight: 600; + word-break: break-all; +} + +.log-file-meta { + margin-top: 6px; + display: flex; + justify-content: space-between; + gap: 10px; + font-size: 11px; + color: var(--accents-4); +} + +.log-list-scroll { + max-height: 72vh; + overflow: auto; + padding-right: 4px; +} + +.log-entry { + border: 1px solid var(--border); + border-radius: 18px; + background: rgba(255, 255, 255, 0.95); + overflow: hidden; +} + +.log-entry-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 14px 16px 10px; +} + +.log-entry-main { + display: flex; + align-items: center; + gap: 10px; + min-width: 0; +} + +.log-entry-time { + font-size: 12px; + color: var(--accents-4); + white-space: nowrap; +} + +.log-badge { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 74px; + height: 24px; + border-radius: 999px; + font-size: 11px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.06em; + border: 0; +} + +.log-badge.debug, +.level-pill.debug { + color: #1d4ed8; + background: #dbeafe; +} + +.log-badge.info, +.level-pill.info { + color: #0369a1; + background: #e0f2fe; +} + +.log-badge.warning, +.level-pill.warning { + color: #b45309; + background: #fef3c7; +} + +.log-badge.error, +.level-pill.error { + color: #b91c1c; + background: #fee2e2; +} + +.log-entry-actions { + display: flex; + align-items: center; + gap: 8px; +} + +.log-entry-body { + padding: 0 16px 16px; +} + +.log-entry-scroll { + max-height: 420px; + overflow: auto; + padding-right: 4px; +} + +.log-message { + font-size: 14px; + line-height: 1.6; + word-break: break-word; + white-space: pre-wrap; + overflow-wrap: anywhere; + max-height: 160px; + overflow: auto; + padding-right: 4px; +} + +.log-meta-grid { + margin-top: 12px; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 10px; +} + +.log-meta-card { + border: 1px solid rgba(0, 0, 0, 0.05); + background: #fafafa; + border-radius: 14px; + padding: 12px; +} + +.log-meta-key { + font-size: 11px; + color: var(--accents-4); + margin-bottom: 6px; + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.log-meta-value { + font-size: 12px; + color: var(--accents-6); + word-break: break-word; + white-space: pre-wrap; + overflow-wrap: anywhere; + max-height: 96px; + overflow: auto; + padding-right: 4px; +} + +.log-stacktrace { + margin-top: 12px; + border-radius: 14px; + background: #0f172a; + color: #e2e8f0; + font-size: 12px; + line-height: 1.55; + padding: 14px; + white-space: pre-wrap; + overflow-wrap: anywhere; + overflow: auto; + max-height: 220px; +} + +.level-pill { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 10px; + border-radius: 999px; + border: 1px solid transparent; + font-size: 12px; + cursor: pointer; + transition: all 0.15s ease; +} + +.level-pill:hover, +.level-pill.active { + border-color: rgba(0, 0, 0, 0.12); + transform: translateY(-1px); +} + +.level-pill-dot { + width: 8px; + height: 8px; + border-radius: 999px; +} + +.level-pill-dot.debug { background: #3b82f6; } +.level-pill-dot.info { background: #0ea5e9; } +.level-pill-dot.warning { background: #f59e0b; } +.level-pill-dot.error { background: #ef4444; } +.level-pill-dot.unknown { background: #94a3b8; } + +.log-modal-body { + margin-top: 14px; + border-radius: 16px; + background: #0b1120; + color: #dbeafe; + font-size: 12px; + line-height: 1.6; + padding: 16px; + overflow: auto; + max-height: min(70vh, 760px); +} + +@media (max-width: 768px) { + .modal-overlay { + padding: 12px; + } + + .modal-content { + padding: 16px; + border-radius: 18px; + } + + .panel-card.h-full { + padding-bottom: 14px; + } + + .file-panel { + padding-bottom: 14px; + } + + .log-entry-header { + flex-direction: column; + align-items: flex-start; + } + + .file-toolbar { + margin-bottom: 10px; + flex-direction: row; + align-items: center; + justify-content: space-between; + } + + .log-file-list { + max-height: 248px; + overflow-y: auto; + padding-right: 4px; + -webkit-overflow-scrolling: touch; + } + + .log-file-item { + padding: 10px; + } + + .log-entry-actions { + width: 100%; + justify-content: flex-end; + } +} + +@media (max-width: 768px) { + .log-entry-scroll { + max-height: 360px; + } + + .log-message { + max-height: 132px; + } + + .log-meta-value { + max-height: 84px; + } +} + +@media (max-width: 768px) { + .log-list-scroll { + max-height: 68vh; + } +} diff --git a/_public/static/admin/css/token.css b/_public/static/admin/css/token.css index 8bf11b84..93b32768 100644 --- a/_public/static/admin/css/token.css +++ b/_public/static/admin/css/token.css @@ -74,6 +74,28 @@ padding: 8px 4px; } + + .token-table-scroll { + max-height: 68vh; + overflow: auto; + } + + .token-note-cell { + max-width: 220px; + white-space: pre-wrap; + overflow-wrap: anywhere; + word-break: break-word; + max-height: 80px; + overflow: auto; + padding-right: 4px; + } + + .token-table-card .font-mono { + white-space: pre-wrap; + overflow-wrap: anywhere; + word-break: break-word; + } + /* Elegant Badges */ .badge { display: inline-flex; @@ -356,3 +378,14 @@ background: #e5e5e5; margin: 0 4px; } + + @media (max-width: 768px) { + .token-table-scroll { + max-height: 62vh; + } + + .token-note-cell { + max-width: 160px; + max-height: 72px; + } + } diff --git a/_public/static/admin/js/cache.js b/_public/static/admin/js/cache.js index 3b0ffc30..f257059d 100644 --- a/_public/static/admin/js/cache.js +++ b/_public/static/admin/js/cache.js @@ -16,9 +16,10 @@ let currentBatchAction = null; let lastBatchAction = null; let isLocalDeleting = false; const cacheListState = { - image: { loaded: false, visible: false, items: [] }, - video: { loaded: false, visible: false, items: [] } + image: { loaded: false, visible: false, items: [], total: 0, page: 1, pageSize: 50 }, + video: { loaded: false, visible: false, items: [], total: 0, page: 1, pageSize: 50 } }; +const onlineTableState = { rows: [], page: 1, pageSize: 50 }; const UI_MAP = { imgCount: 'img-count', imgSize: 'img-size', @@ -29,6 +30,10 @@ const UI_MAP = { onlineLastClear: 'online-last-clear', accountTableBody: 'account-table-body', accountEmpty: 'account-empty', + onlinePaginationInfo: 'online-pagination-info', + onlinePagePrev: 'online-page-prev', + onlinePageNext: 'online-page-next', + onlinePageSize: 'online-page-size', selectAll: 'select-all', localImageSelectAll: 'local-image-select-all', localVideoSelectAll: 'local-video-select-all', @@ -41,6 +46,14 @@ const UI_MAP = { localVideoList: 'local-video-list', localImageBody: 'local-image-body', localVideoBody: 'local-video-body', + localImagePaginationInfo: 'local-image-pagination-info', + localVideoPaginationInfo: 'local-video-pagination-info', + localImagePagePrev: 'local-image-page-prev', + localVideoPagePrev: 'local-video-page-prev', + localImagePageNext: 'local-image-page-next', + localVideoPageNext: 'local-video-page-next', + localImagePageSize: 'local-image-page-size', + localVideoPageSize: 'local-video-page-size', onlineAssetsTable: 'online-assets-table', batchProgress: 'batch-progress', batchProgressText: 'batch-progress-text', @@ -363,16 +376,24 @@ function renderAccountTable(data) { })); } + onlineTableState.rows = rows; + const totalPages = Math.max(1, Math.ceil(rows.length / onlineTableState.pageSize)); + onlineTableState.page = Math.min(Math.max(1, onlineTableState.page), totalPages); + if (rows.length === 0) { tbody.replaceChildren(); empty.classList.remove('hidden'); + renderOnlinePagination(); + syncSelectAllState(); + updateSelectedCount(); return; } empty.classList.add('hidden'); + const visibleRows = getVisibleOnlineRows(); const selected = selectedTokens; const fragment = document.createDocumentFragment(); - rows.forEach(row => { + visibleRows.forEach(row => { const tr = document.createElement('tr'); const isSelected = selected.has(row.token); if (isSelected) tr.classList.add('row-selected'); @@ -436,11 +457,17 @@ function renderAccountTable(data) { fragment.appendChild(tr); }); tbody.replaceChildren(fragment); + renderOnlinePagination(); syncSelectAllState(); updateSelectedCount(); updateBatchActionsVisibility(); } +function getVisibleOnlineRows() { + const start = Math.max(0, (onlineTableState.page - 1) * onlineTableState.pageSize); + return onlineTableState.rows.slice(start, start + onlineTableState.pageSize); +} + async function clearCache(type) { const ok = await confirmAction(t(type === 'image' ? 'cache.confirmClearImage' : 'cache.confirmClearVideo'), { okText: t('cache.clear') }); if (!ok) return; @@ -495,10 +522,11 @@ function toggleSelect(token, checkbox) { function toggleSelectAll(checkbox) { const shouldSelect = checkbox.checked; - selectedTokens.clear(); - if (shouldSelect) { - accountMap.forEach((_, token) => selectedTokens.add(token)); - } + const visibleRows = getVisibleOnlineRows(); + visibleRows.forEach(row => { + if (shouldSelect) selectedTokens.add(row.token); + else selectedTokens.delete(row.token); + }); syncRowCheckboxes(); updateSelectedCount(); } @@ -534,6 +562,34 @@ function toggleLocalSelectAll(type, checkbox) { updateSelectedCount(); } + +function selectVisibleLocal(type) { + const set = selectedLocal[type]; + const items = cacheListState[type]?.items || []; + if (!set || items.length === 0) return; + items.forEach(item => { + if (item && item.name) set.add(item.name); + }); + syncLocalRowCheckboxes(type); + updateSelectedCount(); +} + +function selectAllLocal(type) { + selectVisibleLocal(type); +} + +function clearAllLocalSelection(type) { + const set = selectedLocal[type]; + if (!set || set.size === 0) return; + set.clear(); + syncLocalRowCheckboxes(type); + updateSelectedCount(); +} + +window.selectVisibleLocal = selectVisibleLocal; +window.selectAllLocal = selectAllLocal; +window.clearAllLocalSelection = clearAllLocalSelection; + function syncLocalRowCheckboxes(type) { const body = type === 'image' ? ui.localImageBody : ui.localVideoBody; if (!body) return; @@ -574,12 +630,81 @@ function syncRowCheckboxes() { function syncSelectAllState() { const selectAll = ui.selectAll; if (!selectAll) return; - const total = accountMap.size; - const selected = selectedTokens.size; + const visibleRows = getVisibleOnlineRows(); + const total = visibleRows.length; + const selected = visibleRows.filter(row => selectedTokens.has(row.token)).length; + selectAll.disabled = total === 0; selectAll.checked = total > 0 && selected === total; selectAll.indeterminate = selected > 0 && selected < total; } +function renderOnlinePagination() { + const info = ui.onlinePaginationInfo; + const prevBtn = ui.onlinePagePrev; + const nextBtn = ui.onlinePageNext; + const sizeSelect = ui.onlinePageSize; + if (!info || !prevBtn || !nextBtn || !sizeSelect) return; + + const totalCount = onlineTableState.rows.length; + const pageSize = Number(onlineTableState.pageSize || 50); + const totalPages = Math.max(1, Math.ceil(totalCount / pageSize)); + const currentPage = Math.min(Math.max(1, Number(onlineTableState.page || 1)), totalPages); + info.textContent = `第 ${totalCount === 0 ? 0 : currentPage} / ${totalPages} 页 · 共 ${totalCount} 条`; + prevBtn.disabled = currentPage <= 1 || totalCount === 0; + nextBtn.disabled = currentPage >= totalPages || totalCount === 0; + sizeSelect.value = String(pageSize); +} + +function selectVisibleAccounts() { + const visibleRows = getVisibleOnlineRows(); + if (visibleRows.length === 0) return; + visibleRows.forEach(row => selectedTokens.add(row.token)); + syncRowCheckboxes(); + updateSelectedCount(); +} + +function selectAllAccounts() { + if (onlineTableState.rows.length === 0) return; + onlineTableState.rows.forEach(row => selectedTokens.add(row.token)); + syncRowCheckboxes(); + updateSelectedCount(); +} + +function clearAllAccountSelection() { + if (selectedTokens.size === 0) return; + selectedTokens.clear(); + syncRowCheckboxes(); + updateSelectedCount(); +} + +function onlineGoPrevPage() { + if (onlineTableState.page <= 1) return; + onlineTableState.page -= 1; + renderAccountTable({ online_accounts: Array.from(accountMap.values()), online_details: [], online: {} }); +} + +function onlineGoNextPage() { + const totalPages = Math.max(1, Math.ceil(onlineTableState.rows.length / onlineTableState.pageSize)); + if (onlineTableState.page >= totalPages) return; + onlineTableState.page += 1; + renderAccountTable({ online_accounts: Array.from(accountMap.values()), online_details: [], online: {} }); +} + +function changeOnlinePageSize() { + const sizeSelect = ui.onlinePageSize; + if (!sizeSelect) return; + onlineTableState.pageSize = Number(sizeSelect.value || 50); + onlineTableState.page = 1; + renderAccountTable({ online_accounts: Array.from(accountMap.values()), online_details: [], online: {} }); +} + +window.selectVisibleAccounts = selectVisibleAccounts; +window.selectAllAccounts = selectAllAccounts; +window.clearAllAccountSelection = clearAllAccountSelection; +window.onlineGoPrevPage = onlineGoPrevPage; +window.onlineGoNextPage = onlineGoNextPage; +window.changeOnlinePageSize = changeOnlinePageSize; + function updateSelectedCount() { const el = ui.selectedCount; const selected = getActiveSelectedSet().size; @@ -728,7 +853,7 @@ async function showCacheSection(type) { if (type === 'image') { cacheListState.image.visible = true; cacheListState.video.visible = false; - if (cacheListState.image.loaded) renderLocalCacheList('image', cacheListState.image.items); + if (cacheListState.image.loaded) { renderLocalCacheList('image', cacheListState.image.items); renderLocalPagination('image'); } else await loadLocalCacheList('image'); if (ui.localCacheLists) ui.localCacheLists.classList.remove('hidden'); if (ui.localImageList) ui.localImageList.classList.remove('hidden'); @@ -740,7 +865,7 @@ async function showCacheSection(type) { if (type === 'video') { cacheListState.video.visible = true; cacheListState.image.visible = false; - if (cacheListState.video.loaded) renderLocalCacheList('video', cacheListState.video.items); + if (cacheListState.video.loaded) { renderLocalCacheList('video', cacheListState.video.items); renderLocalPagination('video'); } else await loadLocalCacheList('video'); if (ui.localCacheLists) ui.localCacheLists.classList.remove('hidden'); if (ui.localVideoList) ui.localVideoList.classList.remove('hidden'); @@ -766,10 +891,15 @@ async function toggleCacheList(type) { async function loadLocalCacheList(type) { const body = type === 'image' ? ui.localImageBody : ui.localVideoBody; - if (!body) return; + const state = cacheListState[type]; + if (!body || !state) return; body.innerHTML = `${t('common.loading')}`; try { - const params = new URLSearchParams({ type, page: '1', page_size: '1000' }); + const params = new URLSearchParams({ + type, + page: String(state.page || 1), + page_size: String(state.pageSize || 50) + }); const res = await fetch(`/v1/admin/cache/list?${params.toString()}`, { headers: buildAuthHeaders(apiKey) }); @@ -779,16 +909,21 @@ async function loadLocalCacheList(type) { } const data = await res.json(); const items = Array.isArray(data.items) ? data.items : []; - cacheListState[type].items = items; - cacheListState[type].loaded = true; + state.items = items; + state.total = Number(data.total || 0); + state.page = Number(data.page || state.page || 1); + state.pageSize = Number(data.page_size || state.pageSize || 50); + state.loaded = true; const keep = new Set(items.map(item => item.name)); const selected = selectedLocal[type]; Array.from(selected).forEach(name => { if (!keep.has(name)) selected.delete(name); }); renderLocalCacheList(type, items); + renderLocalPagination(type); } catch (e) { body.innerHTML = `${t('common.loadFailed')}`; + renderLocalPagination(type); } } @@ -798,6 +933,7 @@ function renderLocalCacheList(type, items) { if (!items || items.length === 0) { body.innerHTML = `${t('cache.noFiles')}`; syncLocalSelectAllState(type); + renderLocalPagination(type); return; } const selected = selectedLocal[type]; @@ -873,6 +1009,53 @@ function renderLocalCacheList(type, items) { updateSelectedCount(); } +function renderLocalPagination(type) { + const state = cacheListState[type]; + const info = type === 'image' ? ui.localImagePaginationInfo : ui.localVideoPaginationInfo; + const prevBtn = type === 'image' ? ui.localImagePagePrev : ui.localVideoPagePrev; + const nextBtn = type === 'image' ? ui.localImagePageNext : ui.localVideoPageNext; + const sizeSelect = type === 'image' ? ui.localImagePageSize : ui.localVideoPageSize; + if (!state || !info || !prevBtn || !nextBtn || !sizeSelect) return; + + const totalCount = Number(state.total || 0); + const pageSize = Number(state.pageSize || 50); + const totalPages = Math.max(1, Math.ceil(totalCount / pageSize)); + const currentPage = Math.min(Math.max(1, Number(state.page || 1)), totalPages); + info.textContent = `第 ${totalCount === 0 ? 0 : currentPage} / ${totalPages} 页 · 共 ${totalCount} 条`; + prevBtn.disabled = currentPage <= 1 || totalCount === 0; + nextBtn.disabled = currentPage >= totalPages || totalCount === 0; + sizeSelect.value = String(pageSize); +} + +async function localGoPrevPage(type) { + const state = cacheListState[type]; + if (!state || state.page <= 1) return; + state.page -= 1; + await loadLocalCacheList(type); +} + +async function localGoNextPage(type) { + const state = cacheListState[type]; + if (!state) return; + const totalPages = Math.max(1, Math.ceil((state.total || 0) / (state.pageSize || 50))); + if (state.page >= totalPages) return; + state.page += 1; + await loadLocalCacheList(type); +} + +async function changeLocalPageSize(type) { + const state = cacheListState[type]; + const sizeSelect = type === 'image' ? ui.localImagePageSize : ui.localVideoPageSize; + if (!state || !sizeSelect) return; + state.pageSize = Number(sizeSelect.value || 50); + state.page = 1; + await loadLocalCacheList(type); +} + +window.localGoPrevPage = localGoPrevPage; +window.localGoNextPage = localGoNextPage; +window.changeLocalPageSize = changeLocalPageSize; + function viewLocalFile(type, name) { const safeName = encodeURIComponent(name); const url = type === 'image' ? `/v1/files/image/${safeName}` : `/v1/files/video/${safeName}`; @@ -887,10 +1070,9 @@ async function deleteLocalFile(type, name) { showToast(t('common.deleteSuccess'), 'success'); const state = cacheListState[type]; if (state && Array.isArray(state.items)) { - state.items = state.items.filter(item => item.name !== name); - state.loaded = true; selectedLocal[type]?.delete(name); - if (state.visible) renderLocalCacheList(type, state.items); + state.loaded = false; + if (state.visible) await loadLocalCacheList(type); } await loadStats(); } diff --git a/_public/static/admin/js/logs.js b/_public/static/admin/js/logs.js new file mode 100644 index 00000000..73c631c9 --- /dev/null +++ b/_public/static/admin/js/logs.js @@ -0,0 +1,582 @@ +let adminAuthHeader = ''; +let logFiles = []; +let currentResponse = null; +let selectedFiles = new Set(); +let autoRefreshTimer = null; +let autoRefreshIndex = -1; +let autoRefreshLongPressTimer = null; +let isRefreshing = false; +let currentFileName = ''; +let isMobileFilePanelCollapsed = false; + +const AUTO_REFRESH_OPTIONS = [5000, 10000, 30000]; +const logsById = (id) => document.getElementById(id); +const isMobileViewport = () => window.innerWidth <= 768; + +document.addEventListener('DOMContentLoaded', async () => { + adminAuthHeader = await ensureAdminKey(); + if (!adminAuthHeader) return; + + bindEvents(); + syncFilePanelMode(); + updateAutoRefreshButton(); + await loadFiles(); + syncAutoRefresh(); +}); + +function bindEvents() { + logsById('refresh-btn')?.addEventListener('click', async () => { + await refreshCurrentView(); + }); + + const autoRefreshBtn = logsById('auto-refresh-btn'); + autoRefreshBtn?.addEventListener('click', () => { + cycleAutoRefresh(); + }); + autoRefreshBtn?.addEventListener('mousedown', startAutoRefreshLongPress); + autoRefreshBtn?.addEventListener('touchstart', startAutoRefreshLongPress, { passive: true }); + autoRefreshBtn?.addEventListener('mouseup', cancelAutoRefreshLongPress); + autoRefreshBtn?.addEventListener('mouseleave', cancelAutoRefreshLongPress); + autoRefreshBtn?.addEventListener('touchend', cancelAutoRefreshLongPress); + autoRefreshBtn?.addEventListener('touchcancel', cancelAutoRefreshLongPress); + + logsById('apply-btn')?.addEventListener('click', async () => { + await loadLogs(); + }); + + logsById('reset-btn')?.addEventListener('click', async () => { + logsById('log-level').value = ''; + logsById('log-limit').value = '200'; + logsById('log-keyword').value = ''; + logsById('exclude-admin-routes').checked = true; + updateClearLevelButton(); + await loadLogs(); + }); + + logsById('clear-level-btn')?.addEventListener('click', async () => { + logsById('log-level').value = ''; + updateClearLevelButton(); + await loadLogs(); + }); + + logsById('log-keyword')?.addEventListener('keydown', async (event) => { + if (event.key === 'Enter') { + event.preventDefault(); + await loadLogs(); + } + }); + + logsById('select-all-files')?.addEventListener('change', (event) => { + toggleSelectAllFiles(Boolean(event.target.checked)); + }); + + logsById('delete-selected-btn')?.addEventListener('click', async () => { + await deleteSelectedFiles(); + }); + + logsById('file-panel-toggle')?.addEventListener('click', () => { + toggleFilePanel(); + }); + + window.addEventListener('resize', () => { + syncFilePanelMode(); + }); + + logsById('close-modal-btn')?.addEventListener('click', closeModal); + logsById('log-modal')?.addEventListener('click', (event) => { + if (event.target.id === 'log-modal') closeModal(); + }); +} + +async function loadFiles(keepSelection = false) { + setLoading(true); + try { + const previousSelection = keepSelection ? currentFileName : ''; + const res = await fetch('/v1/admin/logs/files', { + headers: buildAuthHeaders(adminAuthHeader), + }); + if (!res.ok) throw new Error(await getErrorMessage(res)); + const data = await res.json(); + logFiles = Array.isArray(data.files) ? data.files : []; + selectedFiles = new Set([...selectedFiles].filter((name) => logFiles.some((item) => item.name === name))); + currentFileName = resolveCurrentFileName(previousSelection); + renderFileList(); + updateFileCountSummary(); + if (currentFileName) { + await loadLogs(); + } else { + renderEmptyFiles(); + } + } catch (error) { + showToast(error.message || '加载日志文件失败', 'error'); + renderEmptyFiles(); + } finally { + setLoading(false); + } +} + +function resolveCurrentFileName(previousSelection) { + if (previousSelection && logFiles.some((item) => item.name === previousSelection)) { + return previousSelection; + } + return logFiles[0]?.name || ''; +} + +async function refreshCurrentView() { + if (isRefreshing) return; + isRefreshing = true; + try { + await loadFiles(true); + } finally { + isRefreshing = false; + } +} + +async function loadLogs() { + const file = currentFileName; + if (!file) { + renderEntries([]); + renderLevelBreakdown({}); + updateStats(null); + return; + } + + setLoading(true); + try { + const params = new URLSearchParams({ + file, + limit: logsById('log-limit').value || '200', + }); + const level = logsById('log-level').value; + const keyword = logsById('log-keyword').value.trim(); + if (level) params.set('level', level); + if (keyword) params.set('keyword', keyword); + if (logsById('exclude-admin-routes').checked) { + params.set('exclude_admin_routes', 'true'); + } + + const res = await fetch(`/v1/admin/logs?${params.toString()}`, { + headers: buildAuthHeaders(adminAuthHeader), + }); + if (!res.ok) throw new Error(await getErrorMessage(res)); + currentResponse = await res.json(); + renderEntries(currentResponse.entries || []); + renderLevelBreakdown(currentResponse.stats?.levels || {}); + updateStats(currentResponse); + updateClearLevelButton(); + syncFileListState(currentFileName); + } catch (error) { + renderEntries([]); + renderLevelBreakdown({}); + updateStats(null); + showToast(error.message || '加载日志失败', 'error'); + } finally { + setLoading(false); + } +} + +function renderFileList() { + const container = logsById('log-file-list'); + const count = logsById('file-count-summary'); + const selectAll = logsById('select-all-files'); + if (!container || !count || !selectAll) return; + + count.textContent = `${logFiles.length} 个日志文件`; + container.innerHTML = ''; + + logFiles.forEach((item) => { + const button = document.createElement('button'); + button.type = 'button'; + button.className = `log-file-item ${item.name === currentFileName ? 'active' : ''}`; + button.innerHTML = ` +
+ +
+
${escapeHtml(item.name)}
+
+ ${escapeHtml(formatDate(item.updated_at))} + ${escapeHtml(formatBytes(item.size))} +
+
+
+ `; + + button.addEventListener('click', async () => { + currentFileName = item.name; + syncFileListState(item.name); + if (isMobileViewport()) { + setMobileFilePanelCollapsed(true); + } + await loadLogs(); + }); + + const checkbox = button.querySelector('.log-file-check'); + checkbox?.addEventListener('click', (event) => { + event.stopPropagation(); + }); + checkbox?.addEventListener('change', (event) => { + if (event.target.checked) { + selectedFiles.add(item.name); + } else { + selectedFiles.delete(item.name); + } + updateFileSelectionState(); + }); + + container.appendChild(button); + }); + + updateFileSelectionState(); +} + +function renderEntries(entries) { + const list = logsById('log-list'); + const empty = logsById('empty-state'); + if (!list || !empty) return; + + list.innerHTML = ''; + empty.classList.toggle('hidden', entries.length > 0); + + entries.forEach((entry) => { + const card = document.createElement('article'); + card.className = 'log-entry'; + const extras = extractExtras(entry); + const extrasHtml = extras.map(([key, value]) => ` +
+
${escapeHtml(key)}
+
${escapeHtml(stringifyValue(value))}
+
+ `).join(''); + const stacktrace = entry.stacktrace + ? `
${escapeHtml(entry.stacktrace)}
` + : ''; + + card.innerHTML = ` +
+
+ +
${escapeHtml(entry.time_display || '-')}
+
${escapeHtml(entry.caller || '-')}
+
+
+ + +
+
+
+
+
${escapeHtml(entry.msg || '')}
+ ${extrasHtml ? `
${extrasHtml}
` : ''} + ${stacktrace} +
+
+ `; + + card.querySelector('[data-action="copy"]')?.addEventListener('click', async () => { + await copyText(entry.raw || JSON.stringify(entry, null, 2)); + }); + card.querySelector('[data-action="raw"]')?.addEventListener('click', () => { + openModal(entry); + }); + card.querySelector('[data-level]')?.addEventListener('click', async () => { + await setLevelFilter(entry.level || ''); + }); + list.appendChild(card); + }); +} + +function renderLevelBreakdown(levels) { + const container = logsById('level-breakdown'); + if (!container) return; + container.innerHTML = ''; + const activeLevel = logsById('log-level').value; + const names = Object.keys(levels).sort((a, b) => (levels[b] || 0) - (levels[a] || 0)); + + names.forEach((level) => { + const pill = document.createElement('button'); + pill.type = 'button'; + pill.className = `level-pill ${escapeHtml(level)} ${activeLevel === level ? 'active' : ''}`; + pill.innerHTML = ` + + ${escapeHtml(level)} + ${levels[level]} + `; + pill.addEventListener('click', async () => { + await setLevelFilter(activeLevel === level ? '' : level); + }); + container.appendChild(pill); + }); +} + +async function setLevelFilter(level) { + logsById('log-level').value = level; + updateClearLevelButton(); + await loadLogs(); +} + +function updateClearLevelButton() { + const button = logsById('clear-level-btn'); + if (!button) return; + button.classList.toggle('hidden', !logsById('log-level').value); +} + +function updateStats(response) { + const fileName = response?.file?.name || currentFileName || '-'; + const updated = response?.file?.updated_at ? formatDate(response.file.updated_at) : '-'; + const size = response?.file?.size ? formatBytes(response.file.size) : '0 B'; + const matched = response?.stats?.matched || 0; + const warning = response?.stats?.levels?.warning || 0; + const error = response?.stats?.levels?.error || 0; + + logsById('stat-file').textContent = fileName; + logsById('stat-updated').textContent = updated; + logsById('stat-size').textContent = size; + logsById('stat-matched').textContent = `${matched}`; + logsById('stat-risk').textContent = `${warning + error}`; +} + +function renderEmptyFiles() { + logsById('log-file-list').innerHTML = '
暂无日志文件
'; + currentFileName = ''; + selectedFiles.clear(); + updateFileCountSummary(); + updateFileSelectionState(); + renderEntries([]); + renderLevelBreakdown({}); + updateStats(null); +} + +function updateFileCountSummary() { + const summary = logsById('file-count-summary'); + if (summary) { + summary.textContent = `${logFiles.length} 个日志文件`; + } +} + +function setLoading(loading) { + logsById('loading-state')?.classList.toggle('hidden', !loading); +} + +function syncFileListState(activeName) { + document.querySelectorAll('.log-file-item').forEach((item) => { + const name = item.querySelector('.log-file-name')?.textContent; + item.classList.toggle('active', name === activeName); + }); +} + +function toggleSelectAllFiles(checked) { + selectedFiles = checked ? new Set(logFiles.map((item) => item.name)) : new Set(); + renderFileList(); +} + +function updateFileSelectionState() { + const selectAll = logsById('select-all-files'); + if (selectAll) { + selectAll.checked = logFiles.length > 0 && selectedFiles.size === logFiles.length; + selectAll.indeterminate = selectedFiles.size > 0 && selectedFiles.size < logFiles.length; + } + const deleteButton = logsById('delete-selected-btn'); + if (deleteButton) { + deleteButton.disabled = selectedFiles.size === 0; + } +} + +async function deleteSelectedFiles() { + const files = [...selectedFiles]; + if (!files.length) { + showToast('请先选择要清理的日志文件', 'error'); + return; + } + if (!window.confirm(`确认清理 ${files.length} 个日志文件?`)) { + return; + } + + try { + const res = await fetch('/v1/admin/logs/delete', { + method: 'POST', + headers: { + ...buildAuthHeaders(adminAuthHeader), + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ files }), + }); + if (!res.ok) throw new Error(await getErrorMessage(res)); + const data = await res.json(); + selectedFiles.clear(); + showToast(`已清理 ${data.deleted?.length || 0} 个日志文件`, 'success'); + await loadFiles(true); + } catch (error) { + showToast(error.message || '清理日志失败', 'error'); + } +} + +function cycleAutoRefresh() { + autoRefreshIndex += 1; + if (autoRefreshIndex >= AUTO_REFRESH_OPTIONS.length) { + autoRefreshIndex = -1; + } + syncAutoRefresh(); + updateAutoRefreshButton(); + const label = autoRefreshIndex >= 0 ? `${AUTO_REFRESH_OPTIONS[autoRefreshIndex] / 1000}s` : '关闭'; + showToast(`自动刷新:${label}`, 'success'); +} + +function syncAutoRefresh() { + if (autoRefreshTimer) { + clearInterval(autoRefreshTimer); + autoRefreshTimer = null; + } + + if (autoRefreshIndex < 0) { + return; + } + + const interval = AUTO_REFRESH_OPTIONS[autoRefreshIndex]; + autoRefreshTimer = window.setInterval(async () => { + if (document.hidden) return; + await refreshCurrentView(); + }, interval); +} + +function updateAutoRefreshButton() { + const button = logsById('auto-refresh-btn'); + if (!button) return; + const text = autoRefreshIndex < 0 ? '自动刷新:关' : `自动刷新:${AUTO_REFRESH_OPTIONS[autoRefreshIndex] / 1000}s`; + button.textContent = text; + button.classList.toggle('auto-refresh-active', autoRefreshIndex >= 0); +} + +function startAutoRefreshLongPress() { + cancelAutoRefreshLongPress(); + autoRefreshLongPressTimer = window.setTimeout(() => { + if (autoRefreshIndex >= 0) { + autoRefreshIndex = -1; + syncAutoRefresh(); + updateAutoRefreshButton(); + showToast('自动刷新已关闭', 'success'); + } + autoRefreshLongPressTimer = null; + }, 600); +} + +function cancelAutoRefreshLongPress() { + if (autoRefreshLongPressTimer) { + clearTimeout(autoRefreshLongPressTimer); + autoRefreshLongPressTimer = null; + } +} + +function toggleFilePanel() { + if (!isMobileViewport()) return; + setMobileFilePanelCollapsed(!isMobileFilePanelCollapsed); +} + +function syncFilePanelMode() { + if (isMobileViewport()) { + if (!logsById('file-panel-body')?.dataset.initialized) { + logsById('file-panel-body').dataset.initialized = 'true'; + isMobileFilePanelCollapsed = true; + } + setMobileFilePanelCollapsed(isMobileFilePanelCollapsed); + } else { + isMobileFilePanelCollapsed = false; + logsById('file-panel-body')?.classList.remove('mobile-collapsed'); + logsById('file-panel-toggle')?.classList.add('desktop-hidden'); + logsById('file-panel-toggle')?.setAttribute('aria-expanded', 'true'); + } +} + +function setMobileFilePanelCollapsed(collapsed) { + isMobileFilePanelCollapsed = collapsed; + const body = logsById('file-panel-body'); + const toggle = logsById('file-panel-toggle'); + const text = logsById('file-panel-toggle-text'); + if (!body || !toggle || !text) return; + + if (isMobileViewport()) { + toggle.classList.remove('desktop-hidden'); + body.classList.toggle('mobile-collapsed', collapsed); + toggle.setAttribute('aria-expanded', String(!collapsed)); + text.textContent = collapsed ? '展开' : '收起'; + } else { + body.classList.remove('mobile-collapsed'); + toggle.classList.add('desktop-hidden'); + toggle.setAttribute('aria-expanded', 'true'); + text.textContent = '展开'; + } +} + +function extractExtras(entry) { + const hiddenKeys = new Set(['time', 'time_display', 'level', 'msg', 'caller', 'stacktrace', 'raw']); + return Object.entries(entry).filter(([key, value]) => !hiddenKeys.has(key) && value !== null && value !== ''); +} + +function openModal(entry) { + logsById('log-modal-meta').textContent = `${entry.time_display || '-'} · ${entry.level || 'unknown'} · ${entry.caller || '-'}`; + logsById('log-modal-body').textContent = safePrettyJson(entry.raw); + logsById('log-modal').classList.remove('hidden'); +} + +function closeModal() { + logsById('log-modal').classList.add('hidden'); +} + +async function copyText(text) { + try { + await navigator.clipboard.writeText(text); + showToast(I18n?.t('common.copied') || '已复制', 'success'); + } catch (error) { + showToast(I18n?.t('common.copyFailed') || '复制失败', 'error'); + } +} + +async function getErrorMessage(res) { + try { + const data = await res.json(); + return data.detail || data.message || `${res.status}`; + } catch (error) { + return `${res.status}`; + } +} + +function safePrettyJson(raw) { + try { + return JSON.stringify(JSON.parse(raw), null, 2); + } catch (error) { + return raw || ''; + } +} + +function stringifyValue(value) { + if (typeof value === 'string') return value; + return JSON.stringify(value); +} + +function formatDate(value) { + if (!value) return '-'; + const date = new Date(value); + if (Number.isNaN(date.getTime())) return value; + return date.toLocaleString(); +} + +function formatBytes(bytes) { + const size = Number(bytes || 0); + if (size < 1024) return `${size} B`; + const units = ['KB', 'MB', 'GB', 'TB']; + let value = size / 1024; + let unitIndex = 0; + while (value >= 1024 && unitIndex < units.length - 1) { + value /= 1024; + unitIndex += 1; + } + return `${value.toFixed(value >= 10 ? 0 : 1)} ${units[unitIndex]}`; +} + +function escapeHtml(value) { + return String(value ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} diff --git a/_public/static/admin/js/token.js b/_public/static/admin/js/token.js index 219a4c16..ff8877f2 100644 --- a/_public/static/admin/js/token.js +++ b/_public/static/admin/js/token.js @@ -295,7 +295,7 @@ function renderTable() { // Note (Left) const tdNote = document.createElement('td'); - tdNote.className = 'text-left text-gray-500 text-xs truncate max-w-[150px]'; + tdNote.className = 'text-left text-gray-500 text-xs token-note-cell'; tdNote.innerText = item.note || '-'; // Actions (Center) diff --git a/_public/static/admin/pages/cache.html b/_public/static/admin/pages/cache.html index 2f283e75..35476938 100644 --- a/_public/static/admin/pages/cache.html +++ b/_public/static/admin/pages/cache.html @@ -88,60 +88,123 @@

缓存 + +
+
+
+ - - - + + + + - +
- + 文件大小时间Token类型资产数上次清空时间 操作
+
-
-
- - - - - - - - - - - - -
- - Token类型资产数上次清空时间操作
- +
+
第 0 / 0 页 · 共 0 条
+
+ + + + + + +
+
diff --git a/_public/static/admin/pages/logs.html b/_public/static/admin/pages/logs.html new file mode 100644 index 00000000..afc01a17 --- /dev/null +++ b/_public/static/admin/pages/logs.html @@ -0,0 +1,167 @@ + + + + + + + Grok2API - 日志查看 + + + + + + + + + + +
+ +
+
+
+
+

日志查看

+

按文件、级别和关键词快速筛选,并以更易读的方式查看结构化日志。

+
+
+ + +
+
+ +
+
+
当前文件
+
-
+
-
+
+
+
已加载条目
+
0
+
按当前筛选展示
+
+
+
告警 / 错误
+
0
+
warning + error
+
+
+
文件大小
+
0 B
+
磁盘占用
+
+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + + +
+
+
+ +
+ + +
+ + +
+
+
+
+
+ + + + + + + + + + + + + + diff --git a/_public/static/admin/pages/token.html b/_public/static/admin/pages/token.html index bd4692c7..dc757328 100644 --- a/_public/static/admin/pages/token.html +++ b/_public/static/admin/pages/token.html @@ -120,7 +120,8 @@

Token -
+
+
@@ -137,6 +138,7 @@

Token

+
加载中...
@@ -92,7 +92,7 @@

日志
@@ -106,10 +106,10 @@

日志

日志文件

-
0 个日志文件
+
0 个日志文件
+

diff --git a/_public/static/i18n/locales/en.json b/_public/static/i18n/locales/en.json index 5d2dc5d7..3d66cd07 100644 --- a/_public/static/i18n/locales/en.json +++ b/_public/static/i18n/locales/en.json @@ -1,602 +1,850 @@ { - "common": { - "save": "Save", - "cancel": "Cancel", - "confirm": "Confirm", - "delete": "Delete", - "loading": "Loading...", - "invalidKey": "Invalid key", - "connectionFailed": "Connection failed", - "requestFailed": "Request failed", - "operationSuccess": "Operation successful", - "gotIt": "Got it", - "notice": "Notice", - "rateLimitNotice": "The official GROK website update did not expose the rate-limits API, making it impossible to accurately calculate remaining Token usage. Please wait for the official API. Currently the count resets to 8 after auto-refresh.", - "pleaseConfirm": "Please confirm", - "ok": "OK", - "close": "Close", - "error": "Error", - "unknownError": "Unknown error", - "connectionInterrupted": "Connection interrupted", - "taskInProgress": "A task is already in progress", - "taskNoPause": "Current task does not support pausing", - "batchNoPause": "Current batch task does not support pausing", - "waitTaskFinish": "Please wait for the current task to finish", - "pause": "Pause", - "stop": "Stop", - "selected": "Selected", - "items": "items", - "export": "Export", - "refresh": "Refresh", - "load": "Load", - "files": "files", - "view": "View", - "retry": "Retry", - "copy": "Copy", - "edit": "Edit", - "loadFailed": "Load failed", - "loadError": "Load failed: {msg}", - "saveFailed": "Save failed", - "saveError": "Save error: {msg}", - "deleteFailed": "Delete failed", - "deleteSuccess": "Deleted successfully", - "notConnected": "Not connected", - "connecting": "Connecting", - "connected": "Connected", - "connectionError": "Connection error", - "generating": "Generating", - "done": "Done", - "failed": "Failed", - "stopped": "Stopped", - "terminated": "Terminated", - "sending": "Sending", - "fileReadFailed": "File read failed", - "noFileSelected": "No file selected", - "configurePublicKey": "Please configure Public Key first", - "enterPrompt": "Please enter a prompt", - "enterContent": "Please enter content", - "alreadyRunning": "Already running", - "createTaskFailed": "Failed to create task", - "copyFailed": "Copy failed", - "copied": "Copied", - "generationFailed": "Generation failed", - "partialError": "Partial error", - "loadingInProgress": "Loading in progress, please wait", - "cleaningInProgress": "Cleaning in progress, please wait", - "noTokenSelected": "No Token selected", - "clearCache": "Clear cache", - "failureDetails": "Failure Details", - "retryFailed": "Retry failed items" - }, - "nav": { - "tokenManage": "Token Mgmt", - "configManage": "Config Mgmt", - "cacheManage": "Cache Mgmt", - "logsView": "Logs", - "chat": "Chat", - "imagine": "Imagine Gallery", - "video": "Video Generation", - "voice": "LiveKit Voice", - "feedback": "Feedback", - "logout": "Logout", - "adminPanel": "Admin Panel", - "storageMode": "Storage Mode" - }, - "login": { - "adminPageTitle": "Grok2API - Login", - "adminTitle": "Admin Panel", - "adminSubtitle": "Enter admin password to continue", - "adminPlaceholder": "Admin Password (app_key)", - "continue": "Continue", - "publicPageTitle": "Grok2API - Public", - "publicTitle": "Public Access", - "publicSubtitle": "Enter Public Key (leave empty if not configured)", - "publicPlaceholder": "Public Key", - "enter": "Enter", - "goToAdmin": "Go to Admin Panel" - }, - "config": { - "pageTitle": "Grok2API - Config", - "title": "Configuration", - "subtitle": "Manage API keys and system parameters.", - "saving": "Saving...", - "saved": "Saved", - "configSaved": "Configuration saved", - "invalidJson": "Invalid JSON: {field}", - "appKeyRequired": "app_key cannot be empty (admin password)", - "flaresolverrRequired": "FlareSolverr URL is required when auto-refresh is enabled", - "generate": "Generate", - "publicAccess": "Public Access", - "noDesc": "No description available. Please refer to the documentation.", - "sectionFallback": "{section} Settings", - "sections": { - "app": "App Settings", - "proxy": "Proxy Config", - "retry": "Retry Strategy", - "chat": "Chat Config", - "video": "Video Config", - "image": "Image Config", - "imagine_fast": "Imagine Fast Config", - "asset": "Asset Config", - "voice": "Voice Config", - "token": "Token Pool Mgmt", - "cache": "Cache Mgmt", - "nsfw": "NSFW Config", - "usage": "Usage Config" + "common": { + "save": "Save", + "cancel": "Cancel", + "confirm": "Confirm", + "delete": "Delete", + "loading": "Loading...", + "invalidKey": "Invalid key", + "connectionFailed": "Connection failed", + "requestFailed": "Request failed", + "operationSuccess": "Operation successful", + "gotIt": "Got it", + "notice": "Notice", + "rateLimitNotice": "The official GROK website update did not expose the rate-limits API, making it impossible to accurately calculate remaining Token usage. Please wait for the official API. Currently the count resets to 8 after auto-refresh.", + "pleaseConfirm": "Please confirm", + "ok": "OK", + "close": "Close", + "error": "Error", + "unknownError": "Unknown error", + "connectionInterrupted": "Connection interrupted", + "taskInProgress": "A task is already in progress", + "taskNoPause": "Current task does not support pausing", + "batchNoPause": "Current batch task does not support pausing", + "waitTaskFinish": "Please wait for the current task to finish", + "pause": "Pause", + "stop": "Stop", + "selected": "Selected", + "items": "items", + "export": "Export", + "refresh": "Refresh", + "load": "Load", + "files": "files", + "view": "View", + "retry": "Retry", + "copy": "Copy", + "edit": "Edit", + "loadFailed": "Load failed", + "loadError": "Load failed: {msg}", + "saveFailed": "Save failed", + "saveError": "Save error: {msg}", + "deleteFailed": "Delete failed", + "deleteSuccess": "Deleted successfully", + "notConnected": "Not connected", + "connecting": "Connecting", + "connected": "Connected", + "connectionError": "Connection error", + "generating": "Generating", + "done": "Done", + "failed": "Failed", + "stopped": "Stopped", + "terminated": "Terminated", + "sending": "Sending", + "fileReadFailed": "File read failed", + "noFileSelected": "No file selected", + "configurePublicKey": "Please configure Public Key first", + "enterPrompt": "Please enter a prompt", + "enterContent": "Please enter content", + "alreadyRunning": "Already running", + "createTaskFailed": "Failed to create task", + "copyFailed": "Copy failed", + "copied": "Copied", + "generationFailed": "Generation failed", + "partialError": "Partial error", + "loadingInProgress": "Loading in progress, please wait", + "cleaningInProgress": "Cleaning in progress, please wait", + "noTokenSelected": "No Token selected", + "clearCache": "Clear cache", + "failureDetails": "Failure Details", + "retryFailed": "Retry failed items" }, - "sectionDescs": { - "proxy": "Incorrect configuration will cause 403 errors. The IP of the first request to Grok must match the IP used to obtain the CF Clearance. Subsequent IP changes will not cause 403." + "nav": { + "tokenManage": "Token Mgmt", + "configManage": "Config Mgmt", + "cacheManage": "Cache Mgmt", + "logsView": "Logs", + "chat": "Chat", + "imagine": "Imagine Gallery", + "video": "Video Generation", + "voice": "LiveKit Voice", + "feedback": "Feedback", + "logout": "Logout", + "adminPanel": "Admin Panel", + "storageMode": "Storage Mode" }, - "fields": { - "app": { - "api_key": {"title": "API Key", "desc": "Token for calling Grok2API services (optional, supports multiple, comma-separated or array)."}, - "app_key": {"title": "Admin Password", "desc": "Password for logging into the Grok2API admin panel (required)."}, - "function_enabled": {"title": "Enable Function Access", "desc": "Whether to enable the function access entry (disabled means function pages are inaccessible)."}, - "function_key": {"title": "Function Password", "desc": "Access password for function pages (optional)."}, - "app_url": {"title": "App URL", "desc": "External access URL for the Grok2API service, used for file link access."}, - "image_format": {"title": "Image Format", "desc": "Default image format (url or base64)."}, - "video_format": {"title": "Video Format", "desc": "Default video format (html or url, url is the processed link)."}, - "temporary": {"title": "Temporary Chat", "desc": "Whether to enable temporary chat mode by default."}, - "disable_memory": {"title": "Disable Memory", "desc": "Whether to disable Grok memory feature by default."}, - "stream": {"title": "Streaming", "desc": "Whether to enable streaming output by default."}, - "thinking": {"title": "Chain of Thought", "desc": "Whether to enable chain of thought output by default."}, - "dynamic_statsig": {"title": "Dynamic Fingerprint", "desc": "Whether to enable dynamic Statsig fingerprint generation by default."}, - "custom_instruction": {"title": "Custom Instruction", "desc": "Multi-line text passed through as Grok request parameter customPersonality."}, - "filter_tags": {"title": "Filter Tags", "desc": "Configure automatic filtering of special tags in Grok responses."} - }, - "proxy": { - "base_proxy_url": {"title": "Base Proxy URL", "desc": "Base service address for proxying requests to Grok website."}, - "asset_proxy_url": {"title": "Asset Proxy URL", "desc": "Address for proxying static resource (image/video) requests to Grok website."}, - "skip_proxy_ssl_verify": {"title": "Skip Proxy SSL Verify", "desc": "Enable when your proxy uses a self-signed certificate (only bypasses proxy certificate validation)."}, - "enabled": {"title": "Enable CF Auto-Refresh", "desc": "When enabled, automatically obtains cf_clearance through FlareSolverr."}, - "flaresolverr_url": {"title": "FlareSolverr URL", "desc": "HTTP address of FlareSolverr service (e.g. http://flaresolverr:8191)."}, - "refresh_interval": {"title": "Refresh Interval (sec)", "desc": "Time interval for auto-refreshing cf_clearance, recommended minimum 300 seconds."}, - "timeout": {"title": "Challenge Timeout (sec)", "desc": "Maximum time to wait for FlareSolverr to solve CF challenge."}, - "cf_clearance": {"title": "CF Clearance", "desc": "Cloudflare Clearance Cookie for bypassing anti-bot verification. Automatically managed when auto-refresh is enabled."}, - "browser": {"title": "Browser Fingerprint", "desc": "curl_cffi browser fingerprint identifier (e.g. chrome136). Automatically managed when auto-refresh is enabled."}, - "user_agent": {"title": "User-Agent", "desc": "HTTP request User-Agent string. Automatically managed when auto-refresh is enabled."} - }, - "retry": { - "max_retry": {"title": "Max Retries", "desc": "Maximum number of retries when Grok service request fails."}, - "retry_status_codes": {"title": "Retry Status Codes", "desc": "HTTP status codes that trigger a retry."}, - "reset_session_status_codes": {"title": "Session Reset Status Codes", "desc": "HTTP status codes that trigger session rebuild (used for proxy rotation)."}, - "retry_backoff_base": {"title": "Backoff Base", "desc": "Base delay (seconds) for retry backoff."}, - "retry_backoff_factor": {"title": "Backoff Factor", "desc": "Exponential multiplier for retry backoff."}, - "retry_backoff_max": {"title": "Backoff Max", "desc": "Maximum delay (seconds) for a single retry wait."}, - "retry_budget": {"title": "Backoff Budget", "desc": "Maximum total retry time (seconds) for a single request."} - }, - "chat": { - "concurrent": {"title": "Concurrency Limit", "desc": "Reverse API concurrency limit."}, - "timeout": {"title": "Request Timeout", "desc": "Reverse API timeout (seconds)."}, - "stream_timeout": {"title": "Stream Idle Timeout", "desc": "Streaming idle timeout (seconds)."} - }, - "video": { - "concurrent": {"title": "Concurrency Limit", "desc": "Reverse API concurrency limit."}, - "timeout": {"title": "Request Timeout", "desc": "Reverse API timeout (seconds)."}, - "stream_timeout": {"title": "Stream Idle Timeout", "desc": "Streaming idle timeout (seconds)."} - }, - "image": { - "timeout": {"title": "Request Timeout", "desc": "WebSocket request timeout (seconds)."}, - "stream_timeout": {"title": "Stream Idle Timeout", "desc": "WebSocket streaming idle timeout (seconds)."}, - "final_timeout": {"title": "Final Image Timeout", "desc": "Timeout (seconds) to wait for final image after receiving medium image."}, - "blocked_grace_seconds": {"title": "Review Grace Seconds", "desc": "Grace seconds for suspected review/censorship after receiving medium image (default 10, configurable)."}, - "nsfw": {"title": "NSFW Mode", "desc": "Whether to enable NSFW for WebSocket requests."}, - "medium_min_bytes": {"title": "Medium Min Bytes", "desc": "Minimum byte size for medium quality image detection."}, - "final_min_bytes": {"title": "Final Min Bytes", "desc": "Minimum byte size for final image detection (typically JPG > 100KB)."}, - "blocked_parallel_enabled": {"title": "Enable Parallel Compensation", "desc": "Whether to enable parallel compensation when suspected review/blocking occurs."}, - "blocked_parallel_attempts": {"title": "Block Compensation Concurrency", "desc": "Number of parallel compensation attempts when suspected review/blocking prevents final image."} - }, - "imagine_fast": { - "n": {"title": "Generation Count", "desc": "Server-side unified generation count for grok-imagine-1.0-fast only (1-10)."}, - "size": {"title": "Image Size", "desc": "Server-side unified size for grok-imagine-1.0-fast only."}, - "response_format": {"title": "Response Format", "desc": "Server-side unified response format for grok-imagine-1.0-fast only."} - }, - "asset": { - "upload_concurrent": {"title": "Upload Concurrency", "desc": "Maximum concurrent uploads. Recommended: 30."}, - "upload_timeout": {"title": "Upload Timeout", "desc": "Upload timeout (seconds). Recommended: 60."}, - "download_concurrent": {"title": "Download Concurrency", "desc": "Maximum concurrent downloads. Recommended: 30."}, - "download_timeout": {"title": "Download Timeout", "desc": "Download timeout (seconds). Recommended: 60."}, - "list_concurrent": {"title": "Query Concurrency", "desc": "Maximum concurrent asset queries. Recommended: 10."}, - "list_timeout": {"title": "Query Timeout", "desc": "Asset query timeout (seconds). Recommended: 60."}, - "list_batch_size": {"title": "Query Batch Size", "desc": "Number of tokens per query batch. Recommended: 10."}, - "delete_concurrent": {"title": "Delete Concurrency", "desc": "Maximum concurrent asset deletions. Recommended: 10."}, - "delete_timeout": {"title": "Delete Timeout", "desc": "Asset deletion timeout (seconds). Recommended: 60."}, - "delete_batch_size": {"title": "Delete Batch Size", "desc": "Number of tokens per delete batch. Recommended: 10."} - }, - "voice": { - "timeout": {"title": "Request Timeout", "desc": "Voice request timeout (seconds)."} - }, - "token": { - "auto_refresh": {"title": "Auto Refresh", "desc": "Whether to enable automatic Token refresh."}, - "refresh_interval_hours": {"title": "Refresh Interval", "desc": "Time interval (hours) for regular Token refresh."}, - "super_refresh_interval_hours": {"title": "Super Refresh Interval", "desc": "Time interval (hours) for Super Token refresh."}, - "fail_threshold": {"title": "Failure Threshold", "desc": "Number of consecutive failures before a Token is marked unavailable."}, - "save_delay_ms": {"title": "Save Delay", "desc": "Delay (ms) for batching Token change writes."}, - "usage_flush_interval_sec": {"title": "Usage Flush Interval", "desc": "Minimum interval (seconds) for writing usage data to database."}, - "reload_interval_sec": {"title": "Sync Interval", "desc": "Token state refresh interval (seconds) in multi-worker scenarios."} - }, - "cache": { - "enable_auto_clean": {"title": "Auto Clean", "desc": "Whether to enable automatic cache cleanup based on size limit."}, - "limit_mb": {"title": "Clean Threshold", "desc": "Cache size threshold (MB) that triggers cleanup."} - }, - "nsfw": { - "concurrent": {"title": "Concurrency Limit", "desc": "Maximum concurrent requests for batch NSFW enable. Recommended: 10."}, - "batch_size": {"title": "Batch Size", "desc": "Batch processing count for NSFW enable. Recommended: 50."}, - "timeout": {"title": "Request Timeout", "desc": "Timeout for NSFW-related requests (seconds). Recommended: 60."} - }, - "usage": { - "concurrent": {"title": "Concurrency Limit", "desc": "Maximum concurrent requests for batch usage refresh. Recommended: 10."}, - "batch_size": {"title": "Batch Size", "desc": "Batch processing count for usage refresh. Recommended: 50."}, - "timeout": {"title": "Request Timeout", "desc": "Usage query timeout (seconds). Recommended: 60."} - } - } - }, - "logs": { - "pageTitle": "Grok2API - Logs", - "title": "Log Viewer", - "subtitle": "Filter structured logs by file, level, and keyword with a cleaner reading experience.", - "filesTitle": "Log Files", - "rawTitle": "Raw Log", - "empty": "No matching log records", - "filters": { - "file": "Log File", - "level": "Level", - "limit": "Rows", - "keyword": "Keyword", - "keywordPlaceholder": "traceID / path / error keyword", - "apply": "Apply", - "reset": "Reset" + "login": { + "adminPageTitle": "Grok2API - Login", + "adminTitle": "Admin Panel", + "adminSubtitle": "Enter admin password to continue", + "adminPlaceholder": "Admin Password (app_key)", + "continue": "Continue", + "publicPageTitle": "Grok2API - Public", + "publicTitle": "Public Access", + "publicSubtitle": "Enter Public Key (leave empty if not configured)", + "publicPlaceholder": "Public Key", + "enter": "Enter", + "goToAdmin": "Go to Admin Panel" + }, + "config": { + "pageTitle": "Grok2API - Config", + "title": "Configuration", + "subtitle": "Manage API keys and system parameters.", + "saving": "Saving...", + "saved": "Saved", + "configSaved": "Configuration saved", + "invalidJson": "Invalid JSON: {field}", + "appKeyRequired": "app_key cannot be empty (admin password)", + "flaresolverrRequired": "FlareSolverr URL is required when auto-refresh is enabled", + "generate": "Generate", + "publicAccess": "Public Access", + "noDesc": "No description available. Please refer to the documentation.", + "sectionFallback": "{section} Settings", + "sections": { + "app": "App Settings", + "proxy": "Proxy Config", + "retry": "Retry Strategy", + "chat": "Chat Config", + "video": "Video Config", + "image": "Image Config", + "imagine_fast": "Imagine Fast Config", + "asset": "Asset Config", + "voice": "Voice Config", + "token": "Token Pool Mgmt", + "cache": "Cache Mgmt", + "nsfw": "NSFW Config", + "usage": "Usage Config" + }, + "sectionDescs": { + "proxy": "Incorrect configuration will cause 403 errors. The IP of the first request to Grok must match the IP used to obtain the CF Clearance. Subsequent IP changes will not cause 403." + }, + "fields": { + "app": { + "api_key": { + "title": "API Key", + "desc": "Token for calling Grok2API services (optional, supports multiple, comma-separated or array)." + }, + "app_key": { + "title": "Admin Password", + "desc": "Password for logging into the Grok2API admin panel (required)." + }, + "function_enabled": { + "title": "Enable Function Access", + "desc": "Whether to enable the function access entry (disabled means function pages are inaccessible)." + }, + "function_key": { + "title": "Function Password", + "desc": "Access password for function pages (optional)." + }, + "app_url": { + "title": "App URL", + "desc": "External access URL for the Grok2API service, used for file link access." + }, + "image_format": { + "title": "Image Format", + "desc": "Default image format (url or base64)." + }, + "video_format": { + "title": "Video Format", + "desc": "Default video format (html or url, url is the processed link)." + }, + "temporary": { + "title": "Temporary Chat", + "desc": "Whether to enable temporary chat mode by default." + }, + "disable_memory": { + "title": "Disable Memory", + "desc": "Whether to disable Grok memory feature by default." + }, + "stream": { + "title": "Streaming", + "desc": "Whether to enable streaming output by default." + }, + "thinking": { + "title": "Chain of Thought", + "desc": "Whether to enable chain of thought output by default." + }, + "dynamic_statsig": { + "title": "Dynamic Fingerprint", + "desc": "Whether to enable dynamic Statsig fingerprint generation by default." + }, + "custom_instruction": { + "title": "Custom Instruction", + "desc": "Multi-line text passed through as Grok request parameter customPersonality." + }, + "filter_tags": { + "title": "Filter Tags", + "desc": "Configure automatic filtering of special tags in Grok responses." + } + }, + "proxy": { + "base_proxy_url": { + "title": "Base Proxy URL", + "desc": "Base service address for proxying requests to Grok website." + }, + "asset_proxy_url": { + "title": "Asset Proxy URL", + "desc": "Address for proxying static resource (image/video) requests to Grok website." + }, + "skip_proxy_ssl_verify": { + "title": "Skip Proxy SSL Verify", + "desc": "Enable when your proxy uses a self-signed certificate (only bypasses proxy certificate validation)." + }, + "enabled": { + "title": "Enable CF Auto-Refresh", + "desc": "When enabled, automatically obtains cf_clearance through FlareSolverr." + }, + "flaresolverr_url": { + "title": "FlareSolverr URL", + "desc": "HTTP address of FlareSolverr service (e.g. http://flaresolverr:8191)." + }, + "refresh_interval": { + "title": "Refresh Interval (sec)", + "desc": "Time interval for auto-refreshing cf_clearance, recommended minimum 300 seconds." + }, + "timeout": { + "title": "Challenge Timeout (sec)", + "desc": "Maximum time to wait for FlareSolverr to solve CF challenge." + }, + "cf_clearance": { + "title": "CF Clearance", + "desc": "Cloudflare Clearance Cookie for bypassing anti-bot verification. Automatically managed when auto-refresh is enabled." + }, + "browser": { + "title": "Browser Fingerprint", + "desc": "curl_cffi browser fingerprint identifier (e.g. chrome136). Automatically managed when auto-refresh is enabled." + }, + "user_agent": { + "title": "User-Agent", + "desc": "HTTP request User-Agent string. Automatically managed when auto-refresh is enabled." + } + }, + "retry": { + "max_retry": { + "title": "Max Retries", + "desc": "Maximum number of retries when Grok service request fails." + }, + "retry_status_codes": { + "title": "Retry Status Codes", + "desc": "HTTP status codes that trigger a retry." + }, + "reset_session_status_codes": { + "title": "Session Reset Status Codes", + "desc": "HTTP status codes that trigger session rebuild (used for proxy rotation)." + }, + "retry_backoff_base": { + "title": "Backoff Base", + "desc": "Base delay (seconds) for retry backoff." + }, + "retry_backoff_factor": { + "title": "Backoff Factor", + "desc": "Exponential multiplier for retry backoff." + }, + "retry_backoff_max": { + "title": "Backoff Max", + "desc": "Maximum delay (seconds) for a single retry wait." + }, + "retry_budget": { + "title": "Backoff Budget", + "desc": "Maximum total retry time (seconds) for a single request." + } + }, + "chat": { + "concurrent": { + "title": "Concurrency Limit", + "desc": "Reverse API concurrency limit." + }, + "timeout": { + "title": "Request Timeout", + "desc": "Reverse API timeout (seconds)." + }, + "stream_timeout": { + "title": "Stream Idle Timeout", + "desc": "Streaming idle timeout (seconds)." + } + }, + "video": { + "concurrent": { + "title": "Concurrency Limit", + "desc": "Reverse API concurrency limit." + }, + "timeout": { + "title": "Request Timeout", + "desc": "Reverse API timeout (seconds)." + }, + "stream_timeout": { + "title": "Stream Idle Timeout", + "desc": "Streaming idle timeout (seconds)." + } + }, + "image": { + "timeout": { + "title": "Request Timeout", + "desc": "WebSocket request timeout (seconds)." + }, + "stream_timeout": { + "title": "Stream Idle Timeout", + "desc": "WebSocket streaming idle timeout (seconds)." + }, + "final_timeout": { + "title": "Final Image Timeout", + "desc": "Timeout (seconds) to wait for final image after receiving medium image." + }, + "blocked_grace_seconds": { + "title": "Review Grace Seconds", + "desc": "Grace seconds for suspected review/censorship after receiving medium image (default 10, configurable)." + }, + "nsfw": { + "title": "NSFW Mode", + "desc": "Whether to enable NSFW for WebSocket requests." + }, + "medium_min_bytes": { + "title": "Medium Min Bytes", + "desc": "Minimum byte size for medium quality image detection." + }, + "final_min_bytes": { + "title": "Final Min Bytes", + "desc": "Minimum byte size for final image detection (typically JPG > 100KB)." + }, + "blocked_parallel_enabled": { + "title": "Enable Parallel Compensation", + "desc": "Whether to enable parallel compensation when suspected review/blocking occurs." + }, + "blocked_parallel_attempts": { + "title": "Block Compensation Concurrency", + "desc": "Number of parallel compensation attempts when suspected review/blocking prevents final image." + } + }, + "imagine_fast": { + "n": { + "title": "Generation Count", + "desc": "Server-side unified generation count for grok-imagine-1.0-fast only (1-10)." + }, + "size": { + "title": "Image Size", + "desc": "Server-side unified size for grok-imagine-1.0-fast only." + }, + "response_format": { + "title": "Response Format", + "desc": "Server-side unified response format for grok-imagine-1.0-fast only." + } + }, + "asset": { + "upload_concurrent": { + "title": "Upload Concurrency", + "desc": "Maximum concurrent uploads. Recommended: 30." + }, + "upload_timeout": { + "title": "Upload Timeout", + "desc": "Upload timeout (seconds). Recommended: 60." + }, + "download_concurrent": { + "title": "Download Concurrency", + "desc": "Maximum concurrent downloads. Recommended: 30." + }, + "download_timeout": { + "title": "Download Timeout", + "desc": "Download timeout (seconds). Recommended: 60." + }, + "list_concurrent": { + "title": "Query Concurrency", + "desc": "Maximum concurrent asset queries. Recommended: 10." + }, + "list_timeout": { + "title": "Query Timeout", + "desc": "Asset query timeout (seconds). Recommended: 60." + }, + "list_batch_size": { + "title": "Query Batch Size", + "desc": "Number of tokens per query batch. Recommended: 10." + }, + "delete_concurrent": { + "title": "Delete Concurrency", + "desc": "Maximum concurrent asset deletions. Recommended: 10." + }, + "delete_timeout": { + "title": "Delete Timeout", + "desc": "Asset deletion timeout (seconds). Recommended: 60." + }, + "delete_batch_size": { + "title": "Delete Batch Size", + "desc": "Number of tokens per delete batch. Recommended: 10." + } + }, + "voice": { + "timeout": { + "title": "Request Timeout", + "desc": "Voice request timeout (seconds)." + } + }, + "token": { + "auto_refresh": { + "title": "Auto Refresh", + "desc": "Whether to enable automatic Token refresh." + }, + "refresh_interval_hours": { + "title": "Refresh Interval", + "desc": "Time interval (hours) for regular Token refresh." + }, + "super_refresh_interval_hours": { + "title": "Super Refresh Interval", + "desc": "Time interval (hours) for Super Token refresh." + }, + "fail_threshold": { + "title": "Failure Threshold", + "desc": "Number of consecutive failures before a Token is marked unavailable." + }, + "save_delay_ms": { + "title": "Save Delay", + "desc": "Delay (ms) for batching Token change writes." + }, + "usage_flush_interval_sec": { + "title": "Usage Flush Interval", + "desc": "Minimum interval (seconds) for writing usage data to database." + }, + "reload_interval_sec": { + "title": "Sync Interval", + "desc": "Token state refresh interval (seconds) in multi-worker scenarios." + } + }, + "cache": { + "enable_auto_clean": { + "title": "Auto Clean", + "desc": "Whether to enable automatic cache cleanup based on size limit." + }, + "limit_mb": { + "title": "Clean Threshold", + "desc": "Cache size threshold (MB) that triggers cleanup." + } + }, + "nsfw": { + "concurrent": { + "title": "Concurrency Limit", + "desc": "Maximum concurrent requests for batch NSFW enable. Recommended: 10." + }, + "batch_size": { + "title": "Batch Size", + "desc": "Batch processing count for NSFW enable. Recommended: 50." + }, + "timeout": { + "title": "Request Timeout", + "desc": "Timeout for NSFW-related requests (seconds). Recommended: 60." + } + }, + "usage": { + "concurrent": { + "title": "Concurrency Limit", + "desc": "Maximum concurrent requests for batch usage refresh. Recommended: 10." + }, + "batch_size": { + "title": "Batch Size", + "desc": "Batch processing count for usage refresh. Recommended: 50." + }, + "timeout": { + "title": "Request Timeout", + "desc": "Usage query timeout (seconds). Recommended: 60." + } + } + } + }, + "logs": { + "pageTitle": "Grok2API - Logs", + "title": "Log Viewer", + "subtitle": "Filter structured logs by file, level, and keyword with a cleaner reading experience.", + "filesTitle": "Log Files", + "rawTitle": "Raw Log", + "empty": "No matching log records", + "filters": { + "file": "Log File", + "level": "Level", + "limit": "Rows", + "keyword": "Keyword", + "keywordPlaceholder": "traceID / path / error keyword", + "apply": "Apply", + "reset": "Reset", + "excludeAdminRoutes": "Hide `/v1/admin/*` only on this page" + }, + "levels": { + "all": "All Levels" + }, + "stats": { + "file": "Current File", + "entries": "Loaded Rows", + "entriesHint": "Shown with current filters", + "errorWarn": "Warnings / Errors", + "errorWarnHint": "warning + error", + "size": "File Size", + "sizeHint": "Disk usage" + }, + "loadFilesFailed": "Failed to load log files", + "loadFailed": "Failed to load logs", + "noFiles": "No log files", + "fileCount": "{count} log files", + "deleteNone": "Select log files to delete first", + "deleteConfirm": "Delete {count} log files?", + "deleteFailed": "Failed to delete logs", + "deleteResult": "Deleted {deleted} log files, {failed} failed", + "toggle": { + "expand": "Expand", + "collapse": "Collapse" + }, + "autoRefresh": { + "buttonAria": "Auto refresh", + "off": "Auto refresh: Off", + "offShort": "Off", + "interval": "Auto refresh: {seconds}s", + "intervalLabel": "{seconds}s", + "changed": "Auto refresh: {label}", + "disabled": "Auto refresh disabled" + }, + "deleteSelected": "Delete Selected" + }, + "token": { + "pageTitle": "Grok2API - Token Mgmt", + "title": "Token List", + "subtitle": "Manage the Grok2API Token service pool.", + "import": "Import", + "add": "Add", + "statTotal": "Total Tokens", + "statActive": "Active", + "statCooling": "Rate Limited", + "statInvalid": "Invalid", + "statChatQuota": "Chat Remaining", + "statImageQuota": "Image Remaining", + "statVideoQuota": "Video Remaining", + "statVideoUnavailable": "N/A", + "statTotalCalls": "Total Calls", + "tabAll": "All", + "tabActive": "Active", + "tabCooling": "Rate Limited", + "tabExpired": "Expired", + "tabNsfw": "NSFW On", + "tabNoNsfw": "NSFW Off", + "statusFilterAria": "Token status filter", + "tableToken": "Token", + "tableType": "Type", + "tableStatus": "Status", + "tableQuota": "Quota", + "tableNote": "Note", + "tableActions": "Actions", + "refreshStatus": "Refresh status", + "emptyState": "No Tokens yet. Click Import or Add above.", + "emptyFilterState": "No results for current filter. Try a different filter.", + "selectPage": "Select page", + "selectAllFiltered": "Select all", + "clearSelection": "Clear selection", + "prevPage": "Prev", + "nextPage": "Next", + "perPage": "{size} / page", + "pagination": "Page {current} / {total} \u00b7 {count} items", + "batchExport": "Export", + "batchRefresh": "Refresh", + "batchDisable": "Disable", + "batchEnable": "Enable", + "batchDelete": "Delete", + "importTitle": "Batch Import Tokens", + "importTargetPool": "Target Pool", + "importTokenList": "Token List (one per line)", + "importPlaceholder": "Paste Tokens, one per line...", + "startImport": "Start Import", + "editTitle": "Edit Token", + "addTitle": "Add Token", + "editType": "Type", + "editQuota": "Quota", + "editNote": "Note", + "editNotePlaceholder": "Optional note", + "tokenEmpty": "Token cannot be empty", + "tokenExists": "Token already exists", + "confirmDelete": "Are you sure you want to delete this Token?", + "confirmDisable": "Are you sure you want to disable this Token?\n{token}", + "confirmEnable": "Are you sure you want to enable this Token?\n{token}", + "confirmBatchDelete": "Are you sure you want to delete {count} selected Tokens?", + "confirmBatchDisable": "Are you sure you want to disable {count} selected Tokens?", + "confirmBatchEnable": "Are you sure you want to enable {count} selected Tokens?", + "disableToken": "Disable", + "enableToken": "Enable", + "disableDone": "Token disabled", + "enableDone": "Token enabled", + "batchDisableDone": "Batch disable complete", + "batchEnableDone": "Batch enable complete", + "noTokenToDisable": "All selected Tokens are already disabled", + "noTokenToEnable": "All selected Tokens are already enabled", + "deleteDone": "Delete complete", + "refreshDone": "Refresh complete", + "refreshSuccess": "Refresh successful", + "refreshFailed": "Refresh failed", + "refreshError": "Refresh failed: {msg}", + "requestError": "Request error", + "notValidJson": "Response is not valid JSON (HTTP {status})", + "listEmpty": "List is empty", + "stopRefresh": "Refresh stopped", + "stopDelete": "Delete stopped", + "stopNsfw": "NSFW stopped", + "nsfwConfirm": "Enable NSFW mode for {count} selected Tokens?", + "nsfwEnable": "Enable NSFW", + "nsfwDone": "NSFW enable complete", + "nsfwResult": "NSFW enable complete: {ok} succeeded, {fail} failed", + "nsfwFailed": "Enable failed: {msg}", + "emptyResponse": "Empty response (HTTP {status})" + }, + "cache": { + "pageTitle": "Grok2API - Cache Mgmt", + "title": "Cache Management", + "subtitle": "Manage local resources and online asset cache.", + "localImages": "Local Images", + "localVideos": "Local Videos", + "onlineAssets": "Online Assets", + "tableFile": "File", + "tableSize": "Size", + "tableTime": "Time", + "tableActions": "Actions", + "tableToken": "Token", + "tableType": "Type", + "tableAssetCount": "Asset Count", + "tableLastClear": "Last Cleared", + "notLoaded": "Not loaded", + "noAccounts": "No accounts available", + "noFiles": "No files", + "noTokenAvailable": "No Token available", + "cannotConnect": "Cannot connect", + "lastClear": "Last cleared: {time}", + "clear": "Clear", + "clean": "Clean", + "confirmClearImage": "Clear all local image cache?", + "confirmClearVideo": "Clear all local video cache?", + "clearSuccess": "Cleanup successful, freed {size} MB", + "clearFailed": "Cleanup failed", + "confirmDeleteFile": "Are you sure you want to delete this file?", + "confirmBatchDeleteFiles": "Are you sure you want to delete {count} selected files?", + "noFilesSelected": "No files selected", + "deletedFiles": "Deleted {count} files", + "deleteResult": "Delete complete: {success} succeeded, {failed} failed", + "loadStatsFailed": "Failed to load statistics", + "selectAccountsToLoad": "Please select accounts to load", + "selectAccountsToClear": "Please select accounts to clear", + "confirmClearAccounts": "Clear online assets for {count} selected accounts?", + "confirmClearAccount": "Clear online assets for account {label}?", + "batchCleanInProgress": "Batch cleaning online assets, please wait...", + "cleanInProgress": "Cleaning online assets, please wait...", + "batchCleanDone": "Batch cleanup complete", + "cleanResult": "Cleanup complete (succeeded: {success}, failed: {failed})", + "cleanFailedEntry": "Cleanup failed", + "requestTimeout": "Request timed out or failed", + "loadDone": "Load complete", + "loadingStatus": "Loading", + "loadStopped": "Load stopped", + "loadFailedMsg": "Load failed: {msg}", + "stoppedLoadRequests": "Remaining load requests stopped", + "cleanStopped": "Clean stopped", + "cleanFailedMsg": "Clean failed: {msg}", + "stoppedCleanRequests": "Remaining clean requests stopped" + }, + "chat": { + "pageTitle": "Grok2API - Chat", + "title": "Chat", + "subtitle": "Chat with Grok via the chat API", + "sessions": "Sessions", + "newChat": "New", + "collapseSidebar": "Collapse", + "expandSidebar": "Expand", + "sessionList": "Session list", + "newSession": "New Session", + "ready": "Ready", + "emptyState": "Type a message to start a conversation.", + "uploadFile": "Upload file", + "placeholder": "Ask anything", + "settings": "Settings", + "send": "Send", + "temperature": "Temperature", + "topP": "Top P", + "systemPrompt": "System Prompt", + "systemPromptPlaceholder": "Optional, set system prompt", + "fileLabel": "[File]", + "compositeContent": "[Composite]", + "storageFull": "Local storage is full, some sessions were not saved", + "attachmentNoEdit": "Attachment messages cannot be edited yet", + "saveEdit": "Save", + "saveEditTitle": "Save edit", + "cancelEdit": "Cancel", + "cancelEditTitle": "Cancel edit", + "contentEmpty": "Content cannot be empty", + "editMessage": "Edit", + "editMessageTitle": "Edit message content", + "regenerate": "Regenerate", + "regenerateTitle": "Regenerate response from here", + "retryTitle": "Retry last response", + "editAnswer": "Edit", + "editAnswerTitle": "Edit response content", + "copyAnswer": "Copy", + "copyAnswerTitle": "Copy response content", + "feedback": "Feedback", + "feedbackTitle": "Feedback to Grok2API", + "noContentToCopy": "No content to copy", + "noChatToRetry": "No conversation to retry", + "requestFailedCheck": "Request failed, please check service status", + "requestFailedStatus": "Request failed: {status}", + "clickRetry": "Click to retry", + "thinking": "Thinking", + "thinkingSec": "Thinking {sec}s", + "thought": "Thought", + "thinkLabel": "Thinking", + "empty": "(empty)", + "toolWebSearch": "Web search", + "toolImageSearch": "Image search", + "toolAgentThink": "Reasoning", + "toolDefault": "Tool" + }, + "imagine": { + "pageTitle": "Grok2API - Imagine Gallery", + "title": "Imagine Gallery", + "subtitle": "Continuously generate images via WebSocket, real-time base64 gallery.", + "genSettings": "Generation Settings", + "prompt": "Prompt", + "promptPlaceholder": "Describe the image you want, e.g.: futuristic city neon rain at night, wide angle photography", + "autoFollow": "Auto Follow", + "autoFollowDesc": "Scroll to latest", + "autoSave": "Auto Save", + "autoSaveDesc": "Auto-save images", + "aspectRatio": "Aspect Ratio", + "ratio2_3": "2:3 Portrait", + "ratio1_1": "1:1 Square", + "ratio3_2": "3:2 Landscape", + "ratio16_9": "16:9 Wide", + "ratio9_16": "9:16 Tall", + "concurrent": "Concurrency", + "tasks": "{n} tasks", + "concurrentTask1": "1 task", + "concurrentTask2": "2 tasks", + "concurrentTask3": "3 tasks", + "autoFilter": "Auto Filter", + "autoFilterDesc": "Filter substandard images", + "nsfwLabel": "NSFW", + "nsfwOn": "On", + "nsfwOff": "Off", + "saveLocation": "Save Location", + "browserDefault": "Browser default location", + "reverseOrder": "Reverse Order", + "reverseOrderDesc": "Show newest on top", + "connectionStatus": "Connection Status", + "connectionMode": "Connection Mode", + "connectionModeAria": "Connection mode", + "imageCount": "Image Count", + "activeTasks": "Active Tasks", + "avgTime": "Avg Time", + "gallery": "Gallery", + "waitingTitle": "Waiting for connection", + "waitingSubtitle": "Generated images will appear here after starting a task", + "start": "Start", + "batch": "Batch", + "batchDownloadTitle": "Batch download", + "selectAll": "Select all", + "deselectAll": "Deselect all", + "download": "Download", + "startedTasks": "Started {count} concurrent tasks", + "startedTasksSSE": "Started {count} concurrent tasks (SSE)", + "generatingSSE": "Generating (SSE)", + "selectFolder": "Selected folder: {name}", + "selectFolderFailed": "Failed to select folder", + "noImagesSelected": "Please select images to download first", + "jszipFailed": "JSZip library failed to load, please refresh and try again", + "packing": "Packing {count} images...", + "packingBtn": "Packing...", + "packingProgress": "Packing... ({done}/{total})", + "noImagesDownloaded": "No images were downloaded successfully", + "generatingZip": "Generating ZIP...", + "packSuccess": "Successfully packed {count} images", + "packFailed": "Packing failed, please try again" }, - "levels": { - "all": "All Levels" + "video": { + "pageTitle": "Grok2API - Video Generation", + "title": "Video Generation", + "subtitle": "Generate short videos with reference images and preset styles.", + "startGenerate": "Generate", + "genSettings": "Generation Settings", + "prompt": "Prompt", + "promptPlaceholder": "e.g.: neon rain at night on the street, slow motion, film grain", + "referenceImage": "Reference Image", + "referenceImagePlaceholder": "https://... or data:image/...", + "aspectRatio": "Aspect Ratio", + "ratio3_2": "3:2 Landscape", + "ratio2_3": "2:3 Portrait", + "ratio16_9": "16:9 Wide", + "ratio9_16": "9:16 Tall", + "ratio1_1": "1:1 Square", + "duration": "Duration", + "seconds": "{n}s", + "resolution": "Resolution", + "stylePreset": "Style Preset", + "upload": "Upload", + "clearImage": "Clear", + "runStatus": "Run Status", + "elapsedTime": "Elapsed {sec}s", + "elapsedTimeNone": "Elapsed -", + "metaRatio": "Ratio", + "metaDuration": "Duration", + "metaResolution": "Resolution", + "metaPreset": "Preset", + "videoPreview": "Video Preview", + "clearPreview": "Clear", + "waitingGenerate": "Waiting for video generation", + "videoTitle": "Video {n}", + "open": "Open", + "generatingPlaceholder": "Generating...", + "superResolution": "Super Resolution", + "superResolutionInProgress": "Super resolution in progress", + "alreadyGenerating": "Already generating", + "referenceConflict": "Reference image: choose either URL/Base64 or file upload", + "downloadFailed": "Download failed, please check if the video link is accessible", + "sec6": "6s", + "sec10": "10s", + "sec15": "15s" }, - "stats": { - "file": "Current File", - "entries": "Loaded Rows", - "entriesHint": "Shown with current filters", - "errorWarn": "Warnings / Errors", - "errorWarnHint": "warning + error", - "size": "File Size", - "sizeHint": "Disk usage" + "voice": { + "pageTitle": "Grok2API - LiveKit Voice", + "title": "LiveKit Voice", + "subtitle": "LiveKit voice session, connect to Grok Voice.", + "startSession": "Start Session", + "stopSession": "Stop Session", + "connectionSettings": "Connection Settings", + "voiceLabel": "Voice", + "personalityLabel": "Personality", + "speedLabel": "Speed", + "settingsHint": "Tip: Voice / Personality settings are sent to the server before connecting.", + "sessionLog": "Session Log", + "clearLog": "Clear", + "copyLog": "Copy", + "sessionStatus": "Session Status", + "audioOutput": "Audio Output", + "audioHint": "Subscribed audio tracks will be automatically inserted here.", + "livekitSDKError": "Error: LiveKit SDK failed to load properly, please refresh the page", + "livekitLoadFailed": "LiveKit SDK failed to load", + "secureContextBrowser": "Please use a modern browser and allow microphone access", + "secureContextHTTPS": "Please use HTTPS or access from localhost", + "secureContextError": "Current environment does not support microphone access, {hint}", + "connectingStatus": "Connecting", + "fetchingToken": "Fetching Token...", + "fetchTokenFailed": "Failed to get Token: {status}", + "fetchTokenSuccess": "Token obtained successfully", + "participantConnected": "Participant connected: {identity}", + "participantDisconnected": "Participant disconnected: {identity}", + "trackSubscribed": "Track subscribed: {kind}", + "disconnected": "Disconnected", + "connectedToServer": "Connected to LiveKit server", + "inCall": "In call", + "openingMic": "Opening microphone...", + "voiceEnabled": "Voice enabled", + "voiceConnected": "Voice connected successfully", + "errorPrefix": "Error: {msg}", + "logCopied": "Log copied", + "copyLogFailed": "Copy failed, please select manually" } - }, - "token": { - "pageTitle": "Grok2API - Token Mgmt", - "title": "Token List", - "subtitle": "Manage the Grok2API Token service pool.", - "import": "Import", - "add": "Add", - "statTotal": "Total Tokens", - "statActive": "Active", - "statCooling": "Rate Limited", - "statInvalid": "Invalid", - "statChatQuota": "Chat Remaining", - "statImageQuota": "Image Remaining", - "statVideoQuota": "Video Remaining", - "statVideoUnavailable": "N/A", - "statTotalCalls": "Total Calls", - "tabAll": "All", - "tabActive": "Active", - "tabCooling": "Rate Limited", - "tabExpired": "Expired", - "tabNsfw": "NSFW On", - "tabNoNsfw": "NSFW Off", - "statusFilterAria": "Token status filter", - "tableToken": "Token", - "tableType": "Type", - "tableStatus": "Status", - "tableQuota": "Quota", - "tableNote": "Note", - "tableActions": "Actions", - "refreshStatus": "Refresh status", - "emptyState": "No Tokens yet. Click Import or Add above.", - "emptyFilterState": "No results for current filter. Try a different filter.", - "selectPage": "Select page", - "selectAllFiltered": "Select all", - "clearSelection": "Clear selection", - "prevPage": "Prev", - "nextPage": "Next", - "perPage": "{size} / page", - "pagination": "Page {current} / {total} \u00b7 {count} items", - "batchExport": "Export", - "batchRefresh": "Refresh", - "batchDisable": "Disable", - "batchEnable": "Enable", - "batchDelete": "Delete", - "importTitle": "Batch Import Tokens", - "importTargetPool": "Target Pool", - "importTokenList": "Token List (one per line)", - "importPlaceholder": "Paste Tokens, one per line...", - "startImport": "Start Import", - "editTitle": "Edit Token", - "addTitle": "Add Token", - "editType": "Type", - "editQuota": "Quota", - "editNote": "Note", - "editNotePlaceholder": "Optional note", - "tokenEmpty": "Token cannot be empty", - "tokenExists": "Token already exists", - "confirmDelete": "Are you sure you want to delete this Token?", - "confirmDisable": "Are you sure you want to disable this Token?\n{token}", - "confirmEnable": "Are you sure you want to enable this Token?\n{token}", - "confirmBatchDelete": "Are you sure you want to delete {count} selected Tokens?", - "confirmBatchDisable": "Are you sure you want to disable {count} selected Tokens?", - "confirmBatchEnable": "Are you sure you want to enable {count} selected Tokens?", - "disableToken": "Disable", - "enableToken": "Enable", - "disableDone": "Token disabled", - "enableDone": "Token enabled", - "batchDisableDone": "Batch disable complete", - "batchEnableDone": "Batch enable complete", - "noTokenToDisable": "All selected Tokens are already disabled", - "noTokenToEnable": "All selected Tokens are already enabled", - "deleteDone": "Delete complete", - "refreshDone": "Refresh complete", - "refreshSuccess": "Refresh successful", - "refreshFailed": "Refresh failed", - "refreshError": "Refresh failed: {msg}", - "requestError": "Request error", - "notValidJson": "Response is not valid JSON (HTTP {status})", - "listEmpty": "List is empty", - "stopRefresh": "Refresh stopped", - "stopDelete": "Delete stopped", - "stopNsfw": "NSFW stopped", - "nsfwConfirm": "Enable NSFW mode for {count} selected Tokens?", - "nsfwEnable": "Enable NSFW", - "nsfwDone": "NSFW enable complete", - "nsfwResult": "NSFW enable complete: {ok} succeeded, {fail} failed", - "nsfwFailed": "Enable failed: {msg}", - "emptyResponse": "Empty response (HTTP {status})" - }, - "cache": { - "pageTitle": "Grok2API - Cache Mgmt", - "title": "Cache Management", - "subtitle": "Manage local resources and online asset cache.", - "localImages": "Local Images", - "localVideos": "Local Videos", - "onlineAssets": "Online Assets", - "tableFile": "File", - "tableSize": "Size", - "tableTime": "Time", - "tableActions": "Actions", - "tableToken": "Token", - "tableType": "Type", - "tableAssetCount": "Asset Count", - "tableLastClear": "Last Cleared", - "notLoaded": "Not loaded", - "noAccounts": "No accounts available", - "noFiles": "No files", - "noTokenAvailable": "No Token available", - "cannotConnect": "Cannot connect", - "lastClear": "Last cleared: {time}", - "clear": "Clear", - "clean": "Clean", - "confirmClearImage": "Clear all local image cache?", - "confirmClearVideo": "Clear all local video cache?", - "clearSuccess": "Cleanup successful, freed {size} MB", - "clearFailed": "Cleanup failed", - "confirmDeleteFile": "Are you sure you want to delete this file?", - "confirmBatchDeleteFiles": "Are you sure you want to delete {count} selected files?", - "noFilesSelected": "No files selected", - "deletedFiles": "Deleted {count} files", - "deleteResult": "Delete complete: {success} succeeded, {failed} failed", - "loadStatsFailed": "Failed to load statistics", - "selectAccountsToLoad": "Please select accounts to load", - "selectAccountsToClear": "Please select accounts to clear", - "confirmClearAccounts": "Clear online assets for {count} selected accounts?", - "confirmClearAccount": "Clear online assets for account {label}?", - "batchCleanInProgress": "Batch cleaning online assets, please wait...", - "cleanInProgress": "Cleaning online assets, please wait...", - "batchCleanDone": "Batch cleanup complete", - "cleanResult": "Cleanup complete (succeeded: {success}, failed: {failed})", - "cleanFailedEntry": "Cleanup failed", - "requestTimeout": "Request timed out or failed", - "loadDone": "Load complete", - "loadingStatus": "Loading", - "loadStopped": "Load stopped", - "loadFailedMsg": "Load failed: {msg}", - "stoppedLoadRequests": "Remaining load requests stopped", - "cleanStopped": "Clean stopped", - "cleanFailedMsg": "Clean failed: {msg}", - "stoppedCleanRequests": "Remaining clean requests stopped" - }, - "chat": { - "pageTitle": "Grok2API - Chat", - "title": "Chat", - "subtitle": "Chat with Grok via the chat API", - "sessions": "Sessions", - "newChat": "New", - "collapseSidebar": "Collapse", - "expandSidebar": "Expand", - "sessionList": "Session list", - "newSession": "New Session", - "ready": "Ready", - "emptyState": "Type a message to start a conversation.", - "uploadFile": "Upload file", - "placeholder": "Ask anything", - "settings": "Settings", - "send": "Send", - "temperature": "Temperature", - "topP": "Top P", - "systemPrompt": "System Prompt", - "systemPromptPlaceholder": "Optional, set system prompt", - "fileLabel": "[File]", - "compositeContent": "[Composite]", - "storageFull": "Local storage is full, some sessions were not saved", - "attachmentNoEdit": "Attachment messages cannot be edited yet", - "saveEdit": "Save", - "saveEditTitle": "Save edit", - "cancelEdit": "Cancel", - "cancelEditTitle": "Cancel edit", - "contentEmpty": "Content cannot be empty", - "editMessage": "Edit", - "editMessageTitle": "Edit message content", - "regenerate": "Regenerate", - "regenerateTitle": "Regenerate response from here", - "retryTitle": "Retry last response", - "editAnswer": "Edit", - "editAnswerTitle": "Edit response content", - "copyAnswer": "Copy", - "copyAnswerTitle": "Copy response content", - "feedback": "Feedback", - "feedbackTitle": "Feedback to Grok2API", - "noContentToCopy": "No content to copy", - "noChatToRetry": "No conversation to retry", - "requestFailedCheck": "Request failed, please check service status", - "requestFailedStatus": "Request failed: {status}", - "clickRetry": "Click to retry", - "thinking": "Thinking", - "thinkingSec": "Thinking {sec}s", - "thought": "Thought", - "thinkLabel": "Thinking", - "empty": "(empty)", - "toolWebSearch": "Web search", - "toolImageSearch": "Image search", - "toolAgentThink": "Reasoning", - "toolDefault": "Tool" - }, - "imagine": { - "pageTitle": "Grok2API - Imagine Gallery", - "title": "Imagine Gallery", - "subtitle": "Continuously generate images via WebSocket, real-time base64 gallery.", - "genSettings": "Generation Settings", - "prompt": "Prompt", - "promptPlaceholder": "Describe the image you want, e.g.: futuristic city neon rain at night, wide angle photography", - "autoFollow": "Auto Follow", - "autoFollowDesc": "Scroll to latest", - "autoSave": "Auto Save", - "autoSaveDesc": "Auto-save images", - "aspectRatio": "Aspect Ratio", - "ratio2_3": "2:3 Portrait", - "ratio1_1": "1:1 Square", - "ratio3_2": "3:2 Landscape", - "ratio16_9": "16:9 Wide", - "ratio9_16": "9:16 Tall", - "concurrent": "Concurrency", - "tasks": "{n} tasks", - "concurrentTask1": "1 task", - "concurrentTask2": "2 tasks", - "concurrentTask3": "3 tasks", - "autoFilter": "Auto Filter", - "autoFilterDesc": "Filter substandard images", - "nsfwLabel": "NSFW", - "nsfwOn": "On", - "nsfwOff": "Off", - "saveLocation": "Save Location", - "browserDefault": "Browser default location", - "reverseOrder": "Reverse Order", - "reverseOrderDesc": "Show newest on top", - "connectionStatus": "Connection Status", - "connectionMode": "Connection Mode", - "connectionModeAria": "Connection mode", - "imageCount": "Image Count", - "activeTasks": "Active Tasks", - "avgTime": "Avg Time", - "gallery": "Gallery", - "waitingTitle": "Waiting for connection", - "waitingSubtitle": "Generated images will appear here after starting a task", - "start": "Start", - "batch": "Batch", - "batchDownloadTitle": "Batch download", - "selectAll": "Select all", - "deselectAll": "Deselect all", - "download": "Download", - "startedTasks": "Started {count} concurrent tasks", - "startedTasksSSE": "Started {count} concurrent tasks (SSE)", - "generatingSSE": "Generating (SSE)", - "selectFolder": "Selected folder: {name}", - "selectFolderFailed": "Failed to select folder", - "noImagesSelected": "Please select images to download first", - "jszipFailed": "JSZip library failed to load, please refresh and try again", - "packing": "Packing {count} images...", - "packingBtn": "Packing...", - "packingProgress": "Packing... ({done}/{total})", - "noImagesDownloaded": "No images were downloaded successfully", - "generatingZip": "Generating ZIP...", - "packSuccess": "Successfully packed {count} images", - "packFailed": "Packing failed, please try again" - }, - "video": { - "pageTitle": "Grok2API - Video Generation", - "title": "Video Generation", - "subtitle": "Generate short videos with reference images and preset styles.", - "startGenerate": "Generate", - "genSettings": "Generation Settings", - "prompt": "Prompt", - "promptPlaceholder": "e.g.: neon rain at night on the street, slow motion, film grain", - "referenceImage": "Reference Image", - "referenceImagePlaceholder": "https://... or data:image/...", - "aspectRatio": "Aspect Ratio", - "ratio3_2": "3:2 Landscape", - "ratio2_3": "2:3 Portrait", - "ratio16_9": "16:9 Wide", - "ratio9_16": "9:16 Tall", - "ratio1_1": "1:1 Square", - "duration": "Duration", - "seconds": "{n}s", - "resolution": "Resolution", - "stylePreset": "Style Preset", - "upload": "Upload", - "clearImage": "Clear", - "runStatus": "Run Status", - "elapsedTime": "Elapsed {sec}s", - "elapsedTimeNone": "Elapsed -", - "metaRatio": "Ratio", - "metaDuration": "Duration", - "metaResolution": "Resolution", - "metaPreset": "Preset", - "videoPreview": "Video Preview", - "clearPreview": "Clear", - "waitingGenerate": "Waiting for video generation", - "videoTitle": "Video {n}", - "open": "Open", - "generatingPlaceholder": "Generating...", - "superResolution": "Super Resolution", - "superResolutionInProgress": "Super resolution in progress", - "alreadyGenerating": "Already generating", - "referenceConflict": "Reference image: choose either URL/Base64 or file upload", - "downloadFailed": "Download failed, please check if the video link is accessible", - "sec6": "6s", - "sec10": "10s", - "sec15": "15s" - }, - "voice": { - "pageTitle": "Grok2API - LiveKit Voice", - "title": "LiveKit Voice", - "subtitle": "LiveKit voice session, connect to Grok Voice.", - "startSession": "Start Session", - "stopSession": "Stop Session", - "connectionSettings": "Connection Settings", - "voiceLabel": "Voice", - "personalityLabel": "Personality", - "speedLabel": "Speed", - "settingsHint": "Tip: Voice / Personality settings are sent to the server before connecting.", - "sessionLog": "Session Log", - "clearLog": "Clear", - "copyLog": "Copy", - "sessionStatus": "Session Status", - "audioOutput": "Audio Output", - "audioHint": "Subscribed audio tracks will be automatically inserted here.", - "livekitSDKError": "Error: LiveKit SDK failed to load properly, please refresh the page", - "livekitLoadFailed": "LiveKit SDK failed to load", - "secureContextBrowser": "Please use a modern browser and allow microphone access", - "secureContextHTTPS": "Please use HTTPS or access from localhost", - "secureContextError": "Current environment does not support microphone access, {hint}", - "connectingStatus": "Connecting", - "fetchingToken": "Fetching Token...", - "fetchTokenFailed": "Failed to get Token: {status}", - "fetchTokenSuccess": "Token obtained successfully", - "participantConnected": "Participant connected: {identity}", - "participantDisconnected": "Participant disconnected: {identity}", - "trackSubscribed": "Track subscribed: {kind}", - "disconnected": "Disconnected", - "connectedToServer": "Connected to LiveKit server", - "inCall": "In call", - "openingMic": "Opening microphone...", - "voiceEnabled": "Voice enabled", - "voiceConnected": "Voice connected successfully", - "errorPrefix": "Error: {msg}", - "logCopied": "Log copied", - "copyLogFailed": "Copy failed, please select manually" - } } diff --git a/_public/static/i18n/locales/zh.json b/_public/static/i18n/locales/zh.json index c0d48c79..6c15f3c0 100644 --- a/_public/static/i18n/locales/zh.json +++ b/_public/static/i18n/locales/zh.json @@ -1,602 +1,850 @@ { - "common": { - "save": "保存", - "cancel": "取消", - "confirm": "确认", - "delete": "删除", - "loading": "加载中...", - "invalidKey": "密钥无效", - "connectionFailed": "连接失败", - "requestFailed": "请求失败", - "operationSuccess": "操作成功", - "gotIt": "我知道了", - "notice": "提示", - "rateLimitNotice": "GROK 官方网页更新后未真实暴露 rate-limits 接口,导致无法准确计算 Token 剩余,请耐心等待官方接口上线,目前自动刷新后会更新为 8 次", - "pleaseConfirm": "请确认", - "ok": "确定", - "close": "关闭", - "error": "错误", - "unknownError": "未知错误", - "connectionInterrupted": "连接中断", - "taskInProgress": "当前有任务进行中", - "taskNoPause": "当前任务不支持暂停", - "batchNoPause": "当前批量任务不支持暂停", - "waitTaskFinish": "请等待当前任务结束", - "pause": "暂停", - "stop": "终止", - "selected": "已选择", - "items": "项", - "export": "导出", - "refresh": "刷新", - "load": "加载", - "files": "个文件", - "view": "查看", - "retry": "重试", - "copy": "复制", - "edit": "编辑", - "loadFailed": "加载失败", - "loadError": "加载失败: {msg}", - "saveFailed": "保存失败", - "saveError": "保存错误: {msg}", - "deleteFailed": "删除失败", - "deleteSuccess": "删除成功", - "notConnected": "未连接", - "connecting": "连接中", - "connected": "连接正常", - "connectionError": "连接错误", - "generating": "生成中", - "done": "完成", - "failed": "失败", - "stopped": "已停止", - "terminated": "已终止", - "sending": "发送中", - "fileReadFailed": "文件读取失败", - "noFileSelected": "未选择文件", - "configurePublicKey": "请先配置 Public Key", - "enterPrompt": "请输入提示词", - "enterContent": "请输入内容", - "alreadyRunning": "已在运行中", - "createTaskFailed": "创建任务失败", - "copyFailed": "复制失败", - "copied": "已复制", - "generationFailed": "生成失败", - "partialError": "部分异常", - "loadingInProgress": "正在加载中,请稍候", - "cleaningInProgress": "正在清理中,请稍候", - "noTokenSelected": "未选择 Token", - "clearCache": "清空缓存", - "failureDetails": "失败详情", - "retryFailed": "重试失败项" - }, - "nav": { - "tokenManage": "Token管理", - "configManage": "配置管理", - "cacheManage": "缓存管理", - "logsView": "日志查看", - "chat": "Chat 聊天", - "imagine": "Imagine 瀑布流", - "video": "Video 视频生成", - "voice": "LiveKit 陪聊", - "feedback": "反馈", - "logout": "退出", - "adminPanel": "管理后台", - "storageMode": "存储模式" - }, - "login": { - "adminPageTitle": "Grok2API - 登录", - "adminTitle": "管理后台", - "adminSubtitle": "请输入后台密码以继续", - "adminPlaceholder": "后台密码 (app_key)", - "continue": "继续", - "publicPageTitle": "Grok2API - Public", - "publicTitle": "Public Access", - "publicSubtitle": "请输入 Public Key(如未配置可直接进入)", - "publicPlaceholder": "Public Key", - "enter": "进入", - "goToAdmin": "进入管理后台" - }, - "config": { - "pageTitle": "Grok2API - 配置管理", - "title": "配置管理", - "subtitle": "管理 API 密钥及系统参数设置。", - "saving": "保存中...", - "saved": "成功", - "configSaved": "配置已保存", - "invalidJson": "无效的 JSON: {field}", - "appKeyRequired": "app_key 不能为空(后台密码)", - "flaresolverrRequired": "启用自动刷新时必须填写 FlareSolverr 地址", - "generate": "生成", - "publicAccess": "功能玩法", - "noDesc": "暂无说明,请参考配置文档。", - "sectionFallback": "{section} 设置", - "sections": { - "app": "应用设置", - "proxy": "代理配置", - "retry": "重试策略", - "chat": "对话配置", - "video": "视频配置", - "image": "图像配置", - "imagine_fast": "Imagine Fast 配置", - "asset": "资产配置", - "voice": "语音配置", - "token": "Token 池管理", - "cache": "缓存管理", - "nsfw": "NSFW 配置", - "usage": "Usage 配置" + "common": { + "save": "\u4fdd\u5b58", + "cancel": "\u53d6\u6d88", + "confirm": "\u786e\u8ba4", + "delete": "\u5220\u9664", + "loading": "\u52a0\u8f7d\u4e2d...", + "invalidKey": "\u5bc6\u94a5\u65e0\u6548", + "connectionFailed": "\u8fde\u63a5\u5931\u8d25", + "requestFailed": "\u8bf7\u6c42\u5931\u8d25", + "operationSuccess": "\u64cd\u4f5c\u6210\u529f", + "gotIt": "\u6211\u77e5\u9053\u4e86", + "notice": "\u63d0\u793a", + "rateLimitNotice": "GROK \u5b98\u65b9\u7f51\u9875\u66f4\u65b0\u540e\u672a\u771f\u5b9e\u66b4\u9732 rate-limits \u63a5\u53e3\uff0c\u5bfc\u81f4\u65e0\u6cd5\u51c6\u786e\u8ba1\u7b97 Token \u5269\u4f59\uff0c\u8bf7\u8010\u5fc3\u7b49\u5f85\u5b98\u65b9\u63a5\u53e3\u4e0a\u7ebf\uff0c\u76ee\u524d\u81ea\u52a8\u5237\u65b0\u540e\u4f1a\u66f4\u65b0\u4e3a 8 \u6b21", + "pleaseConfirm": "\u8bf7\u786e\u8ba4", + "ok": "\u786e\u5b9a", + "close": "\u5173\u95ed", + "error": "\u9519\u8bef", + "unknownError": "\u672a\u77e5\u9519\u8bef", + "connectionInterrupted": "\u8fde\u63a5\u4e2d\u65ad", + "taskInProgress": "\u5f53\u524d\u6709\u4efb\u52a1\u8fdb\u884c\u4e2d", + "taskNoPause": "\u5f53\u524d\u4efb\u52a1\u4e0d\u652f\u6301\u6682\u505c", + "batchNoPause": "\u5f53\u524d\u6279\u91cf\u4efb\u52a1\u4e0d\u652f\u6301\u6682\u505c", + "waitTaskFinish": "\u8bf7\u7b49\u5f85\u5f53\u524d\u4efb\u52a1\u7ed3\u675f", + "pause": "\u6682\u505c", + "stop": "\u7ec8\u6b62", + "selected": "\u5df2\u9009\u62e9", + "items": "\u9879", + "export": "\u5bfc\u51fa", + "refresh": "\u5237\u65b0", + "load": "\u52a0\u8f7d", + "files": "\u4e2a\u6587\u4ef6", + "view": "\u67e5\u770b", + "retry": "\u91cd\u8bd5", + "copy": "\u590d\u5236", + "edit": "\u7f16\u8f91", + "loadFailed": "\u52a0\u8f7d\u5931\u8d25", + "loadError": "\u52a0\u8f7d\u5931\u8d25: {msg}", + "saveFailed": "\u4fdd\u5b58\u5931\u8d25", + "saveError": "\u4fdd\u5b58\u9519\u8bef: {msg}", + "deleteFailed": "\u5220\u9664\u5931\u8d25", + "deleteSuccess": "\u5220\u9664\u6210\u529f", + "notConnected": "\u672a\u8fde\u63a5", + "connecting": "\u8fde\u63a5\u4e2d", + "connected": "\u8fde\u63a5\u6b63\u5e38", + "connectionError": "\u8fde\u63a5\u9519\u8bef", + "generating": "\u751f\u6210\u4e2d", + "done": "\u5b8c\u6210", + "failed": "\u5931\u8d25", + "stopped": "\u5df2\u505c\u6b62", + "terminated": "\u5df2\u7ec8\u6b62", + "sending": "\u53d1\u9001\u4e2d", + "fileReadFailed": "\u6587\u4ef6\u8bfb\u53d6\u5931\u8d25", + "noFileSelected": "\u672a\u9009\u62e9\u6587\u4ef6", + "configurePublicKey": "\u8bf7\u5148\u914d\u7f6e Public Key", + "enterPrompt": "\u8bf7\u8f93\u5165\u63d0\u793a\u8bcd", + "enterContent": "\u8bf7\u8f93\u5165\u5185\u5bb9", + "alreadyRunning": "\u5df2\u5728\u8fd0\u884c\u4e2d", + "createTaskFailed": "\u521b\u5efa\u4efb\u52a1\u5931\u8d25", + "copyFailed": "\u590d\u5236\u5931\u8d25", + "copied": "\u5df2\u590d\u5236", + "generationFailed": "\u751f\u6210\u5931\u8d25", + "partialError": "\u90e8\u5206\u5f02\u5e38", + "loadingInProgress": "\u6b63\u5728\u52a0\u8f7d\u4e2d\uff0c\u8bf7\u7a0d\u5019", + "cleaningInProgress": "\u6b63\u5728\u6e05\u7406\u4e2d\uff0c\u8bf7\u7a0d\u5019", + "noTokenSelected": "\u672a\u9009\u62e9 Token", + "clearCache": "\u6e05\u7a7a\u7f13\u5b58", + "failureDetails": "\u5931\u8d25\u8be6\u60c5", + "retryFailed": "\u91cd\u8bd5\u5931\u8d25\u9879" }, - "sectionDescs": { - "proxy": "配置不正确将导致 403 错误。服务首次请求 Grok 时的 IP 必须与获取 CF Clearance 时的 IP 一致,后续服务器请求 IP 变化不会导致 403。" + "nav": { + "tokenManage": "Token\u7ba1\u7406", + "configManage": "\u914d\u7f6e\u7ba1\u7406", + "cacheManage": "\u7f13\u5b58\u7ba1\u7406", + "logsView": "\u65e5\u5fd7\u67e5\u770b", + "chat": "Chat \u804a\u5929", + "imagine": "Imagine \u7011\u5e03\u6d41", + "video": "Video \u89c6\u9891\u751f\u6210", + "voice": "LiveKit \u966a\u804a", + "feedback": "\u53cd\u9988", + "logout": "\u9000\u51fa", + "adminPanel": "\u7ba1\u7406\u540e\u53f0", + "storageMode": "\u5b58\u50a8\u6a21\u5f0f" }, - "fields": { - "app": { - "api_key": {"title": "API 密钥", "desc": "调用 Grok2API 服务的 Token(可选,支持多个,逗号分隔或数组)。"}, - "app_key": {"title": "后台密码", "desc": "登录 Grok2API 管理后台的密码(必填)。"}, - "function_enabled": {"title": "启用功能玩法", "desc": "是否启用功能玩法入口(关闭则功能玩法页面不可访问)。"}, - "function_key": {"title": "Function 密码", "desc": "功能玩法页面的访问密码(可选)。"}, - "app_url": {"title": "应用地址", "desc": "当前 Grok2API 服务的外部访问 URL,用于文件链接访问。"}, - "image_format": {"title": "图片格式", "desc": "默认生成的图片格式(url 或 base64)。"}, - "video_format": {"title": "视频格式", "desc": "默认生成的视频格式(html 或 url,url 为处理后的链接)。"}, - "temporary": {"title": "临时对话", "desc": "是否默认启用临时对话模式。"}, - "disable_memory": {"title": "禁用记忆", "desc": "是否默认禁用 Grok 记忆功能。"}, - "stream": {"title": "流式响应", "desc": "是否默认启用流式输出。"}, - "thinking": {"title": "思维链", "desc": "是否默认启用思维链输出。"}, - "dynamic_statsig": {"title": "动态指纹", "desc": "是否默认启用动态生成 Statsig 指纹。"}, - "custom_instruction": {"title": "自定义指令", "desc": "多行文本,会透传为 Grok 请求参数 customPersonality。"}, - "filter_tags": {"title": "过滤标签", "desc": "设置自动过滤 Grok 响应中的特殊标签。"} - }, - "proxy": { - "base_proxy_url": {"title": "基础代理 URL", "desc": "代理请求到 Grok 官网的基础服务地址。"}, - "asset_proxy_url": {"title": "资源代理 URL", "desc": "代理请求到 Grok 官网的静态资源(图片/视频)地址。"}, - "skip_proxy_ssl_verify": {"title": "跳过代理 SSL 校验", "desc": "代理使用自签名证书时启用(仅放行代理证书校验)。"}, - "enabled": {"title": "启用 CF 自动刷新", "desc": "启用后将通过 FlareSolverr 自动获取 cf_clearance。"}, - "flaresolverr_url": {"title": "FlareSolverr 地址", "desc": "FlareSolverr 服务的 HTTP 地址(如 http://flaresolverr:8191)。"}, - "refresh_interval": {"title": "刷新间隔(秒)", "desc": "自动刷新 cf_clearance 的时间间隔,建议不低于 300 秒。"}, - "timeout": {"title": "挑战超时(秒)", "desc": "等待 FlareSolverr 解决 CF 挑战的最大时间。"}, - "cf_clearance": {"title": "CF Clearance", "desc": "Cloudflare Clearance Cookie,用于绕过反爬虫验证。启用自动刷新时由系统自动管理。"}, - "browser": {"title": "浏览器指纹", "desc": "curl_cffi 浏览器指纹标识(如 chrome136)。启用自动刷新时由系统自动管理。"}, - "user_agent": {"title": "User-Agent", "desc": "HTTP 请求的 User-Agent 字符串。启用自动刷新时由系统自动管理。"} - }, - "retry": { - "max_retry": {"title": "最大重试次数", "desc": "请求 Grok 服务失败时的最大重试次数。"}, - "retry_status_codes": {"title": "重试状态码", "desc": "触发重试的 HTTP 状态码列表。"}, - "reset_session_status_codes": {"title": "重建状态码", "desc": "触发重建 session 的 HTTP 状态码列表(用于轮换代理)。"}, - "retry_backoff_base": {"title": "退避基数", "desc": "重试退避的基础延迟(秒)。"}, - "retry_backoff_factor": {"title": "退避倍率", "desc": "重试退避的指数放大系数。"}, - "retry_backoff_max": {"title": "退避上限", "desc": "单次重试等待的最大延迟(秒)。"}, - "retry_budget": {"title": "退避预算", "desc": "单次请求的最大重试总耗时(秒)。"} - }, - "chat": { - "concurrent": {"title": "并发上限", "desc": "Reverse 接口并发上限。"}, - "timeout": {"title": "请求超时", "desc": "Reverse 接口超时时间(秒)。"}, - "stream_timeout": {"title": "流空闲超时", "desc": "流式空闲超时时间(秒)。"} - }, - "video": { - "concurrent": {"title": "并发上限", "desc": "Reverse 接口并发上限。"}, - "timeout": {"title": "请求超时", "desc": "Reverse 接口超时时间(秒)。"}, - "stream_timeout": {"title": "流空闲超时", "desc": "流式空闲超时时间(秒)。"} - }, - "image": { - "timeout": {"title": "请求超时", "desc": "WebSocket 请求超时时间(秒)。"}, - "stream_timeout": {"title": "流空闲超时", "desc": "WebSocket 流式空闲超时时间(秒)。"}, - "final_timeout": {"title": "最终图超时", "desc": "收到中等图后等待最终图的超时秒数。"}, - "blocked_grace_seconds": {"title": "审查宽限秒数", "desc": "收到中等图后,判定疑似被审查的宽限秒数(默认 10 秒,可自定义)。"}, - "nsfw": {"title": "NSFW 模式", "desc": "WebSocket 请求是否启用 NSFW。"}, - "medium_min_bytes": {"title": "中等图最小字节", "desc": "判定中等质量图的最小字节数。"}, - "final_min_bytes": {"title": "最终图最小字节", "desc": "判定最终图的最小字节数(通常 JPG > 100KB)。"}, - "blocked_parallel_enabled": {"title": "启用并行补偿", "desc": "疑似审查/拦截时,是否启用并行补偿生成。"}, - "blocked_parallel_attempts": {"title": "拦截补偿并发次数", "desc": "疑似审查/拦截导致无最终图时,自动并行补偿生成次数。"} - }, - "imagine_fast": { - "n": {"title": "生成数量", "desc": "仅用于 grok-imagine-1.0-fast 的服务端统一生成数量(1-10)。"}, - "size": {"title": "图片尺寸", "desc": "仅用于 grok-imagine-1.0-fast 的服务端统一尺寸。"}, - "response_format": {"title": "响应格式", "desc": "仅用于 grok-imagine-1.0-fast 的服务端统一返回格式。"} - }, - "asset": { - "upload_concurrent": {"title": "上传并发", "desc": "上传接口的最大并发数。推荐 30。"}, - "upload_timeout": {"title": "上传超时", "desc": "上传接口超时时间(秒)。推荐 60。"}, - "download_concurrent": {"title": "下载并发", "desc": "下载接口的最大并发数。推荐 30。"}, - "download_timeout": {"title": "下载超时", "desc": "下载接口超时时间(秒)。推荐 60。"}, - "list_concurrent": {"title": "查询并发", "desc": "资产查询接口的最大并发数。推荐 10。"}, - "list_timeout": {"title": "查询超时", "desc": "资产查询接口超时时间(秒)。推荐 60。"}, - "list_batch_size": {"title": "查询批次大小", "desc": "单次查询可处理的 Token 数量。推荐 10。"}, - "delete_concurrent": {"title": "删除并发", "desc": "资产删除接口的最大并发数。推荐 10。"}, - "delete_timeout": {"title": "删除超时", "desc": "资产删除接口超时时间(秒)。推荐 60。"}, - "delete_batch_size": {"title": "删除批次大小", "desc": "单次删除可处理的 Token 数量。推荐 10。"} - }, - "voice": { - "timeout": {"title": "请求超时", "desc": "Voice 请求超时时间(秒)。"} - }, - "token": { - "auto_refresh": {"title": "自动刷新", "desc": "是否开启 Token 自动刷新机制。"}, - "refresh_interval_hours": {"title": "刷新间隔", "desc": "普通 Token 刷新的时间间隔(小时)。"}, - "super_refresh_interval_hours": {"title": "Super 刷新间隔", "desc": "Super Token 刷新的时间间隔(小时)。"}, - "fail_threshold": {"title": "失败阈值", "desc": "单个 Token 连续失败多少次后被标记为不可用。"}, - "save_delay_ms": {"title": "保存延迟", "desc": "Token 变更合并写入的延迟(毫秒)。"}, - "usage_flush_interval_sec": {"title": "用量落库间隔", "desc": "用量类字段写入数据库的最小间隔(秒)。"}, - "reload_interval_sec": {"title": "同步间隔", "desc": "多 worker 场景下 Token 状态刷新间隔(秒)。"} - }, - "cache": { - "enable_auto_clean": {"title": "自动清理", "desc": "是否启用缓存自动清理,开启后按上限自动回收。"}, - "limit_mb": {"title": "清理阈值", "desc": "缓存大小阈值(MB),超过阈值会触发清理。"} - }, - "nsfw": { - "concurrent": {"title": "并发上限", "desc": "批量开启 NSFW 模式时的并发请求上限。推荐 10。"}, - "batch_size": {"title": "批次大小", "desc": "批量开启 NSFW 模式的单批处理数量。推荐 50。"}, - "timeout": {"title": "请求超时", "desc": "NSFW 开启相关请求的超时时间(秒)。推荐 60。"} - }, - "usage": { - "concurrent": {"title": "并发上限", "desc": "批量刷新用量时的并发请求上限。推荐 10。"}, - "batch_size": {"title": "批次大小", "desc": "批量刷新用量的单批处理数量。推荐 50。"}, - "timeout": {"title": "请求超时", "desc": "用量查询接口的超时时间(秒)。推荐 60。"} - } - } - }, - "logs": { - "pageTitle": "Grok2API - 日志查看", - "title": "日志查看", - "subtitle": "按文件、级别和关键词快速筛选,并以更易读的方式查看结构化日志。", - "filesTitle": "日志文件", - "rawTitle": "原始日志", - "empty": "没有匹配的日志记录", - "filters": { - "file": "日志文件", - "level": "级别", - "limit": "条数", - "keyword": "关键词", - "keywordPlaceholder": "traceID / path / 错误关键词", - "apply": "应用筛选", - "reset": "重置" + "login": { + "adminPageTitle": "Grok2API - \u767b\u5f55", + "adminTitle": "\u7ba1\u7406\u540e\u53f0", + "adminSubtitle": "\u8bf7\u8f93\u5165\u540e\u53f0\u5bc6\u7801\u4ee5\u7ee7\u7eed", + "adminPlaceholder": "\u540e\u53f0\u5bc6\u7801 (app_key)", + "continue": "\u7ee7\u7eed", + "publicPageTitle": "Grok2API - Public", + "publicTitle": "Public Access", + "publicSubtitle": "\u8bf7\u8f93\u5165 Public Key\uff08\u5982\u672a\u914d\u7f6e\u53ef\u76f4\u63a5\u8fdb\u5165\uff09", + "publicPlaceholder": "Public Key", + "enter": "\u8fdb\u5165", + "goToAdmin": "\u8fdb\u5165\u7ba1\u7406\u540e\u53f0" + }, + "config": { + "pageTitle": "Grok2API - \u914d\u7f6e\u7ba1\u7406", + "title": "\u914d\u7f6e\u7ba1\u7406", + "subtitle": "\u7ba1\u7406 API \u5bc6\u94a5\u53ca\u7cfb\u7edf\u53c2\u6570\u8bbe\u7f6e\u3002", + "saving": "\u4fdd\u5b58\u4e2d...", + "saved": "\u6210\u529f", + "configSaved": "\u914d\u7f6e\u5df2\u4fdd\u5b58", + "invalidJson": "\u65e0\u6548\u7684 JSON: {field}", + "appKeyRequired": "app_key \u4e0d\u80fd\u4e3a\u7a7a\uff08\u540e\u53f0\u5bc6\u7801\uff09", + "flaresolverrRequired": "\u542f\u7528\u81ea\u52a8\u5237\u65b0\u65f6\u5fc5\u987b\u586b\u5199 FlareSolverr \u5730\u5740", + "generate": "\u751f\u6210", + "publicAccess": "\u529f\u80fd\u73a9\u6cd5", + "noDesc": "\u6682\u65e0\u8bf4\u660e\uff0c\u8bf7\u53c2\u8003\u914d\u7f6e\u6587\u6863\u3002", + "sectionFallback": "{section} \u8bbe\u7f6e", + "sections": { + "app": "\u5e94\u7528\u8bbe\u7f6e", + "proxy": "\u4ee3\u7406\u914d\u7f6e", + "retry": "\u91cd\u8bd5\u7b56\u7565", + "chat": "\u5bf9\u8bdd\u914d\u7f6e", + "video": "\u89c6\u9891\u914d\u7f6e", + "image": "\u56fe\u50cf\u914d\u7f6e", + "imagine_fast": "Imagine Fast \u914d\u7f6e", + "asset": "\u8d44\u4ea7\u914d\u7f6e", + "voice": "\u8bed\u97f3\u914d\u7f6e", + "token": "Token \u6c60\u7ba1\u7406", + "cache": "\u7f13\u5b58\u7ba1\u7406", + "nsfw": "NSFW \u914d\u7f6e", + "usage": "Usage \u914d\u7f6e" + }, + "sectionDescs": { + "proxy": "\u914d\u7f6e\u4e0d\u6b63\u786e\u5c06\u5bfc\u81f4 403 \u9519\u8bef\u3002\u670d\u52a1\u9996\u6b21\u8bf7\u6c42 Grok \u65f6\u7684 IP \u5fc5\u987b\u4e0e\u83b7\u53d6 CF Clearance \u65f6\u7684 IP \u4e00\u81f4\uff0c\u540e\u7eed\u670d\u52a1\u5668\u8bf7\u6c42 IP \u53d8\u5316\u4e0d\u4f1a\u5bfc\u81f4 403\u3002" + }, + "fields": { + "app": { + "api_key": { + "title": "API \u5bc6\u94a5", + "desc": "\u8c03\u7528 Grok2API \u670d\u52a1\u7684 Token\uff08\u53ef\u9009\uff0c\u652f\u6301\u591a\u4e2a\uff0c\u9017\u53f7\u5206\u9694\u6216\u6570\u7ec4\uff09\u3002" + }, + "app_key": { + "title": "\u540e\u53f0\u5bc6\u7801", + "desc": "\u767b\u5f55 Grok2API \u7ba1\u7406\u540e\u53f0\u7684\u5bc6\u7801\uff08\u5fc5\u586b\uff09\u3002" + }, + "function_enabled": { + "title": "\u542f\u7528\u529f\u80fd\u73a9\u6cd5", + "desc": "\u662f\u5426\u542f\u7528\u529f\u80fd\u73a9\u6cd5\u5165\u53e3\uff08\u5173\u95ed\u5219\u529f\u80fd\u73a9\u6cd5\u9875\u9762\u4e0d\u53ef\u8bbf\u95ee\uff09\u3002" + }, + "function_key": { + "title": "Function \u5bc6\u7801", + "desc": "\u529f\u80fd\u73a9\u6cd5\u9875\u9762\u7684\u8bbf\u95ee\u5bc6\u7801\uff08\u53ef\u9009\uff09\u3002" + }, + "app_url": { + "title": "\u5e94\u7528\u5730\u5740", + "desc": "\u5f53\u524d Grok2API \u670d\u52a1\u7684\u5916\u90e8\u8bbf\u95ee URL\uff0c\u7528\u4e8e\u6587\u4ef6\u94fe\u63a5\u8bbf\u95ee\u3002" + }, + "image_format": { + "title": "\u56fe\u7247\u683c\u5f0f", + "desc": "\u9ed8\u8ba4\u751f\u6210\u7684\u56fe\u7247\u683c\u5f0f\uff08url \u6216 base64\uff09\u3002" + }, + "video_format": { + "title": "\u89c6\u9891\u683c\u5f0f", + "desc": "\u9ed8\u8ba4\u751f\u6210\u7684\u89c6\u9891\u683c\u5f0f\uff08html \u6216 url\uff0curl \u4e3a\u5904\u7406\u540e\u7684\u94fe\u63a5\uff09\u3002" + }, + "temporary": { + "title": "\u4e34\u65f6\u5bf9\u8bdd", + "desc": "\u662f\u5426\u9ed8\u8ba4\u542f\u7528\u4e34\u65f6\u5bf9\u8bdd\u6a21\u5f0f\u3002" + }, + "disable_memory": { + "title": "\u7981\u7528\u8bb0\u5fc6", + "desc": "\u662f\u5426\u9ed8\u8ba4\u7981\u7528 Grok \u8bb0\u5fc6\u529f\u80fd\u3002" + }, + "stream": { + "title": "\u6d41\u5f0f\u54cd\u5e94", + "desc": "\u662f\u5426\u9ed8\u8ba4\u542f\u7528\u6d41\u5f0f\u8f93\u51fa\u3002" + }, + "thinking": { + "title": "\u601d\u7ef4\u94fe", + "desc": "\u662f\u5426\u9ed8\u8ba4\u542f\u7528\u601d\u7ef4\u94fe\u8f93\u51fa\u3002" + }, + "dynamic_statsig": { + "title": "\u52a8\u6001\u6307\u7eb9", + "desc": "\u662f\u5426\u9ed8\u8ba4\u542f\u7528\u52a8\u6001\u751f\u6210 Statsig \u6307\u7eb9\u3002" + }, + "custom_instruction": { + "title": "\u81ea\u5b9a\u4e49\u6307\u4ee4", + "desc": "\u591a\u884c\u6587\u672c\uff0c\u4f1a\u900f\u4f20\u4e3a Grok \u8bf7\u6c42\u53c2\u6570 customPersonality\u3002" + }, + "filter_tags": { + "title": "\u8fc7\u6ee4\u6807\u7b7e", + "desc": "\u8bbe\u7f6e\u81ea\u52a8\u8fc7\u6ee4 Grok \u54cd\u5e94\u4e2d\u7684\u7279\u6b8a\u6807\u7b7e\u3002" + } + }, + "proxy": { + "base_proxy_url": { + "title": "\u57fa\u7840\u4ee3\u7406 URL", + "desc": "\u4ee3\u7406\u8bf7\u6c42\u5230 Grok \u5b98\u7f51\u7684\u57fa\u7840\u670d\u52a1\u5730\u5740\u3002" + }, + "asset_proxy_url": { + "title": "\u8d44\u6e90\u4ee3\u7406 URL", + "desc": "\u4ee3\u7406\u8bf7\u6c42\u5230 Grok \u5b98\u7f51\u7684\u9759\u6001\u8d44\u6e90\uff08\u56fe\u7247/\u89c6\u9891\uff09\u5730\u5740\u3002" + }, + "skip_proxy_ssl_verify": { + "title": "\u8df3\u8fc7\u4ee3\u7406 SSL \u6821\u9a8c", + "desc": "\u4ee3\u7406\u4f7f\u7528\u81ea\u7b7e\u540d\u8bc1\u4e66\u65f6\u542f\u7528\uff08\u4ec5\u653e\u884c\u4ee3\u7406\u8bc1\u4e66\u6821\u9a8c\uff09\u3002" + }, + "enabled": { + "title": "\u542f\u7528 CF \u81ea\u52a8\u5237\u65b0", + "desc": "\u542f\u7528\u540e\u5c06\u901a\u8fc7 FlareSolverr \u81ea\u52a8\u83b7\u53d6 cf_clearance\u3002" + }, + "flaresolverr_url": { + "title": "FlareSolverr \u5730\u5740", + "desc": "FlareSolverr \u670d\u52a1\u7684 HTTP \u5730\u5740\uff08\u5982 http://flaresolverr:8191\uff09\u3002" + }, + "refresh_interval": { + "title": "\u5237\u65b0\u95f4\u9694\uff08\u79d2\uff09", + "desc": "\u81ea\u52a8\u5237\u65b0 cf_clearance \u7684\u65f6\u95f4\u95f4\u9694\uff0c\u5efa\u8bae\u4e0d\u4f4e\u4e8e 300 \u79d2\u3002" + }, + "timeout": { + "title": "\u6311\u6218\u8d85\u65f6\uff08\u79d2\uff09", + "desc": "\u7b49\u5f85 FlareSolverr \u89e3\u51b3 CF \u6311\u6218\u7684\u6700\u5927\u65f6\u95f4\u3002" + }, + "cf_clearance": { + "title": "CF Clearance", + "desc": "Cloudflare Clearance Cookie\uff0c\u7528\u4e8e\u7ed5\u8fc7\u53cd\u722c\u866b\u9a8c\u8bc1\u3002\u542f\u7528\u81ea\u52a8\u5237\u65b0\u65f6\u7531\u7cfb\u7edf\u81ea\u52a8\u7ba1\u7406\u3002" + }, + "browser": { + "title": "\u6d4f\u89c8\u5668\u6307\u7eb9", + "desc": "curl_cffi \u6d4f\u89c8\u5668\u6307\u7eb9\u6807\u8bc6\uff08\u5982 chrome136\uff09\u3002\u542f\u7528\u81ea\u52a8\u5237\u65b0\u65f6\u7531\u7cfb\u7edf\u81ea\u52a8\u7ba1\u7406\u3002" + }, + "user_agent": { + "title": "User-Agent", + "desc": "HTTP \u8bf7\u6c42\u7684 User-Agent \u5b57\u7b26\u4e32\u3002\u542f\u7528\u81ea\u52a8\u5237\u65b0\u65f6\u7531\u7cfb\u7edf\u81ea\u52a8\u7ba1\u7406\u3002" + } + }, + "retry": { + "max_retry": { + "title": "\u6700\u5927\u91cd\u8bd5\u6b21\u6570", + "desc": "\u8bf7\u6c42 Grok \u670d\u52a1\u5931\u8d25\u65f6\u7684\u6700\u5927\u91cd\u8bd5\u6b21\u6570\u3002" + }, + "retry_status_codes": { + "title": "\u91cd\u8bd5\u72b6\u6001\u7801", + "desc": "\u89e6\u53d1\u91cd\u8bd5\u7684 HTTP \u72b6\u6001\u7801\u5217\u8868\u3002" + }, + "reset_session_status_codes": { + "title": "\u91cd\u5efa\u72b6\u6001\u7801", + "desc": "\u89e6\u53d1\u91cd\u5efa session \u7684 HTTP \u72b6\u6001\u7801\u5217\u8868\uff08\u7528\u4e8e\u8f6e\u6362\u4ee3\u7406\uff09\u3002" + }, + "retry_backoff_base": { + "title": "\u9000\u907f\u57fa\u6570", + "desc": "\u91cd\u8bd5\u9000\u907f\u7684\u57fa\u7840\u5ef6\u8fdf\uff08\u79d2\uff09\u3002" + }, + "retry_backoff_factor": { + "title": "\u9000\u907f\u500d\u7387", + "desc": "\u91cd\u8bd5\u9000\u907f\u7684\u6307\u6570\u653e\u5927\u7cfb\u6570\u3002" + }, + "retry_backoff_max": { + "title": "\u9000\u907f\u4e0a\u9650", + "desc": "\u5355\u6b21\u91cd\u8bd5\u7b49\u5f85\u7684\u6700\u5927\u5ef6\u8fdf\uff08\u79d2\uff09\u3002" + }, + "retry_budget": { + "title": "\u9000\u907f\u9884\u7b97", + "desc": "\u5355\u6b21\u8bf7\u6c42\u7684\u6700\u5927\u91cd\u8bd5\u603b\u8017\u65f6\uff08\u79d2\uff09\u3002" + } + }, + "chat": { + "concurrent": { + "title": "\u5e76\u53d1\u4e0a\u9650", + "desc": "Reverse \u63a5\u53e3\u5e76\u53d1\u4e0a\u9650\u3002" + }, + "timeout": { + "title": "\u8bf7\u6c42\u8d85\u65f6", + "desc": "Reverse \u63a5\u53e3\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" + }, + "stream_timeout": { + "title": "\u6d41\u7a7a\u95f2\u8d85\u65f6", + "desc": "\u6d41\u5f0f\u7a7a\u95f2\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" + } + }, + "video": { + "concurrent": { + "title": "\u5e76\u53d1\u4e0a\u9650", + "desc": "Reverse \u63a5\u53e3\u5e76\u53d1\u4e0a\u9650\u3002" + }, + "timeout": { + "title": "\u8bf7\u6c42\u8d85\u65f6", + "desc": "Reverse \u63a5\u53e3\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" + }, + "stream_timeout": { + "title": "\u6d41\u7a7a\u95f2\u8d85\u65f6", + "desc": "\u6d41\u5f0f\u7a7a\u95f2\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" + } + }, + "image": { + "timeout": { + "title": "\u8bf7\u6c42\u8d85\u65f6", + "desc": "WebSocket \u8bf7\u6c42\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" + }, + "stream_timeout": { + "title": "\u6d41\u7a7a\u95f2\u8d85\u65f6", + "desc": "WebSocket \u6d41\u5f0f\u7a7a\u95f2\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" + }, + "final_timeout": { + "title": "\u6700\u7ec8\u56fe\u8d85\u65f6", + "desc": "\u6536\u5230\u4e2d\u7b49\u56fe\u540e\u7b49\u5f85\u6700\u7ec8\u56fe\u7684\u8d85\u65f6\u79d2\u6570\u3002" + }, + "blocked_grace_seconds": { + "title": "\u5ba1\u67e5\u5bbd\u9650\u79d2\u6570", + "desc": "\u6536\u5230\u4e2d\u7b49\u56fe\u540e\uff0c\u5224\u5b9a\u7591\u4f3c\u88ab\u5ba1\u67e5\u7684\u5bbd\u9650\u79d2\u6570\uff08\u9ed8\u8ba4 10 \u79d2\uff0c\u53ef\u81ea\u5b9a\u4e49\uff09\u3002" + }, + "nsfw": { + "title": "NSFW \u6a21\u5f0f", + "desc": "WebSocket \u8bf7\u6c42\u662f\u5426\u542f\u7528 NSFW\u3002" + }, + "medium_min_bytes": { + "title": "\u4e2d\u7b49\u56fe\u6700\u5c0f\u5b57\u8282", + "desc": "\u5224\u5b9a\u4e2d\u7b49\u8d28\u91cf\u56fe\u7684\u6700\u5c0f\u5b57\u8282\u6570\u3002" + }, + "final_min_bytes": { + "title": "\u6700\u7ec8\u56fe\u6700\u5c0f\u5b57\u8282", + "desc": "\u5224\u5b9a\u6700\u7ec8\u56fe\u7684\u6700\u5c0f\u5b57\u8282\u6570\uff08\u901a\u5e38 JPG > 100KB\uff09\u3002" + }, + "blocked_parallel_enabled": { + "title": "\u542f\u7528\u5e76\u884c\u8865\u507f", + "desc": "\u7591\u4f3c\u5ba1\u67e5/\u62e6\u622a\u65f6\uff0c\u662f\u5426\u542f\u7528\u5e76\u884c\u8865\u507f\u751f\u6210\u3002" + }, + "blocked_parallel_attempts": { + "title": "\u62e6\u622a\u8865\u507f\u5e76\u53d1\u6b21\u6570", + "desc": "\u7591\u4f3c\u5ba1\u67e5/\u62e6\u622a\u5bfc\u81f4\u65e0\u6700\u7ec8\u56fe\u65f6\uff0c\u81ea\u52a8\u5e76\u884c\u8865\u507f\u751f\u6210\u6b21\u6570\u3002" + } + }, + "imagine_fast": { + "n": { + "title": "\u751f\u6210\u6570\u91cf", + "desc": "\u4ec5\u7528\u4e8e grok-imagine-1.0-fast \u7684\u670d\u52a1\u7aef\u7edf\u4e00\u751f\u6210\u6570\u91cf\uff081-10\uff09\u3002" + }, + "size": { + "title": "\u56fe\u7247\u5c3a\u5bf8", + "desc": "\u4ec5\u7528\u4e8e grok-imagine-1.0-fast \u7684\u670d\u52a1\u7aef\u7edf\u4e00\u5c3a\u5bf8\u3002" + }, + "response_format": { + "title": "\u54cd\u5e94\u683c\u5f0f", + "desc": "\u4ec5\u7528\u4e8e grok-imagine-1.0-fast \u7684\u670d\u52a1\u7aef\u7edf\u4e00\u8fd4\u56de\u683c\u5f0f\u3002" + } + }, + "asset": { + "upload_concurrent": { + "title": "\u4e0a\u4f20\u5e76\u53d1", + "desc": "\u4e0a\u4f20\u63a5\u53e3\u7684\u6700\u5927\u5e76\u53d1\u6570\u3002\u63a8\u8350 30\u3002" + }, + "upload_timeout": { + "title": "\u4e0a\u4f20\u8d85\u65f6", + "desc": "\u4e0a\u4f20\u63a5\u53e3\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002\u63a8\u8350 60\u3002" + }, + "download_concurrent": { + "title": "\u4e0b\u8f7d\u5e76\u53d1", + "desc": "\u4e0b\u8f7d\u63a5\u53e3\u7684\u6700\u5927\u5e76\u53d1\u6570\u3002\u63a8\u8350 30\u3002" + }, + "download_timeout": { + "title": "\u4e0b\u8f7d\u8d85\u65f6", + "desc": "\u4e0b\u8f7d\u63a5\u53e3\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002\u63a8\u8350 60\u3002" + }, + "list_concurrent": { + "title": "\u67e5\u8be2\u5e76\u53d1", + "desc": "\u8d44\u4ea7\u67e5\u8be2\u63a5\u53e3\u7684\u6700\u5927\u5e76\u53d1\u6570\u3002\u63a8\u8350 10\u3002" + }, + "list_timeout": { + "title": "\u67e5\u8be2\u8d85\u65f6", + "desc": "\u8d44\u4ea7\u67e5\u8be2\u63a5\u53e3\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002\u63a8\u8350 60\u3002" + }, + "list_batch_size": { + "title": "\u67e5\u8be2\u6279\u6b21\u5927\u5c0f", + "desc": "\u5355\u6b21\u67e5\u8be2\u53ef\u5904\u7406\u7684 Token \u6570\u91cf\u3002\u63a8\u8350 10\u3002" + }, + "delete_concurrent": { + "title": "\u5220\u9664\u5e76\u53d1", + "desc": "\u8d44\u4ea7\u5220\u9664\u63a5\u53e3\u7684\u6700\u5927\u5e76\u53d1\u6570\u3002\u63a8\u8350 10\u3002" + }, + "delete_timeout": { + "title": "\u5220\u9664\u8d85\u65f6", + "desc": "\u8d44\u4ea7\u5220\u9664\u63a5\u53e3\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002\u63a8\u8350 60\u3002" + }, + "delete_batch_size": { + "title": "\u5220\u9664\u6279\u6b21\u5927\u5c0f", + "desc": "\u5355\u6b21\u5220\u9664\u53ef\u5904\u7406\u7684 Token \u6570\u91cf\u3002\u63a8\u8350 10\u3002" + } + }, + "voice": { + "timeout": { + "title": "\u8bf7\u6c42\u8d85\u65f6", + "desc": "Voice \u8bf7\u6c42\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" + } + }, + "token": { + "auto_refresh": { + "title": "\u81ea\u52a8\u5237\u65b0", + "desc": "\u662f\u5426\u5f00\u542f Token \u81ea\u52a8\u5237\u65b0\u673a\u5236\u3002" + }, + "refresh_interval_hours": { + "title": "\u5237\u65b0\u95f4\u9694", + "desc": "\u666e\u901a Token \u5237\u65b0\u7684\u65f6\u95f4\u95f4\u9694\uff08\u5c0f\u65f6\uff09\u3002" + }, + "super_refresh_interval_hours": { + "title": "Super \u5237\u65b0\u95f4\u9694", + "desc": "Super Token \u5237\u65b0\u7684\u65f6\u95f4\u95f4\u9694\uff08\u5c0f\u65f6\uff09\u3002" + }, + "fail_threshold": { + "title": "\u5931\u8d25\u9608\u503c", + "desc": "\u5355\u4e2a Token \u8fde\u7eed\u5931\u8d25\u591a\u5c11\u6b21\u540e\u88ab\u6807\u8bb0\u4e3a\u4e0d\u53ef\u7528\u3002" + }, + "save_delay_ms": { + "title": "\u4fdd\u5b58\u5ef6\u8fdf", + "desc": "Token \u53d8\u66f4\u5408\u5e76\u5199\u5165\u7684\u5ef6\u8fdf\uff08\u6beb\u79d2\uff09\u3002" + }, + "usage_flush_interval_sec": { + "title": "\u7528\u91cf\u843d\u5e93\u95f4\u9694", + "desc": "\u7528\u91cf\u7c7b\u5b57\u6bb5\u5199\u5165\u6570\u636e\u5e93\u7684\u6700\u5c0f\u95f4\u9694\uff08\u79d2\uff09\u3002" + }, + "reload_interval_sec": { + "title": "\u540c\u6b65\u95f4\u9694", + "desc": "\u591a worker \u573a\u666f\u4e0b Token \u72b6\u6001\u5237\u65b0\u95f4\u9694\uff08\u79d2\uff09\u3002" + } + }, + "cache": { + "enable_auto_clean": { + "title": "\u81ea\u52a8\u6e05\u7406", + "desc": "\u662f\u5426\u542f\u7528\u7f13\u5b58\u81ea\u52a8\u6e05\u7406\uff0c\u5f00\u542f\u540e\u6309\u4e0a\u9650\u81ea\u52a8\u56de\u6536\u3002" + }, + "limit_mb": { + "title": "\u6e05\u7406\u9608\u503c", + "desc": "\u7f13\u5b58\u5927\u5c0f\u9608\u503c\uff08MB\uff09\uff0c\u8d85\u8fc7\u9608\u503c\u4f1a\u89e6\u53d1\u6e05\u7406\u3002" + } + }, + "nsfw": { + "concurrent": { + "title": "\u5e76\u53d1\u4e0a\u9650", + "desc": "\u6279\u91cf\u5f00\u542f NSFW \u6a21\u5f0f\u65f6\u7684\u5e76\u53d1\u8bf7\u6c42\u4e0a\u9650\u3002\u63a8\u8350 10\u3002" + }, + "batch_size": { + "title": "\u6279\u6b21\u5927\u5c0f", + "desc": "\u6279\u91cf\u5f00\u542f NSFW \u6a21\u5f0f\u7684\u5355\u6279\u5904\u7406\u6570\u91cf\u3002\u63a8\u8350 50\u3002" + }, + "timeout": { + "title": "\u8bf7\u6c42\u8d85\u65f6", + "desc": "NSFW \u5f00\u542f\u76f8\u5173\u8bf7\u6c42\u7684\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002\u63a8\u8350 60\u3002" + } + }, + "usage": { + "concurrent": { + "title": "\u5e76\u53d1\u4e0a\u9650", + "desc": "\u6279\u91cf\u5237\u65b0\u7528\u91cf\u65f6\u7684\u5e76\u53d1\u8bf7\u6c42\u4e0a\u9650\u3002\u63a8\u8350 10\u3002" + }, + "batch_size": { + "title": "\u6279\u6b21\u5927\u5c0f", + "desc": "\u6279\u91cf\u5237\u65b0\u7528\u91cf\u7684\u5355\u6279\u5904\u7406\u6570\u91cf\u3002\u63a8\u8350 50\u3002" + }, + "timeout": { + "title": "\u8bf7\u6c42\u8d85\u65f6", + "desc": "\u7528\u91cf\u67e5\u8be2\u63a5\u53e3\u7684\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002\u63a8\u8350 60\u3002" + } + } + } + }, + "logs": { + "pageTitle": "Grok2API - \u65e5\u5fd7\u67e5\u770b", + "title": "\u65e5\u5fd7\u67e5\u770b", + "subtitle": "\u6309\u6587\u4ef6\u3001\u7ea7\u522b\u548c\u5173\u952e\u8bcd\u5feb\u901f\u7b5b\u9009\uff0c\u5e76\u4ee5\u66f4\u6613\u8bfb\u7684\u65b9\u5f0f\u67e5\u770b\u7ed3\u6784\u5316\u65e5\u5fd7\u3002", + "filesTitle": "\u65e5\u5fd7\u6587\u4ef6", + "rawTitle": "\u539f\u59cb\u65e5\u5fd7", + "empty": "\u6ca1\u6709\u5339\u914d\u7684\u65e5\u5fd7\u8bb0\u5f55", + "filters": { + "file": "\u65e5\u5fd7\u6587\u4ef6", + "level": "\u7ea7\u522b", + "limit": "\u6761\u6570", + "keyword": "\u5173\u952e\u8bcd", + "keywordPlaceholder": "traceID / path / \u9519\u8bef\u5173\u952e\u8bcd", + "apply": "\u5e94\u7528\u7b5b\u9009", + "reset": "\u91cd\u7f6e", + "excludeAdminRoutes": "\u4ec5\u5728\u6b64\u9875\u8fc7\u6ee4 `/v1/admin/*`" + }, + "levels": { + "all": "\u5168\u90e8\u7ea7\u522b" + }, + "stats": { + "file": "\u5f53\u524d\u6587\u4ef6", + "entries": "\u5df2\u52a0\u8f7d\u6761\u76ee", + "entriesHint": "\u6309\u5f53\u524d\u7b5b\u9009\u5c55\u793a", + "errorWarn": "\u544a\u8b66 / \u9519\u8bef", + "errorWarnHint": "warning + error", + "size": "\u6587\u4ef6\u5927\u5c0f", + "sizeHint": "\u78c1\u76d8\u5360\u7528" + }, + "loadFilesFailed": "\u52a0\u8f7d\u65e5\u5fd7\u6587\u4ef6\u5931\u8d25", + "loadFailed": "\u52a0\u8f7d\u65e5\u5fd7\u5931\u8d25", + "noFiles": "\u6682\u65e0\u65e5\u5fd7\u6587\u4ef6", + "fileCount": "{count} \u4e2a\u65e5\u5fd7\u6587\u4ef6", + "deleteNone": "\u8bf7\u5148\u9009\u62e9\u8981\u6e05\u7406\u7684\u65e5\u5fd7\u6587\u4ef6", + "deleteConfirm": "\u786e\u8ba4\u6e05\u7406 {count} \u4e2a\u65e5\u5fd7\u6587\u4ef6\uff1f", + "deleteFailed": "\u6e05\u7406\u65e5\u5fd7\u5931\u8d25", + "deleteResult": "\u5df2\u6e05\u7406 {deleted} \u4e2a\u65e5\u5fd7\u6587\u4ef6\uff0c\u5931\u8d25 {failed} \u4e2a", + "toggle": { + "expand": "\u5c55\u5f00", + "collapse": "\u6536\u8d77" + }, + "autoRefresh": { + "buttonAria": "\u81ea\u52a8\u5237\u65b0", + "off": "\u81ea\u52a8\u5237\u65b0\uff1a\u5173", + "offShort": "\u5173\u95ed", + "interval": "\u81ea\u52a8\u5237\u65b0\uff1a{seconds}s", + "intervalLabel": "{seconds}s", + "changed": "\u81ea\u52a8\u5237\u65b0\uff1a{label}", + "disabled": "\u81ea\u52a8\u5237\u65b0\u5df2\u5173\u95ed" + }, + "deleteSelected": "\u6e05\u7406\u6240\u9009" + }, + "token": { + "pageTitle": "Grok2API - Token \u7ba1\u7406", + "title": "Token \u5217\u8868", + "subtitle": "\u7ba1\u7406 Grok2API \u7684 Token \u670d\u52a1\u53f7\u6c60\u3002", + "import": "\u5bfc\u5165", + "add": "\u6dfb\u52a0", + "statTotal": "Token \u603b\u6570", + "statActive": "Token \u6b63\u5e38", + "statCooling": "Token \u9650\u6d41", + "statInvalid": "Token \u5931\u6548", + "statChatQuota": "Chat \u5269\u4f59", + "statImageQuota": "Image \u5269\u4f59", + "statVideoQuota": "Video \u5269\u4f59", + "statVideoUnavailable": "\u65e0\u6cd5\u7edf\u8ba1", + "statTotalCalls": "\u603b\u8c03\u7528\u6b21\u6570", + "tabAll": "\u5168\u90e8", + "tabActive": "\u6b63\u5e38", + "tabCooling": "\u9650\u6d41", + "tabExpired": "\u5931\u6548", + "tabNsfw": "\u5df2\u5f00 NSFW", + "tabNoNsfw": "\u672a\u5f00 NSFW", + "statusFilterAria": "Token \u72b6\u6001\u7b5b\u9009", + "tableToken": "Token", + "tableType": "\u7c7b\u578b", + "tableStatus": "\u72b6\u6001", + "tableQuota": "\u989d\u5ea6", + "tableNote": "\u5907\u6ce8", + "tableActions": "\u64cd\u4f5c", + "refreshStatus": "\u5237\u65b0\u72b6\u6001", + "emptyState": "\u6682\u65e0 Token\uff0c\u8bf7\u70b9\u51fb\u53f3\u4e0a\u89d2\u5bfc\u5165\u6216\u6dfb\u52a0\u3002", + "emptyFilterState": "\u5f53\u524d\u7b5b\u9009\u65e0\u7ed3\u679c\uff0c\u8bf7\u5207\u6362\u7b5b\u9009\u6761\u4ef6\u3002", + "selectPage": "\u5168\u9009\u672c\u9875", + "selectAllFiltered": "\u5168\u9009\u5168\u90e8", + "clearSelection": "\u53d6\u6d88\u5168\u9009", + "prevPage": "\u4e0a\u4e00\u9875", + "nextPage": "\u4e0b\u4e00\u9875", + "perPage": "{size} / \u9875", + "pagination": "\u7b2c {current} / {total} \u9875 \u00b7 \u5171 {count} \u6761", + "batchExport": "\u5bfc\u51fa", + "batchRefresh": "\u5237\u65b0", + "batchDisable": "\u7981\u7528", + "batchEnable": "\u542f\u7528", + "batchDelete": "\u5220\u9664", + "importTitle": "\u6279\u91cf\u5bfc\u5165 Token", + "importTargetPool": "\u76ee\u6807 Pool", + "importTokenList": "Token \u5217\u8868 (\u6bcf\u884c\u4e00\u4e2a)", + "importPlaceholder": "\u7c98\u8d34 Token\uff0c\u4e00\u884c\u4e00\u4e2a...", + "startImport": "\u5f00\u59cb\u5bfc\u5165", + "editTitle": "\u7f16\u8f91 Token", + "addTitle": "\u6dfb\u52a0 Token", + "editType": "\u7c7b\u578b", + "editQuota": "\u989d\u5ea6", + "editNote": "\u5907\u6ce8", + "editNotePlaceholder": "\u53ef\u9009\u5907\u6ce8", + "tokenEmpty": "Token \u4e0d\u80fd\u4e3a\u7a7a", + "tokenExists": "Token \u5df2\u5b58\u5728", + "confirmDelete": "\u786e\u5b9a\u8981\u5220\u9664\u6b64 Token \u5417\uff1f", + "confirmDisable": "\u786e\u5b9a\u8981\u7981\u7528\u8be5 Token \u5417\uff1f\n{token}", + "confirmEnable": "\u786e\u5b9a\u8981\u542f\u7528\u8be5 Token \u5417\uff1f\n{token}", + "confirmBatchDelete": "\u786e\u5b9a\u8981\u5220\u9664\u9009\u4e2d\u7684 {count} \u4e2a Token \u5417\uff1f", + "confirmBatchDisable": "\u786e\u5b9a\u8981\u7981\u7528\u9009\u4e2d\u7684 {count} \u4e2a Token \u5417\uff1f", + "confirmBatchEnable": "\u786e\u5b9a\u8981\u542f\u7528\u9009\u4e2d\u7684 {count} \u4e2a Token \u5417\uff1f", + "disableToken": "\u7981\u7528", + "enableToken": "\u542f\u7528", + "disableDone": "Token \u5df2\u7981\u7528", + "enableDone": "Token \u5df2\u542f\u7528", + "batchDisableDone": "\u6279\u91cf\u7981\u7528\u5b8c\u6210", + "batchEnableDone": "\u6279\u91cf\u542f\u7528\u5b8c\u6210", + "noTokenToDisable": "\u9009\u4e2d\u7684 Token \u5df2\u5168\u90e8\u662f\u7981\u7528\u72b6\u6001", + "noTokenToEnable": "\u9009\u4e2d\u7684 Token \u5df2\u5168\u90e8\u662f\u542f\u7528\u72b6\u6001", + "deleteDone": "\u5220\u9664\u5b8c\u6210", + "refreshDone": "\u5237\u65b0\u5b8c\u6210", + "refreshSuccess": "\u5237\u65b0\u6210\u529f", + "refreshFailed": "\u5237\u65b0\u5931\u8d25", + "refreshError": "\u5237\u65b0\u5931\u8d25: {msg}", + "requestError": "\u8bf7\u6c42\u9519\u8bef", + "notValidJson": "\u54cd\u5e94\u4e0d\u662f\u6709\u6548 JSON (HTTP {status})", + "listEmpty": "\u5217\u8868\u4e3a\u7a7a", + "stopRefresh": "\u5df2\u7ec8\u6b62\u5237\u65b0", + "stopDelete": "\u5df2\u7ec8\u6b62\u5220\u9664", + "stopNsfw": "\u5df2\u7ec8\u6b62 NSFW", + "nsfwConfirm": "\u662f\u5426\u4e3a\u9009\u4e2d\u7684 {count} \u4e2a Token \u5f00\u542f NSFW \u6a21\u5f0f\uff1f", + "nsfwEnable": "\u5f00\u542f NSFW", + "nsfwDone": "NSFW \u5f00\u542f\u5b8c\u6210", + "nsfwResult": "NSFW \u5f00\u542f\u5b8c\u6210\uff1a\u6210\u529f {ok}\uff0c\u5931\u8d25 {fail}", + "nsfwFailed": "\u5f00\u542f\u5931\u8d25: {msg}", + "emptyResponse": "\u7a7a\u54cd\u5e94 (HTTP {status})" + }, + "cache": { + "pageTitle": "Grok2API - \u7f13\u5b58\u7ba1\u7406", + "title": "\u7f13\u5b58\u7ba1\u7406", + "subtitle": "\u7ba1\u7406\u672c\u5730\u8d44\u6e90\u4e0e\u5728\u7ebf\u8d44\u4ea7\u7f13\u5b58\u3002", + "localImages": "\u672c\u5730\u56fe\u7247", + "localVideos": "\u672c\u5730\u89c6\u9891", + "onlineAssets": "\u5728\u7ebf\u8d44\u4ea7", + "tableFile": "\u6587\u4ef6", + "tableSize": "\u5927\u5c0f", + "tableTime": "\u65f6\u95f4", + "tableActions": "\u64cd\u4f5c", + "tableToken": "Token", + "tableType": "\u7c7b\u578b", + "tableAssetCount": "\u8d44\u4ea7\u6570", + "tableLastClear": "\u4e0a\u6b21\u6e05\u7a7a\u65f6\u95f4", + "notLoaded": "\u672a\u52a0\u8f7d", + "noAccounts": "\u6682\u65e0\u53ef\u7528\u8d26\u53f7", + "noFiles": "\u6682\u65e0\u6587\u4ef6", + "noTokenAvailable": "\u65e0\u53ef\u7528 Token", + "cannotConnect": "\u65e0\u6cd5\u8fde\u63a5", + "lastClear": "\u4e0a\u6b21\u6e05\u7a7a\uff1a{time}", + "clear": "\u6e05\u7a7a", + "clean": "\u6e05\u7406", + "confirmClearImage": "\u786e\u5b9a\u8981\u6e05\u7a7a\u672c\u5730\u56fe\u7247\u7f13\u5b58\u5417\uff1f", + "confirmClearVideo": "\u786e\u5b9a\u8981\u6e05\u7a7a\u672c\u5730\u89c6\u9891\u7f13\u5b58\u5417\uff1f", + "clearSuccess": "\u6e05\u7406\u6210\u529f\uff0c\u91ca\u653e {size} MB", + "clearFailed": "\u6e05\u7406\u5931\u8d25", + "confirmDeleteFile": "\u786e\u5b9a\u8981\u5220\u9664\u8be5\u6587\u4ef6\u5417\uff1f", + "confirmBatchDeleteFiles": "\u786e\u5b9a\u8981\u5220\u9664\u9009\u4e2d\u7684 {count} \u4e2a\u6587\u4ef6\u5417\uff1f", + "noFilesSelected": "\u672a\u9009\u62e9\u6587\u4ef6", + "deletedFiles": "\u5df2\u5220\u9664 {count} \u4e2a\u6587\u4ef6", + "deleteResult": "\u5220\u9664\u5b8c\u6210\uff1a\u6210\u529f {success}\uff0c\u5931\u8d25 {failed}", + "loadStatsFailed": "\u52a0\u8f7d\u7edf\u8ba1\u5931\u8d25", + "selectAccountsToLoad": "\u8bf7\u9009\u62e9\u8981\u52a0\u8f7d\u7684\u8d26\u53f7", + "selectAccountsToClear": "\u8bf7\u9009\u62e9\u8981\u6e05\u7a7a\u7684\u8d26\u53f7", + "confirmClearAccounts": "\u786e\u5b9a\u8981\u6e05\u7a7a\u9009\u4e2d\u7684 {count} \u4e2a\u8d26\u53f7\u5728\u7ebf\u8d44\u4ea7\u5417\uff1f", + "confirmClearAccount": "\u786e\u5b9a\u8981\u6e05\u7a7a\u8d26\u53f7 {label} \u7684\u5728\u7ebf\u8d44\u4ea7\u5417\uff1f", + "batchCleanInProgress": "\u6b63\u5728\u6279\u91cf\u6e05\u7406\u5728\u7ebf\u8d44\u4ea7\uff0c\u8bf7\u7a0d\u5019...", + "cleanInProgress": "\u6b63\u5728\u6e05\u7406\u5728\u7ebf\u8d44\u4ea7\uff0c\u8bf7\u7a0d\u5019...", + "batchCleanDone": "\u6279\u91cf\u6e05\u7406\u5b8c\u6210", + "cleanResult": "\u6e05\u7406\u5b8c\u6210 (\u6210\u529f: {success}, \u5931\u8d25: {failed})", + "cleanFailedEntry": "\u6e05\u7406\u5931\u8d25", + "requestTimeout": "\u8bf7\u6c42\u8d85\u65f6\u6216\u5931\u8d25", + "loadDone": "\u52a0\u8f7d\u5b8c\u6210", + "loadingStatus": "\u52a0\u8f7d\u4e2d", + "loadStopped": "\u5df2\u7ec8\u6b62\u52a0\u8f7d", + "loadFailedMsg": "\u52a0\u8f7d\u5931\u8d25: {msg}", + "stoppedLoadRequests": "\u5df2\u7ec8\u6b62\u5269\u4f59\u52a0\u8f7d\u8bf7\u6c42", + "cleanStopped": "\u5df2\u7ec8\u6b62\u6e05\u7406", + "cleanFailedMsg": "\u6e05\u7406\u5931\u8d25: {msg}", + "stoppedCleanRequests": "\u5df2\u7ec8\u6b62\u5269\u4f59\u6e05\u7406\u8bf7\u6c42" + }, + "chat": { + "pageTitle": "Grok2API - Chat \u804a\u5929", + "title": "Chat \u804a\u5929", + "subtitle": "\u901a\u8fc7 chat \u63a5\u53e3\u4e0e Grok \u8fdb\u884c\u4f1a\u8bdd\u804a\u5929", + "sessions": "\u4f1a\u8bdd", + "newChat": "\u65b0\u589e", + "collapseSidebar": "\u6298\u53e0", + "expandSidebar": "\u5c55\u5f00", + "sessionList": "\u4f1a\u8bdd\u5217\u8868", + "newSession": "\u65b0\u4f1a\u8bdd", + "ready": "\u5c31\u7eea", + "emptyState": "\u8f93\u5165\u4e00\u6761\u6d88\u606f\u5f00\u59cb\u5bf9\u8bdd\u3002", + "uploadFile": "\u4e0a\u4f20\u6587\u4ef6", + "placeholder": "\u8be2\u95ee\u4efb\u4f55\u5185\u5bb9", + "settings": "\u8bbe\u7f6e", + "send": "\u53d1\u9001", + "temperature": "\u6e29\u5ea6", + "topP": "Top P", + "systemPrompt": "System Prompt", + "systemPromptPlaceholder": "\u53ef\u9009\uff0c\u8bbe\u7f6e\u7cfb\u7edf\u63d0\u793a\u8bcd", + "fileLabel": "[\u6587\u4ef6]", + "compositeContent": "[\u590d\u5408\u5185\u5bb9]", + "storageFull": "\u672c\u5730\u5b58\u50a8\u7a7a\u95f4\u4e0d\u8db3\uff0c\u90e8\u5206\u4f1a\u8bdd\u672a\u4fdd\u5b58", + "attachmentNoEdit": "\u9644\u4ef6\u6d88\u606f\u6682\u4e0d\u652f\u6301\u7f16\u8f91", + "saveEdit": "\u4fdd\u5b58", + "saveEditTitle": "\u4fdd\u5b58\u7f16\u8f91", + "cancelEdit": "\u53d6\u6d88", + "cancelEditTitle": "\u53d6\u6d88\u7f16\u8f91", + "contentEmpty": "\u5185\u5bb9\u4e0d\u80fd\u4e3a\u7a7a", + "editMessage": "\u7f16\u8f91", + "editMessageTitle": "\u7f16\u8f91\u6d88\u606f\u5185\u5bb9", + "regenerate": "\u91cd\u65b0\u751f\u6210", + "regenerateTitle": "\u4ece\u6b64\u5904\u91cd\u65b0\u751f\u6210\u56de\u590d", + "retryTitle": "\u91cd\u8bd5\u4e0a\u4e00\u6761\u56de\u7b54", + "editAnswer": "\u7f16\u8f91", + "editAnswerTitle": "\u7f16\u8f91\u56de\u7b54\u5185\u5bb9", + "copyAnswer": "\u590d\u5236", + "copyAnswerTitle": "\u590d\u5236\u56de\u7b54\u5185\u5bb9", + "feedback": "\u53cd\u9988", + "feedbackTitle": "\u53cd\u9988\u5230 Grok2API", + "noContentToCopy": "\u6682\u65e0\u5185\u5bb9\u53ef\u590d\u5236", + "noChatToRetry": "\u6ca1\u6709\u53ef\u91cd\u8bd5\u7684\u5bf9\u8bdd", + "requestFailedCheck": "\u8bf7\u6c42\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u670d\u52a1\u72b6\u6001", + "requestFailedStatus": "\u8bf7\u6c42\u5931\u8d25: {status}", + "clickRetry": "\u70b9\u51fb\u91cd\u8bd5", + "thinking": "\u601d\u8003\u4e2d", + "thinkingSec": "\u601d\u8003 {sec} \u79d2", + "thought": "\u5df2\u601d\u8003", + "thinkLabel": "\u601d\u8003", + "empty": "\uff08\u7a7a\uff09", + "toolWebSearch": "\u7f51\u9875\u641c\u7d22", + "toolImageSearch": "\u56fe\u7247\u641c\u7d22", + "toolAgentThink": "\u601d\u8003\u63a8\u7406", + "toolDefault": "\u5de5\u5177" + }, + "imagine": { + "pageTitle": "Grok2API - Imagine \u7011\u5e03\u6d41", + "title": "Imagine \u7011\u5e03\u6d41", + "subtitle": "\u901a\u8fc7 WebSocket \u6301\u7eed\u751f\u6210\u56fe\u7247\uff0c\u5b9e\u65f6\u5c55\u793a base64 \u7011\u5e03\u6d41\u3002", + "genSettings": "\u751f\u6210\u8bbe\u7f6e", + "prompt": "\u63d0\u793a\u8bcd", + "promptPlaceholder": "\u63cf\u8ff0\u4f60\u60f3\u8981\u7684\u753b\u9762\uff0c\u4f8b\u5982\uff1a\u672a\u6765\u57ce\u5e02\u9713\u8679\u96e8\u591c\uff0c\u5e7f\u89d2\u6444\u5f71", + "autoFollow": "\u81ea\u52a8\u8ddf\u968f", + "autoFollowDesc": "\u6eda\u52a8\u5230\u6700\u65b0", + "autoSave": "\u81ea\u52a8\u4fdd\u5b58", + "autoSaveDesc": "\u81ea\u52a8\u4fdd\u5b58\u56fe\u7247", + "aspectRatio": "\u5bbd\u9ad8\u6bd4", + "ratio2_3": "2:3 \u7ad6\u56fe", + "ratio1_1": "1:1 \u65b9\u56fe", + "ratio3_2": "3:2 \u6a2a\u56fe", + "ratio16_9": "16:9 \u5bbd\u5c4f", + "ratio9_16": "9:16 \u7ad6\u5c4f", + "concurrent": "\u5e76\u53d1\u6570\u91cf", + "tasks": "{n} \u4e2a\u4efb\u52a1", + "concurrentTask1": "1 \u4e2a\u4efb\u52a1", + "concurrentTask2": "2 \u4e2a\u4efb\u52a1", + "concurrentTask3": "3 \u4e2a\u4efb\u52a1", + "autoFilter": "\u81ea\u52a8\u8fc7\u6ee4", + "autoFilterDesc": "\u8fc7\u6ee4\u4e0d\u8fbe\u6807\u56fe\u7247", + "nsfwLabel": "\u662f\u5426 NSFW", + "nsfwOn": "\u5f00\u542f", + "nsfwOff": "\u5173\u95ed", + "saveLocation": "\u4fdd\u5b58\u4f4d\u7f6e", + "browserDefault": "\u6d4f\u89c8\u5668\u9ed8\u8ba4\u4f4d\u7f6e", + "reverseOrder": "\u53cd\u5411\u65b0\u589e", + "reverseOrderDesc": "\u6700\u65b0\u663e\u793a\u5728\u4e0a\u65b9", + "connectionStatus": "\u8fde\u63a5\u72b6\u6001", + "connectionMode": "\u8fde\u63a5\u6a21\u5f0f", + "connectionModeAria": "\u8fde\u63a5\u6a21\u5f0f", + "imageCount": "\u56fe\u7247\u6570\u91cf", + "activeTasks": "\u6d3b\u8dc3\u4efb\u52a1", + "avgTime": "\u5e73\u5747\u8017\u65f6", + "gallery": "\u7011\u5e03\u6d41", + "waitingTitle": "\u7b49\u5f85\u8fde\u63a5\u4e2d", + "waitingSubtitle": "\u542f\u52a8\u4efb\u52a1\u540e\uff0c\u751f\u6210\u7684\u56fe\u7247\u4f1a\u81ea\u52a8\u6c47\u805a\u5230\u8fd9\u91cc", + "start": "\u5f00\u59cb", + "batch": "\u6279\u91cf", + "batchDownloadTitle": "\u6279\u91cf\u4e0b\u8f7d", + "selectAll": "\u5168\u9009", + "deselectAll": "\u53d6\u6d88\u5168\u9009", + "download": "\u4e0b\u8f7d", + "startedTasks": "\u5df2\u542f\u52a8 {count} \u4e2a\u5e76\u53d1\u4efb\u52a1", + "startedTasksSSE": "\u5df2\u542f\u52a8 {count} \u4e2a\u5e76\u53d1\u4efb\u52a1 (SSE)", + "generatingSSE": "\u751f\u6210\u4e2d (SSE)", + "selectFolder": "\u5df2\u9009\u62e9\u6587\u4ef6\u5939: {name}", + "selectFolderFailed": "\u9009\u62e9\u6587\u4ef6\u5939\u5931\u8d25", + "noImagesSelected": "\u8bf7\u5148\u9009\u62e9\u8981\u4e0b\u8f7d\u7684\u56fe\u7247", + "jszipFailed": "JSZip \u5e93\u52a0\u8f7d\u5931\u8d25\uff0c\u8bf7\u5237\u65b0\u9875\u9762\u91cd\u8bd5", + "packing": "\u6b63\u5728\u6253\u5305 {count} \u5f20\u56fe\u7247...", + "packingBtn": "\u6253\u5305\u4e2d...", + "packingProgress": "\u6253\u5305\u4e2d... ({done}/{total})", + "noImagesDownloaded": "\u6ca1\u6709\u6210\u529f\u83b7\u53d6\u4efb\u4f55\u56fe\u7247", + "generatingZip": "\u751f\u6210\u538b\u7f29\u5305...", + "packSuccess": "\u6210\u529f\u6253\u5305 {count} \u5f20\u56fe\u7247", + "packFailed": "\u6253\u5305\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5" }, - "levels": { - "all": "全部级别" + "video": { + "pageTitle": "Grok2API - Video \u89c6\u9891\u751f\u6210", + "title": "Video \u89c6\u9891\u751f\u6210", + "subtitle": "\u751f\u6210\u77ed\u89c6\u9891\uff0c\u652f\u6301\u53c2\u8003\u56fe\u4e0e\u591a\u79cd\u9884\u8bbe\u98ce\u683c\u3002", + "startGenerate": "\u5f00\u59cb\u751f\u6210", + "genSettings": "\u751f\u6210\u8bbe\u7f6e", + "prompt": "\u63d0\u793a\u8bcd", + "promptPlaceholder": "\u4f8b\u5982\uff1a\u8857\u5934\u9713\u8679\u96e8\u591c\uff0c\u6162\u955c\u5934\uff0c\u80f6\u7247\u8d28\u611f", + "referenceImage": "\u53c2\u8003\u56fe", + "referenceImagePlaceholder": "https://... \u6216 data:image/...", + "aspectRatio": "\u753b\u9762\u6bd4\u4f8b", + "ratio3_2": "3:2 \u6a2a\u6784\u56fe", + "ratio2_3": "2:3 \u7ad6\u6784\u56fe", + "ratio16_9": "16:9 \u5bbd\u5c4f", + "ratio9_16": "9:16 \u7ad6\u5c4f", + "ratio1_1": "1:1 \u65b9\u5f62", + "duration": "\u89c6\u9891\u65f6\u957f", + "seconds": "{n} \u79d2", + "resolution": "\u5206\u8fa8\u7387", + "stylePreset": "\u98ce\u683c\u9884\u8bbe", + "upload": "\u4e0a\u4f20", + "clearImage": "\u6e05\u9664", + "runStatus": "\u8fd0\u884c\u72b6\u6001", + "elapsedTime": "\u8017\u65f6 {sec}s", + "elapsedTimeNone": "\u8017\u65f6 -", + "metaRatio": "\u6bd4\u4f8b", + "metaDuration": "\u65f6\u957f", + "metaResolution": "\u5206\u8fa8\u7387", + "metaPreset": "\u9884\u8bbe", + "videoPreview": "\u89c6\u9891\u9884\u89c8", + "clearPreview": "\u6e05\u7a7a", + "waitingGenerate": "\u7b49\u5f85\u751f\u6210\u89c6\u9891", + "videoTitle": "\u89c6\u9891 {n}", + "open": "\u6253\u5f00", + "generatingPlaceholder": "\u751f\u6210\u4e2d\u2026", + "superResolution": "\u8d85\u5206\u8fa8\u7387", + "superResolutionInProgress": "\u8d85\u5206\u8fa8\u7387\u4e2d", + "alreadyGenerating": "\u5df2\u5728\u751f\u6210\u4e2d", + "referenceConflict": "\u53c2\u8003\u56fe\u53ea\u80fd\u9009\u62e9\u5176\u4e00\uff1aURL/Base64 \u6216 \u672c\u5730\u4e0a\u4f20", + "downloadFailed": "\u4e0b\u8f7d\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u89c6\u9891\u94fe\u63a5\u662f\u5426\u53ef\u8bbf\u95ee", + "sec6": "6 \u79d2", + "sec10": "10 \u79d2", + "sec15": "15 \u79d2" }, - "stats": { - "file": "当前文件", - "entries": "已加载条目", - "entriesHint": "按当前筛选展示", - "errorWarn": "告警 / 错误", - "errorWarnHint": "warning + error", - "size": "文件大小", - "sizeHint": "磁盘占用" + "voice": { + "pageTitle": "Grok2API - LiveKit \u966a\u804a", + "title": "LiveKit \u966a\u804a", + "subtitle": "LiveKit \u8bed\u97f3\u4f1a\u8bdd\uff0c\u8fde\u63a5 Grok Voice\u3002", + "startSession": "\u5f00\u59cb\u4f1a\u8bdd", + "stopSession": "\u505c\u6b62\u4f1a\u8bdd", + "connectionSettings": "\u8fde\u63a5\u8bbe\u7f6e", + "voiceLabel": "\u58f0\u97f3 (Voice)", + "personalityLabel": "\u4e2a\u6027 (Personality)", + "speedLabel": "\u8bed\u901f", + "settingsHint": "\u63d0\u793a\uff1aVoice / Personality \u4f1a\u5728\u5efa\u7acb\u8fde\u63a5\u524d\u53d1\u9001\u5230\u670d\u52a1\u7aef\u3002", + "sessionLog": "\u4f1a\u8bdd\u65e5\u5fd7", + "clearLog": "\u6e05\u7a7a", + "copyLog": "\u590d\u5236", + "sessionStatus": "\u4f1a\u8bdd\u72b6\u6001", + "audioOutput": "\u97f3\u9891\u8f93\u51fa", + "audioHint": "\u8ba2\u9605\u5230\u7684\u97f3\u8f68\u4f1a\u81ea\u52a8\u63d2\u5165\u6b64\u533a\u57df\u3002", + "livekitSDKError": "\u9519\u8bef: LiveKit SDK \u672a\u80fd\u6b63\u786e\u52a0\u8f7d\uff0c\u8bf7\u5237\u65b0\u9875\u9762\u91cd\u8bd5", + "livekitLoadFailed": "LiveKit SDK \u52a0\u8f7d\u5931\u8d25", + "secureContextBrowser": "\u8bf7\u4f7f\u7528\u6700\u65b0\u7248\u6d4f\u89c8\u5668\u5e76\u5141\u8bb8\u9ea6\u514b\u98ce\u6743\u9650", + "secureContextHTTPS": "\u8bf7\u4f7f\u7528 HTTPS \u6216\u5728\u672c\u673a localhost \u8bbf\u95ee", + "secureContextError": "\u5f53\u524d\u73af\u5883\u4e0d\u652f\u6301\u9ea6\u514b\u98ce\u6743\u9650\uff0c{hint}", + "connectingStatus": "\u6b63\u5728\u8fde\u63a5", + "fetchingToken": "\u6b63\u5728\u83b7\u53d6 Token...", + "fetchTokenFailed": "\u83b7\u53d6 Token \u5931\u8d25: {status}", + "fetchTokenSuccess": "\u83b7\u53d6 Token \u6210\u529f", + "participantConnected": "\u53c2\u4e0e\u8005\u5df2\u8fde\u63a5: {identity}", + "participantDisconnected": "\u53c2\u4e0e\u8005\u5df2\u65ad\u5f00: {identity}", + "trackSubscribed": "\u8ba2\u9605\u97f3\u8f68: {kind}", + "disconnected": "\u5df2\u65ad\u5f00\u8fde\u63a5", + "connectedToServer": "\u5df2\u8fde\u63a5\u5230 LiveKit \u670d\u52a1\u5668", + "inCall": "\u901a\u8bdd\u4e2d", + "openingMic": "\u6b63\u5728\u5f00\u542f\u9ea6\u514b\u98ce...", + "voiceEnabled": "\u8bed\u97f3\u5df2\u5f00\u542f", + "voiceConnected": "\u8bed\u97f3\u8fde\u63a5\u6210\u529f", + "errorPrefix": "\u9519\u8bef: {msg}", + "logCopied": "\u65e5\u5fd7\u5df2\u590d\u5236", + "copyLogFailed": "\u590d\u5236\u5931\u8d25\uff0c\u8bf7\u624b\u52a8\u9009\u62e9" } - }, - "token": { - "pageTitle": "Grok2API - Token 管理", - "title": "Token 列表", - "subtitle": "管理 Grok2API 的 Token 服务号池。", - "import": "导入", - "add": "添加", - "statTotal": "Token 总数", - "statActive": "Token 正常", - "statCooling": "Token 限流", - "statInvalid": "Token 失效", - "statChatQuota": "Chat 剩余", - "statImageQuota": "Image 剩余", - "statVideoQuota": "Video 剩余", - "statVideoUnavailable": "无法统计", - "statTotalCalls": "总调用次数", - "tabAll": "全部", - "tabActive": "正常", - "tabCooling": "限流", - "tabExpired": "失效", - "tabNsfw": "已开 NSFW", - "tabNoNsfw": "未开 NSFW", - "statusFilterAria": "Token 状态筛选", - "tableToken": "Token", - "tableType": "类型", - "tableStatus": "状态", - "tableQuota": "额度", - "tableNote": "备注", - "tableActions": "操作", - "refreshStatus": "刷新状态", - "emptyState": "暂无 Token,请点击右上角导入或添加。", - "emptyFilterState": "当前筛选无结果,请切换筛选条件。", - "selectPage": "全选本页", - "selectAllFiltered": "全选全部", - "clearSelection": "取消全选", - "prevPage": "上一页", - "nextPage": "下一页", - "perPage": "{size} / 页", - "pagination": "第 {current} / {total} 页 · 共 {count} 条", - "batchExport": "导出", - "batchRefresh": "刷新", - "batchDisable": "禁用", - "batchEnable": "启用", - "batchDelete": "删除", - "importTitle": "批量导入 Token", - "importTargetPool": "目标 Pool", - "importTokenList": "Token 列表 (每行一个)", - "importPlaceholder": "粘贴 Token,一行一个...", - "startImport": "开始导入", - "editTitle": "编辑 Token", - "addTitle": "添加 Token", - "editType": "类型", - "editQuota": "额度", - "editNote": "备注", - "editNotePlaceholder": "可选备注", - "tokenEmpty": "Token 不能为空", - "tokenExists": "Token 已存在", - "confirmDelete": "确定要删除此 Token 吗?", - "confirmDisable": "确定要禁用该 Token 吗?\n{token}", - "confirmEnable": "确定要启用该 Token 吗?\n{token}", - "confirmBatchDelete": "确定要删除选中的 {count} 个 Token 吗?", - "confirmBatchDisable": "确定要禁用选中的 {count} 个 Token 吗?", - "confirmBatchEnable": "确定要启用选中的 {count} 个 Token 吗?", - "disableToken": "禁用", - "enableToken": "启用", - "disableDone": "Token 已禁用", - "enableDone": "Token 已启用", - "batchDisableDone": "批量禁用完成", - "batchEnableDone": "批量启用完成", - "noTokenToDisable": "选中的 Token 已全部是禁用状态", - "noTokenToEnable": "选中的 Token 已全部是启用状态", - "deleteDone": "删除完成", - "refreshDone": "刷新完成", - "refreshSuccess": "刷新成功", - "refreshFailed": "刷新失败", - "refreshError": "刷新失败: {msg}", - "requestError": "请求错误", - "notValidJson": "响应不是有效 JSON (HTTP {status})", - "listEmpty": "列表为空", - "stopRefresh": "已终止刷新", - "stopDelete": "已终止删除", - "stopNsfw": "已终止 NSFW", - "nsfwConfirm": "是否为选中的 {count} 个 Token 开启 NSFW 模式?", - "nsfwEnable": "开启 NSFW", - "nsfwDone": "NSFW 开启完成", - "nsfwResult": "NSFW 开启完成:成功 {ok},失败 {fail}", - "nsfwFailed": "开启失败: {msg}", - "emptyResponse": "空响应 (HTTP {status})" - }, - "cache": { - "pageTitle": "Grok2API - 缓存管理", - "title": "缓存管理", - "subtitle": "管理本地资源与在线资产缓存。", - "localImages": "本地图片", - "localVideos": "本地视频", - "onlineAssets": "在线资产", - "tableFile": "文件", - "tableSize": "大小", - "tableTime": "时间", - "tableActions": "操作", - "tableToken": "Token", - "tableType": "类型", - "tableAssetCount": "资产数", - "tableLastClear": "上次清空时间", - "notLoaded": "未加载", - "noAccounts": "暂无可用账号", - "noFiles": "暂无文件", - "noTokenAvailable": "无可用 Token", - "cannotConnect": "无法连接", - "lastClear": "上次清空:{time}", - "clear": "清空", - "clean": "清理", - "confirmClearImage": "确定要清空本地图片缓存吗?", - "confirmClearVideo": "确定要清空本地视频缓存吗?", - "clearSuccess": "清理成功,释放 {size} MB", - "clearFailed": "清理失败", - "confirmDeleteFile": "确定要删除该文件吗?", - "confirmBatchDeleteFiles": "确定要删除选中的 {count} 个文件吗?", - "noFilesSelected": "未选择文件", - "deletedFiles": "已删除 {count} 个文件", - "deleteResult": "删除完成:成功 {success},失败 {failed}", - "loadStatsFailed": "加载统计失败", - "selectAccountsToLoad": "请选择要加载的账号", - "selectAccountsToClear": "请选择要清空的账号", - "confirmClearAccounts": "确定要清空选中的 {count} 个账号在线资产吗?", - "confirmClearAccount": "确定要清空账号 {label} 的在线资产吗?", - "batchCleanInProgress": "正在批量清理在线资产,请稍候...", - "cleanInProgress": "正在清理在线资产,请稍候...", - "batchCleanDone": "批量清理完成", - "cleanResult": "清理完成 (成功: {success}, 失败: {failed})", - "cleanFailedEntry": "清理失败", - "requestTimeout": "请求超时或失败", - "loadDone": "加载完成", - "loadingStatus": "加载中", - "loadStopped": "已终止加载", - "loadFailedMsg": "加载失败: {msg}", - "stoppedLoadRequests": "已终止剩余加载请求", - "cleanStopped": "已终止清理", - "cleanFailedMsg": "清理失败: {msg}", - "stoppedCleanRequests": "已终止剩余清理请求" - }, - "chat": { - "pageTitle": "Grok2API - Chat 聊天", - "title": "Chat 聊天", - "subtitle": "通过 chat 接口与 Grok 进行会话聊天", - "sessions": "会话", - "newChat": "新增", - "collapseSidebar": "折叠", - "expandSidebar": "展开", - "sessionList": "会话列表", - "newSession": "新会话", - "ready": "就绪", - "emptyState": "输入一条消息开始对话。", - "uploadFile": "上传文件", - "placeholder": "询问任何内容", - "settings": "设置", - "send": "发送", - "temperature": "温度", - "topP": "Top P", - "systemPrompt": "System Prompt", - "systemPromptPlaceholder": "可选,设置系统提示词", - "fileLabel": "[文件]", - "compositeContent": "[复合内容]", - "storageFull": "本地存储空间不足,部分会话未保存", - "attachmentNoEdit": "附件消息暂不支持编辑", - "saveEdit": "保存", - "saveEditTitle": "保存编辑", - "cancelEdit": "取消", - "cancelEditTitle": "取消编辑", - "contentEmpty": "内容不能为空", - "editMessage": "编辑", - "editMessageTitle": "编辑消息内容", - "regenerate": "重新生成", - "regenerateTitle": "从此处重新生成回复", - "retryTitle": "重试上一条回答", - "editAnswer": "编辑", - "editAnswerTitle": "编辑回答内容", - "copyAnswer": "复制", - "copyAnswerTitle": "复制回答内容", - "feedback": "反馈", - "feedbackTitle": "反馈到 Grok2API", - "noContentToCopy": "暂无内容可复制", - "noChatToRetry": "没有可重试的对话", - "requestFailedCheck": "请求失败,请检查服务状态", - "requestFailedStatus": "请求失败: {status}", - "clickRetry": "点击重试", - "thinking": "思考中", - "thinkingSec": "思考 {sec} 秒", - "thought": "已思考", - "thinkLabel": "思考", - "empty": "(空)", - "toolWebSearch": "网页搜索", - "toolImageSearch": "图片搜索", - "toolAgentThink": "思考推理", - "toolDefault": "工具" - }, - "imagine": { - "pageTitle": "Grok2API - Imagine 瀑布流", - "title": "Imagine 瀑布流", - "subtitle": "通过 WebSocket 持续生成图片,实时展示 base64 瀑布流。", - "genSettings": "生成设置", - "prompt": "提示词", - "promptPlaceholder": "描述你想要的画面,例如:未来城市霓虹雨夜,广角摄影", - "autoFollow": "自动跟随", - "autoFollowDesc": "滚动到最新", - "autoSave": "自动保存", - "autoSaveDesc": "自动保存图片", - "aspectRatio": "宽高比", - "ratio2_3": "2:3 竖图", - "ratio1_1": "1:1 方图", - "ratio3_2": "3:2 横图", - "ratio16_9": "16:9 宽屏", - "ratio9_16": "9:16 竖屏", - "concurrent": "并发数量", - "tasks": "{n} 个任务", - "concurrentTask1": "1 个任务", - "concurrentTask2": "2 个任务", - "concurrentTask3": "3 个任务", - "autoFilter": "自动过滤", - "autoFilterDesc": "过滤不达标图片", - "nsfwLabel": "是否 NSFW", - "nsfwOn": "开启", - "nsfwOff": "关闭", - "saveLocation": "保存位置", - "browserDefault": "浏览器默认位置", - "reverseOrder": "反向新增", - "reverseOrderDesc": "最新显示在上方", - "connectionStatus": "连接状态", - "connectionMode": "连接模式", - "connectionModeAria": "连接模式", - "imageCount": "图片数量", - "activeTasks": "活跃任务", - "avgTime": "平均耗时", - "gallery": "瀑布流", - "waitingTitle": "等待连接中", - "waitingSubtitle": "启动任务后,生成的图片会自动汇聚到这里", - "start": "开始", - "batch": "批量", - "batchDownloadTitle": "批量下载", - "selectAll": "全选", - "deselectAll": "取消全选", - "download": "下载", - "startedTasks": "已启动 {count} 个并发任务", - "startedTasksSSE": "已启动 {count} 个并发任务 (SSE)", - "generatingSSE": "生成中 (SSE)", - "selectFolder": "已选择文件夹: {name}", - "selectFolderFailed": "选择文件夹失败", - "noImagesSelected": "请先选择要下载的图片", - "jszipFailed": "JSZip 库加载失败,请刷新页面重试", - "packing": "正在打包 {count} 张图片...", - "packingBtn": "打包中...", - "packingProgress": "打包中... ({done}/{total})", - "noImagesDownloaded": "没有成功获取任何图片", - "generatingZip": "生成压缩包...", - "packSuccess": "成功打包 {count} 张图片", - "packFailed": "打包失败,请重试" - }, - "video": { - "pageTitle": "Grok2API - Video 视频生成", - "title": "Video 视频生成", - "subtitle": "生成短视频,支持参考图与多种预设风格。", - "startGenerate": "开始生成", - "genSettings": "生成设置", - "prompt": "提示词", - "promptPlaceholder": "例如:街头霓虹雨夜,慢镜头,胶片质感", - "referenceImage": "参考图", - "referenceImagePlaceholder": "https://... 或 data:image/...", - "aspectRatio": "画面比例", - "ratio3_2": "3:2 横构图", - "ratio2_3": "2:3 竖构图", - "ratio16_9": "16:9 宽屏", - "ratio9_16": "9:16 竖屏", - "ratio1_1": "1:1 方形", - "duration": "视频时长", - "seconds": "{n} 秒", - "resolution": "分辨率", - "stylePreset": "风格预设", - "upload": "上传", - "clearImage": "清除", - "runStatus": "运行状态", - "elapsedTime": "耗时 {sec}s", - "elapsedTimeNone": "耗时 -", - "metaRatio": "比例", - "metaDuration": "时长", - "metaResolution": "分辨率", - "metaPreset": "预设", - "videoPreview": "视频预览", - "clearPreview": "清空", - "waitingGenerate": "等待生成视频", - "videoTitle": "视频 {n}", - "open": "打开", - "generatingPlaceholder": "生成中…", - "superResolution": "超分辨率", - "superResolutionInProgress": "超分辨率中", - "alreadyGenerating": "已在生成中", - "referenceConflict": "参考图只能选择其一:URL/Base64 或 本地上传", - "downloadFailed": "下载失败,请检查视频链接是否可访问", - "sec6": "6 秒", - "sec10": "10 秒", - "sec15": "15 秒" - }, - "voice": { - "pageTitle": "Grok2API - LiveKit 陪聊", - "title": "LiveKit 陪聊", - "subtitle": "LiveKit 语音会话,连接 Grok Voice。", - "startSession": "开始会话", - "stopSession": "停止会话", - "connectionSettings": "连接设置", - "voiceLabel": "声音 (Voice)", - "personalityLabel": "个性 (Personality)", - "speedLabel": "语速", - "settingsHint": "提示:Voice / Personality 会在建立连接前发送到服务端。", - "sessionLog": "会话日志", - "clearLog": "清空", - "copyLog": "复制", - "sessionStatus": "会话状态", - "audioOutput": "音频输出", - "audioHint": "订阅到的音轨会自动插入此区域。", - "livekitSDKError": "错误: LiveKit SDK 未能正确加载,请刷新页面重试", - "livekitLoadFailed": "LiveKit SDK 加载失败", - "secureContextBrowser": "请使用最新版浏览器并允许麦克风权限", - "secureContextHTTPS": "请使用 HTTPS 或在本机 localhost 访问", - "secureContextError": "当前环境不支持麦克风权限,{hint}", - "connectingStatus": "正在连接", - "fetchingToken": "正在获取 Token...", - "fetchTokenFailed": "获取 Token 失败: {status}", - "fetchTokenSuccess": "获取 Token 成功", - "participantConnected": "参与者已连接: {identity}", - "participantDisconnected": "参与者已断开: {identity}", - "trackSubscribed": "订阅音轨: {kind}", - "disconnected": "已断开连接", - "connectedToServer": "已连接到 LiveKit 服务器", - "inCall": "通话中", - "openingMic": "正在开启麦克风...", - "voiceEnabled": "语音已开启", - "voiceConnected": "语音连接成功", - "errorPrefix": "错误: {msg}", - "logCopied": "日志已复制", - "copyLogFailed": "复制失败,请手动选择" - } } diff --git a/app/core/log_viewer.py b/app/core/log_viewer.py index 0536cdf9..4a380d68 100644 --- a/app/core/log_viewer.py +++ b/app/core/log_viewer.py @@ -88,18 +88,23 @@ def read_log_entries( def delete_log_files(file_names: list[str]) -> dict[str, Any]: deleted: list[str] = [] missing: list[str] = [] + failed: list[str] = [] for file_name in file_names: path = _resolve_log_path(file_name) if not path.exists() or not path.is_file(): missing.append(path.name) continue - path.unlink() - deleted.append(path.name) + try: + path.unlink() + deleted.append(path.name) + except OSError: + failed.append(path.name) return { "deleted": deleted, "missing": missing, + "failed": failed, } From 8956e8e4e37a3cf02c53a8b24f8f1836c1e360a3 Mon Sep 17 00:00:00 2001 From: piexian <64474352+piexian@users.noreply.github.com> Date: Fri, 6 Mar 2026 22:21:53 +0800 Subject: [PATCH 3/5] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=9C=A8?= =?UTF-8?q?=E7=BA=BF=E8=B5=84=E4=BA=A7=E5=88=86=E9=A1=B5=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _public/static/admin/js/cache.js | 38 ++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/_public/static/admin/js/cache.js b/_public/static/admin/js/cache.js index f8246270..cccf1ff8 100644 --- a/_public/static/admin/js/cache.js +++ b/_public/static/admin/js/cache.js @@ -324,18 +324,13 @@ function updateAccountSelect(accounts) { }); } -function renderAccountTable(data) { - const tbody = ui.accountTableBody; - const empty = ui.accountEmpty; - if (!tbody || !empty) return; - +function buildOnlineRows(data) { const details = Array.isArray(data.online_details) ? data.online_details : []; const accounts = Array.isArray(data.online_accounts) ? data.online_accounts : []; const detailsMap = new Map(details.map(item => [item.token, item])); - let rows = []; if (accounts.length > 0) { - rows = accounts.map(item => { + return accounts.map(item => { const detail = detailsMap.get(item.token); const state = accountStates.get(item.token); let count = '-'; @@ -365,8 +360,10 @@ function renderAccountTable(data) { last_asset_clear_at }; }); - } else if (details.length > 0) { - rows = details.map(item => ({ + } + + if (details.length > 0) { + return details.map(item => ({ token: item.token, token_masked: item.token_masked, pool: (accountMap.get(item.token) || {}).pool || '-', @@ -376,7 +373,15 @@ function renderAccountTable(data) { })); } - onlineTableState.rows = rows; + return []; +} + +function renderOnlineTablePage() { + const tbody = ui.accountTableBody; + const empty = ui.accountEmpty; + if (!tbody || !empty) return; + + const rows = onlineTableState.rows; const totalPages = Math.max(1, Math.ceil(rows.length / onlineTableState.pageSize)); onlineTableState.page = Math.min(Math.max(1, onlineTableState.page), totalPages); @@ -463,6 +468,11 @@ function renderAccountTable(data) { updateBatchActionsVisibility(); } +function renderAccountTable(data) { + onlineTableState.rows = buildOnlineRows(data); + renderOnlineTablePage(); +} + function getVisibleOnlineRows() { const start = Math.max(0, (onlineTableState.page - 1) * onlineTableState.pageSize); return onlineTableState.rows.slice(start, start + onlineTableState.pageSize); @@ -710,14 +720,14 @@ function clearAllAccountSelection() { function onlineGoPrevPage() { if (onlineTableState.page <= 1) return; onlineTableState.page -= 1; - renderAccountTable({ online_accounts: Array.from(accountMap.values()), online_details: [], online: {} }); + renderOnlineTablePage(); } function onlineGoNextPage() { const totalPages = Math.max(1, Math.ceil(onlineTableState.rows.length / onlineTableState.pageSize)); if (onlineTableState.page >= totalPages) return; onlineTableState.page += 1; - renderAccountTable({ online_accounts: Array.from(accountMap.values()), online_details: [], online: {} }); + renderOnlineTablePage(); } function changeOnlinePageSize() { @@ -725,7 +735,7 @@ function changeOnlinePageSize() { if (!sizeSelect) return; onlineTableState.pageSize = Number(sizeSelect.value || 50); onlineTableState.page = 1; - renderAccountTable({ online_accounts: Array.from(accountMap.values()), online_details: [], online: {} }); + renderOnlineTablePage(); } window.selectVisibleAccounts = selectVisibleAccounts; @@ -1311,7 +1321,7 @@ async function startBatchLoad(tokens) { setOnlineStatus(t('cache.loadingStatus'), 'text-xs text-blue-600 mt-1'); updateLoadButton(); if (accountMap.size > 0) { - renderAccountTable({ online_accounts: Array.from(accountMap.values()), online_details: [], online: {} }); + renderOnlineTablePage(); } refreshBatchUI(); From cbf29a380762aa5cd846faa402a16cb4515aa2e6 Mon Sep 17 00:00:00 2001 From: piexian <64474352+piexian@users.noreply.github.com> Date: Fri, 6 Mar 2026 23:12:02 +0800 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E9=A1=B5=E5=A4=A7=E5=B0=8F=E7=BB=9F=E8=AE=A1=E4=B8=8E=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _public/static/admin/js/logs.js | 4 +-- _public/static/admin/pages/logs.html | 4 +-- _public/static/i18n/locales/en.json | 4 +-- _public/static/i18n/locales/zh.json | 4 +-- app/core/log_viewer.py | 38 ++++++++++++++++++++++------ 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/_public/static/admin/js/logs.js b/_public/static/admin/js/logs.js index ed1941dc..951d32e9 100644 --- a/_public/static/admin/js/logs.js +++ b/_public/static/admin/js/logs.js @@ -398,14 +398,14 @@ function updateClearLevelButton() { function updateStats(response) { const fileName = response?.file?.name || currentFileName || '-'; const updated = response?.file?.updated_at ? formatDate(response.file.updated_at) : '-'; - const size = response?.file?.size ? formatBytes(response.file.size) : '0 B'; + const totalSize = logFiles.reduce((sum, item) => sum + Number(item?.size || 0), 0); const matched = response?.stats?.matched || 0; const warning = response?.stats?.levels?.warning || 0; const error = response?.stats?.levels?.error || 0; logsById('stat-file').textContent = fileName; logsById('stat-updated').textContent = updated; - logsById('stat-size').textContent = size; + logsById('stat-size').textContent = formatBytes(totalSize); logsById('stat-matched').textContent = `${matched}`; logsById('stat-risk').textContent = `${warning + error}`; } diff --git a/_public/static/admin/pages/logs.html b/_public/static/admin/pages/logs.html index 1d8c5615..abf347a5 100644 --- a/_public/static/admin/pages/logs.html +++ b/_public/static/admin/pages/logs.html @@ -57,9 +57,9 @@

日志
warning + error

-
文件大小
+
总大小
0 B
-
磁盘占用
+
日志目录占用
diff --git a/_public/static/i18n/locales/en.json b/_public/static/i18n/locales/en.json index 3d66cd07..703b899e 100644 --- a/_public/static/i18n/locales/en.json +++ b/_public/static/i18n/locales/en.json @@ -482,8 +482,8 @@ "entriesHint": "Shown with current filters", "errorWarn": "Warnings / Errors", "errorWarnHint": "warning + error", - "size": "File Size", - "sizeHint": "Disk usage" + "size": "Total Size", + "sizeHint": "Log directory usage" }, "loadFilesFailed": "Failed to load log files", "loadFailed": "Failed to load logs", diff --git a/_public/static/i18n/locales/zh.json b/_public/static/i18n/locales/zh.json index 6c15f3c0..e68b4f8f 100644 --- a/_public/static/i18n/locales/zh.json +++ b/_public/static/i18n/locales/zh.json @@ -482,8 +482,8 @@ "entriesHint": "\u6309\u5f53\u524d\u7b5b\u9009\u5c55\u793a", "errorWarn": "\u544a\u8b66 / \u9519\u8bef", "errorWarnHint": "warning + error", - "size": "\u6587\u4ef6\u5927\u5c0f", - "sizeHint": "\u78c1\u76d8\u5360\u7528" + "size": "\u603b\u5927\u5c0f", + "sizeHint": "\u65e5\u5fd7\u76ee\u5f55\u5360\u7528" }, "loadFilesFailed": "\u52a0\u8f7d\u65e5\u5fd7\u6587\u4ef6\u5931\u8d25", "loadFailed": "\u52a0\u8f7d\u65e5\u5fd7\u5931\u8d25", diff --git a/app/core/log_viewer.py b/app/core/log_viewer.py index 4a380d68..dbc57d2c 100644 --- a/app/core/log_viewer.py +++ b/app/core/log_viewer.py @@ -20,13 +20,8 @@ class LogViewerError(ValueError): def list_log_files() -> list[dict[str, Any]]: - if not LOG_DIR.exists(): - return [] - files: list[dict[str, Any]] = [] - for path in sorted(LOG_DIR.iterdir(), key=_sort_key, reverse=True): - if not path.is_file() or path.suffix.lower() not in ALLOWED_SUFFIXES: - continue + for path in sorted(_allowed_log_paths().values(), key=_sort_key, reverse=True): stat = path.stat() files.append( { @@ -108,13 +103,40 @@ def delete_log_files(file_names: list[str]) -> dict[str, Any]: } -def _resolve_log_path(file_name: str) -> Path: +def _allowed_log_paths() -> dict[str, Path]: + if not LOG_DIR.exists(): + return {} + + base_dir = LOG_DIR.resolve() + allowed: dict[str, Path] = {} + for path in LOG_DIR.iterdir(): + if not path.is_file() or path.suffix.lower() not in ALLOWED_SUFFIXES: + continue + try: + resolved = path.resolve() + except OSError: + continue + if resolved.parent != base_dir: + continue + allowed[path.name] = resolved + return allowed + + +def _validate_log_file_name(file_name: str) -> str: candidate = Path(file_name) if candidate.name != file_name: raise LogViewerError("Invalid log file name") if candidate.suffix.lower() not in ALLOWED_SUFFIXES: raise LogViewerError("Unsupported log file") - return LOG_DIR / candidate.name + return candidate.name + + +def _resolve_log_path(file_name: str) -> Path: + safe_name = _validate_log_file_name(file_name) + path = _allowed_log_paths().get(safe_name) + if path is None: + raise FileNotFoundError(safe_name) + return path def _sort_key(path: Path) -> tuple[float, str]: From 3f35d18a8ebe2020574084e8c5ef8a6618ad0db6 Mon Sep 17 00:00:00 2001 From: piexian <64474352+piexian@users.noreply.github.com> Date: Sat, 7 Mar 2026 00:28:35 +0800 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E9=A1=B5=E6=8C=89=E9=92=AE=E5=9B=BD=E9=99=85=E5=8C=96=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _public/static/admin/js/logs.js | 10 +- _public/static/i18n/locales/en.json | 1681 ++++++++++++++------------- _public/static/i18n/locales/zh.json | 1681 ++++++++++++++------------- 3 files changed, 1691 insertions(+), 1681 deletions(-) diff --git a/_public/static/admin/js/logs.js b/_public/static/admin/js/logs.js index 951d32e9..c0d8f07a 100644 --- a/_public/static/admin/js/logs.js +++ b/_public/static/admin/js/logs.js @@ -21,7 +21,15 @@ document.addEventListener('DOMContentLoaded', async () => { bindEvents(); syncFilePanelMode(); - updateAutoRefreshButton(); + if (window.I18n?.onReady) { + I18n.onReady(() => { + updateAutoRefreshButton(); + updateFileCountSummary(); + updateStats(currentResponse); + }); + } else { + updateAutoRefreshButton(); + } await loadFiles(); syncAutoRefresh(); }); diff --git a/_public/static/i18n/locales/en.json b/_public/static/i18n/locales/en.json index 703b899e..cf5249ad 100644 --- a/_public/static/i18n/locales/en.json +++ b/_public/static/i18n/locales/en.json @@ -1,850 +1,851 @@ { - "common": { - "save": "Save", - "cancel": "Cancel", - "confirm": "Confirm", - "delete": "Delete", - "loading": "Loading...", - "invalidKey": "Invalid key", - "connectionFailed": "Connection failed", - "requestFailed": "Request failed", - "operationSuccess": "Operation successful", - "gotIt": "Got it", - "notice": "Notice", - "rateLimitNotice": "The official GROK website update did not expose the rate-limits API, making it impossible to accurately calculate remaining Token usage. Please wait for the official API. Currently the count resets to 8 after auto-refresh.", - "pleaseConfirm": "Please confirm", - "ok": "OK", - "close": "Close", - "error": "Error", - "unknownError": "Unknown error", - "connectionInterrupted": "Connection interrupted", - "taskInProgress": "A task is already in progress", - "taskNoPause": "Current task does not support pausing", - "batchNoPause": "Current batch task does not support pausing", - "waitTaskFinish": "Please wait for the current task to finish", - "pause": "Pause", - "stop": "Stop", - "selected": "Selected", - "items": "items", - "export": "Export", - "refresh": "Refresh", - "load": "Load", - "files": "files", - "view": "View", - "retry": "Retry", - "copy": "Copy", - "edit": "Edit", - "loadFailed": "Load failed", - "loadError": "Load failed: {msg}", - "saveFailed": "Save failed", - "saveError": "Save error: {msg}", - "deleteFailed": "Delete failed", - "deleteSuccess": "Deleted successfully", - "notConnected": "Not connected", - "connecting": "Connecting", - "connected": "Connected", - "connectionError": "Connection error", - "generating": "Generating", - "done": "Done", - "failed": "Failed", - "stopped": "Stopped", - "terminated": "Terminated", - "sending": "Sending", - "fileReadFailed": "File read failed", - "noFileSelected": "No file selected", - "configurePublicKey": "Please configure Public Key first", - "enterPrompt": "Please enter a prompt", - "enterContent": "Please enter content", - "alreadyRunning": "Already running", - "createTaskFailed": "Failed to create task", - "copyFailed": "Copy failed", - "copied": "Copied", - "generationFailed": "Generation failed", - "partialError": "Partial error", - "loadingInProgress": "Loading in progress, please wait", - "cleaningInProgress": "Cleaning in progress, please wait", - "noTokenSelected": "No Token selected", - "clearCache": "Clear cache", - "failureDetails": "Failure Details", - "retryFailed": "Retry failed items" + "common": { + "save": "Save", + "cancel": "Cancel", + "confirm": "Confirm", + "delete": "Delete", + "loading": "Loading...", + "invalidKey": "Invalid key", + "connectionFailed": "Connection failed", + "requestFailed": "Request failed", + "operationSuccess": "Operation successful", + "gotIt": "Got it", + "notice": "Notice", + "rateLimitNotice": "The official GROK website update did not expose the rate-limits API, making it impossible to accurately calculate remaining Token usage. Please wait for the official API. Currently the count resets to 8 after auto-refresh.", + "pleaseConfirm": "Please confirm", + "ok": "OK", + "close": "Close", + "error": "Error", + "unknownError": "Unknown error", + "connectionInterrupted": "Connection interrupted", + "taskInProgress": "A task is already in progress", + "taskNoPause": "Current task does not support pausing", + "batchNoPause": "Current batch task does not support pausing", + "waitTaskFinish": "Please wait for the current task to finish", + "pause": "Pause", + "stop": "Stop", + "selected": "Selected", + "items": "items", + "export": "Export", + "refresh": "Refresh", + "load": "Load", + "files": "files", + "view": "View", + "retry": "Retry", + "copy": "Copy", + "edit": "Edit", + "loadFailed": "Load failed", + "loadError": "Load failed: {msg}", + "saveFailed": "Save failed", + "saveError": "Save error: {msg}", + "deleteFailed": "Delete failed", + "deleteSuccess": "Deleted successfully", + "notConnected": "Not connected", + "connecting": "Connecting", + "connected": "Connected", + "connectionError": "Connection error", + "generating": "Generating", + "done": "Done", + "failed": "Failed", + "stopped": "Stopped", + "terminated": "Terminated", + "sending": "Sending", + "fileReadFailed": "File read failed", + "noFileSelected": "No file selected", + "configurePublicKey": "Please configure Public Key first", + "enterPrompt": "Please enter a prompt", + "enterContent": "Please enter content", + "alreadyRunning": "Already running", + "createTaskFailed": "Failed to create task", + "copyFailed": "Copy failed", + "copied": "Copied", + "generationFailed": "Generation failed", + "partialError": "Partial error", + "loadingInProgress": "Loading in progress, please wait", + "cleaningInProgress": "Cleaning in progress, please wait", + "noTokenSelected": "No Token selected", + "clearCache": "Clear cache", + "failureDetails": "Failure Details", + "retryFailed": "Retry failed items", + "raw": "Raw" + }, + "nav": { + "tokenManage": "Token Mgmt", + "configManage": "Config Mgmt", + "cacheManage": "Cache Mgmt", + "logsView": "Logs", + "chat": "Chat", + "imagine": "Imagine Gallery", + "video": "Video Generation", + "voice": "LiveKit Voice", + "feedback": "Feedback", + "logout": "Logout", + "adminPanel": "Admin Panel", + "storageMode": "Storage Mode" + }, + "login": { + "adminPageTitle": "Grok2API - Login", + "adminTitle": "Admin Panel", + "adminSubtitle": "Enter admin password to continue", + "adminPlaceholder": "Admin Password (app_key)", + "continue": "Continue", + "publicPageTitle": "Grok2API - Public", + "publicTitle": "Public Access", + "publicSubtitle": "Enter Public Key (leave empty if not configured)", + "publicPlaceholder": "Public Key", + "enter": "Enter", + "goToAdmin": "Go to Admin Panel" + }, + "config": { + "pageTitle": "Grok2API - Config", + "title": "Configuration", + "subtitle": "Manage API keys and system parameters.", + "saving": "Saving...", + "saved": "Saved", + "configSaved": "Configuration saved", + "invalidJson": "Invalid JSON: {field}", + "appKeyRequired": "app_key cannot be empty (admin password)", + "flaresolverrRequired": "FlareSolverr URL is required when auto-refresh is enabled", + "generate": "Generate", + "publicAccess": "Public Access", + "noDesc": "No description available. Please refer to the documentation.", + "sectionFallback": "{section} Settings", + "sections": { + "app": "App Settings", + "proxy": "Proxy Config", + "retry": "Retry Strategy", + "chat": "Chat Config", + "video": "Video Config", + "image": "Image Config", + "imagine_fast": "Imagine Fast Config", + "asset": "Asset Config", + "voice": "Voice Config", + "token": "Token Pool Mgmt", + "cache": "Cache Mgmt", + "nsfw": "NSFW Config", + "usage": "Usage Config" }, - "nav": { - "tokenManage": "Token Mgmt", - "configManage": "Config Mgmt", - "cacheManage": "Cache Mgmt", - "logsView": "Logs", - "chat": "Chat", - "imagine": "Imagine Gallery", - "video": "Video Generation", - "voice": "LiveKit Voice", - "feedback": "Feedback", - "logout": "Logout", - "adminPanel": "Admin Panel", - "storageMode": "Storage Mode" + "sectionDescs": { + "proxy": "Incorrect configuration will cause 403 errors. The IP of the first request to Grok must match the IP used to obtain the CF Clearance. Subsequent IP changes will not cause 403." }, - "login": { - "adminPageTitle": "Grok2API - Login", - "adminTitle": "Admin Panel", - "adminSubtitle": "Enter admin password to continue", - "adminPlaceholder": "Admin Password (app_key)", - "continue": "Continue", - "publicPageTitle": "Grok2API - Public", - "publicTitle": "Public Access", - "publicSubtitle": "Enter Public Key (leave empty if not configured)", - "publicPlaceholder": "Public Key", - "enter": "Enter", - "goToAdmin": "Go to Admin Panel" - }, - "config": { - "pageTitle": "Grok2API - Config", - "title": "Configuration", - "subtitle": "Manage API keys and system parameters.", - "saving": "Saving...", - "saved": "Saved", - "configSaved": "Configuration saved", - "invalidJson": "Invalid JSON: {field}", - "appKeyRequired": "app_key cannot be empty (admin password)", - "flaresolverrRequired": "FlareSolverr URL is required when auto-refresh is enabled", - "generate": "Generate", - "publicAccess": "Public Access", - "noDesc": "No description available. Please refer to the documentation.", - "sectionFallback": "{section} Settings", - "sections": { - "app": "App Settings", - "proxy": "Proxy Config", - "retry": "Retry Strategy", - "chat": "Chat Config", - "video": "Video Config", - "image": "Image Config", - "imagine_fast": "Imagine Fast Config", - "asset": "Asset Config", - "voice": "Voice Config", - "token": "Token Pool Mgmt", - "cache": "Cache Mgmt", - "nsfw": "NSFW Config", - "usage": "Usage Config" - }, - "sectionDescs": { - "proxy": "Incorrect configuration will cause 403 errors. The IP of the first request to Grok must match the IP used to obtain the CF Clearance. Subsequent IP changes will not cause 403." - }, - "fields": { - "app": { - "api_key": { - "title": "API Key", - "desc": "Token for calling Grok2API services (optional, supports multiple, comma-separated or array)." - }, - "app_key": { - "title": "Admin Password", - "desc": "Password for logging into the Grok2API admin panel (required)." - }, - "function_enabled": { - "title": "Enable Function Access", - "desc": "Whether to enable the function access entry (disabled means function pages are inaccessible)." - }, - "function_key": { - "title": "Function Password", - "desc": "Access password for function pages (optional)." - }, - "app_url": { - "title": "App URL", - "desc": "External access URL for the Grok2API service, used for file link access." - }, - "image_format": { - "title": "Image Format", - "desc": "Default image format (url or base64)." - }, - "video_format": { - "title": "Video Format", - "desc": "Default video format (html or url, url is the processed link)." - }, - "temporary": { - "title": "Temporary Chat", - "desc": "Whether to enable temporary chat mode by default." - }, - "disable_memory": { - "title": "Disable Memory", - "desc": "Whether to disable Grok memory feature by default." - }, - "stream": { - "title": "Streaming", - "desc": "Whether to enable streaming output by default." - }, - "thinking": { - "title": "Chain of Thought", - "desc": "Whether to enable chain of thought output by default." - }, - "dynamic_statsig": { - "title": "Dynamic Fingerprint", - "desc": "Whether to enable dynamic Statsig fingerprint generation by default." - }, - "custom_instruction": { - "title": "Custom Instruction", - "desc": "Multi-line text passed through as Grok request parameter customPersonality." - }, - "filter_tags": { - "title": "Filter Tags", - "desc": "Configure automatic filtering of special tags in Grok responses." - } - }, - "proxy": { - "base_proxy_url": { - "title": "Base Proxy URL", - "desc": "Base service address for proxying requests to Grok website." - }, - "asset_proxy_url": { - "title": "Asset Proxy URL", - "desc": "Address for proxying static resource (image/video) requests to Grok website." - }, - "skip_proxy_ssl_verify": { - "title": "Skip Proxy SSL Verify", - "desc": "Enable when your proxy uses a self-signed certificate (only bypasses proxy certificate validation)." - }, - "enabled": { - "title": "Enable CF Auto-Refresh", - "desc": "When enabled, automatically obtains cf_clearance through FlareSolverr." - }, - "flaresolverr_url": { - "title": "FlareSolverr URL", - "desc": "HTTP address of FlareSolverr service (e.g. http://flaresolverr:8191)." - }, - "refresh_interval": { - "title": "Refresh Interval (sec)", - "desc": "Time interval for auto-refreshing cf_clearance, recommended minimum 300 seconds." - }, - "timeout": { - "title": "Challenge Timeout (sec)", - "desc": "Maximum time to wait for FlareSolverr to solve CF challenge." - }, - "cf_clearance": { - "title": "CF Clearance", - "desc": "Cloudflare Clearance Cookie for bypassing anti-bot verification. Automatically managed when auto-refresh is enabled." - }, - "browser": { - "title": "Browser Fingerprint", - "desc": "curl_cffi browser fingerprint identifier (e.g. chrome136). Automatically managed when auto-refresh is enabled." - }, - "user_agent": { - "title": "User-Agent", - "desc": "HTTP request User-Agent string. Automatically managed when auto-refresh is enabled." - } - }, - "retry": { - "max_retry": { - "title": "Max Retries", - "desc": "Maximum number of retries when Grok service request fails." - }, - "retry_status_codes": { - "title": "Retry Status Codes", - "desc": "HTTP status codes that trigger a retry." - }, - "reset_session_status_codes": { - "title": "Session Reset Status Codes", - "desc": "HTTP status codes that trigger session rebuild (used for proxy rotation)." - }, - "retry_backoff_base": { - "title": "Backoff Base", - "desc": "Base delay (seconds) for retry backoff." - }, - "retry_backoff_factor": { - "title": "Backoff Factor", - "desc": "Exponential multiplier for retry backoff." - }, - "retry_backoff_max": { - "title": "Backoff Max", - "desc": "Maximum delay (seconds) for a single retry wait." - }, - "retry_budget": { - "title": "Backoff Budget", - "desc": "Maximum total retry time (seconds) for a single request." - } - }, - "chat": { - "concurrent": { - "title": "Concurrency Limit", - "desc": "Reverse API concurrency limit." - }, - "timeout": { - "title": "Request Timeout", - "desc": "Reverse API timeout (seconds)." - }, - "stream_timeout": { - "title": "Stream Idle Timeout", - "desc": "Streaming idle timeout (seconds)." - } - }, - "video": { - "concurrent": { - "title": "Concurrency Limit", - "desc": "Reverse API concurrency limit." - }, - "timeout": { - "title": "Request Timeout", - "desc": "Reverse API timeout (seconds)." - }, - "stream_timeout": { - "title": "Stream Idle Timeout", - "desc": "Streaming idle timeout (seconds)." - } - }, - "image": { - "timeout": { - "title": "Request Timeout", - "desc": "WebSocket request timeout (seconds)." - }, - "stream_timeout": { - "title": "Stream Idle Timeout", - "desc": "WebSocket streaming idle timeout (seconds)." - }, - "final_timeout": { - "title": "Final Image Timeout", - "desc": "Timeout (seconds) to wait for final image after receiving medium image." - }, - "blocked_grace_seconds": { - "title": "Review Grace Seconds", - "desc": "Grace seconds for suspected review/censorship after receiving medium image (default 10, configurable)." - }, - "nsfw": { - "title": "NSFW Mode", - "desc": "Whether to enable NSFW for WebSocket requests." - }, - "medium_min_bytes": { - "title": "Medium Min Bytes", - "desc": "Minimum byte size for medium quality image detection." - }, - "final_min_bytes": { - "title": "Final Min Bytes", - "desc": "Minimum byte size for final image detection (typically JPG > 100KB)." - }, - "blocked_parallel_enabled": { - "title": "Enable Parallel Compensation", - "desc": "Whether to enable parallel compensation when suspected review/blocking occurs." - }, - "blocked_parallel_attempts": { - "title": "Block Compensation Concurrency", - "desc": "Number of parallel compensation attempts when suspected review/blocking prevents final image." - } - }, - "imagine_fast": { - "n": { - "title": "Generation Count", - "desc": "Server-side unified generation count for grok-imagine-1.0-fast only (1-10)." - }, - "size": { - "title": "Image Size", - "desc": "Server-side unified size for grok-imagine-1.0-fast only." - }, - "response_format": { - "title": "Response Format", - "desc": "Server-side unified response format for grok-imagine-1.0-fast only." - } - }, - "asset": { - "upload_concurrent": { - "title": "Upload Concurrency", - "desc": "Maximum concurrent uploads. Recommended: 30." - }, - "upload_timeout": { - "title": "Upload Timeout", - "desc": "Upload timeout (seconds). Recommended: 60." - }, - "download_concurrent": { - "title": "Download Concurrency", - "desc": "Maximum concurrent downloads. Recommended: 30." - }, - "download_timeout": { - "title": "Download Timeout", - "desc": "Download timeout (seconds). Recommended: 60." - }, - "list_concurrent": { - "title": "Query Concurrency", - "desc": "Maximum concurrent asset queries. Recommended: 10." - }, - "list_timeout": { - "title": "Query Timeout", - "desc": "Asset query timeout (seconds). Recommended: 60." - }, - "list_batch_size": { - "title": "Query Batch Size", - "desc": "Number of tokens per query batch. Recommended: 10." - }, - "delete_concurrent": { - "title": "Delete Concurrency", - "desc": "Maximum concurrent asset deletions. Recommended: 10." - }, - "delete_timeout": { - "title": "Delete Timeout", - "desc": "Asset deletion timeout (seconds). Recommended: 60." - }, - "delete_batch_size": { - "title": "Delete Batch Size", - "desc": "Number of tokens per delete batch. Recommended: 10." - } - }, - "voice": { - "timeout": { - "title": "Request Timeout", - "desc": "Voice request timeout (seconds)." - } - }, - "token": { - "auto_refresh": { - "title": "Auto Refresh", - "desc": "Whether to enable automatic Token refresh." - }, - "refresh_interval_hours": { - "title": "Refresh Interval", - "desc": "Time interval (hours) for regular Token refresh." - }, - "super_refresh_interval_hours": { - "title": "Super Refresh Interval", - "desc": "Time interval (hours) for Super Token refresh." - }, - "fail_threshold": { - "title": "Failure Threshold", - "desc": "Number of consecutive failures before a Token is marked unavailable." - }, - "save_delay_ms": { - "title": "Save Delay", - "desc": "Delay (ms) for batching Token change writes." - }, - "usage_flush_interval_sec": { - "title": "Usage Flush Interval", - "desc": "Minimum interval (seconds) for writing usage data to database." - }, - "reload_interval_sec": { - "title": "Sync Interval", - "desc": "Token state refresh interval (seconds) in multi-worker scenarios." - } - }, - "cache": { - "enable_auto_clean": { - "title": "Auto Clean", - "desc": "Whether to enable automatic cache cleanup based on size limit." - }, - "limit_mb": { - "title": "Clean Threshold", - "desc": "Cache size threshold (MB) that triggers cleanup." - } - }, - "nsfw": { - "concurrent": { - "title": "Concurrency Limit", - "desc": "Maximum concurrent requests for batch NSFW enable. Recommended: 10." - }, - "batch_size": { - "title": "Batch Size", - "desc": "Batch processing count for NSFW enable. Recommended: 50." - }, - "timeout": { - "title": "Request Timeout", - "desc": "Timeout for NSFW-related requests (seconds). Recommended: 60." - } - }, - "usage": { - "concurrent": { - "title": "Concurrency Limit", - "desc": "Maximum concurrent requests for batch usage refresh. Recommended: 10." - }, - "batch_size": { - "title": "Batch Size", - "desc": "Batch processing count for usage refresh. Recommended: 50." - }, - "timeout": { - "title": "Request Timeout", - "desc": "Usage query timeout (seconds). Recommended: 60." - } - } + "fields": { + "app": { + "api_key": { + "title": "API Key", + "desc": "Token for calling Grok2API services (optional, supports multiple, comma-separated or array)." + }, + "app_key": { + "title": "Admin Password", + "desc": "Password for logging into the Grok2API admin panel (required)." + }, + "function_enabled": { + "title": "Enable Function Access", + "desc": "Whether to enable the function access entry (disabled means function pages are inaccessible)." + }, + "function_key": { + "title": "Function Password", + "desc": "Access password for function pages (optional)." + }, + "app_url": { + "title": "App URL", + "desc": "External access URL for the Grok2API service, used for file link access." + }, + "image_format": { + "title": "Image Format", + "desc": "Default image format (url or base64)." + }, + "video_format": { + "title": "Video Format", + "desc": "Default video format (html or url, url is the processed link)." + }, + "temporary": { + "title": "Temporary Chat", + "desc": "Whether to enable temporary chat mode by default." + }, + "disable_memory": { + "title": "Disable Memory", + "desc": "Whether to disable Grok memory feature by default." + }, + "stream": { + "title": "Streaming", + "desc": "Whether to enable streaming output by default." + }, + "thinking": { + "title": "Chain of Thought", + "desc": "Whether to enable chain of thought output by default." + }, + "dynamic_statsig": { + "title": "Dynamic Fingerprint", + "desc": "Whether to enable dynamic Statsig fingerprint generation by default." + }, + "custom_instruction": { + "title": "Custom Instruction", + "desc": "Multi-line text passed through as Grok request parameter customPersonality." + }, + "filter_tags": { + "title": "Filter Tags", + "desc": "Configure automatic filtering of special tags in Grok responses." } + }, + "proxy": { + "base_proxy_url": { + "title": "Base Proxy URL", + "desc": "Base service address for proxying requests to Grok website." + }, + "asset_proxy_url": { + "title": "Asset Proxy URL", + "desc": "Address for proxying static resource (image/video) requests to Grok website." + }, + "skip_proxy_ssl_verify": { + "title": "Skip Proxy SSL Verify", + "desc": "Enable when your proxy uses a self-signed certificate (only bypasses proxy certificate validation)." + }, + "enabled": { + "title": "Enable CF Auto-Refresh", + "desc": "When enabled, automatically obtains cf_clearance through FlareSolverr." + }, + "flaresolverr_url": { + "title": "FlareSolverr URL", + "desc": "HTTP address of FlareSolverr service (e.g. http://flaresolverr:8191)." + }, + "refresh_interval": { + "title": "Refresh Interval (sec)", + "desc": "Time interval for auto-refreshing cf_clearance, recommended minimum 300 seconds." + }, + "timeout": { + "title": "Challenge Timeout (sec)", + "desc": "Maximum time to wait for FlareSolverr to solve CF challenge." + }, + "cf_clearance": { + "title": "CF Clearance", + "desc": "Cloudflare Clearance Cookie for bypassing anti-bot verification. Automatically managed when auto-refresh is enabled." + }, + "browser": { + "title": "Browser Fingerprint", + "desc": "curl_cffi browser fingerprint identifier (e.g. chrome136). Automatically managed when auto-refresh is enabled." + }, + "user_agent": { + "title": "User-Agent", + "desc": "HTTP request User-Agent string. Automatically managed when auto-refresh is enabled." + } + }, + "retry": { + "max_retry": { + "title": "Max Retries", + "desc": "Maximum number of retries when Grok service request fails." + }, + "retry_status_codes": { + "title": "Retry Status Codes", + "desc": "HTTP status codes that trigger a retry." + }, + "reset_session_status_codes": { + "title": "Session Reset Status Codes", + "desc": "HTTP status codes that trigger session rebuild (used for proxy rotation)." + }, + "retry_backoff_base": { + "title": "Backoff Base", + "desc": "Base delay (seconds) for retry backoff." + }, + "retry_backoff_factor": { + "title": "Backoff Factor", + "desc": "Exponential multiplier for retry backoff." + }, + "retry_backoff_max": { + "title": "Backoff Max", + "desc": "Maximum delay (seconds) for a single retry wait." + }, + "retry_budget": { + "title": "Backoff Budget", + "desc": "Maximum total retry time (seconds) for a single request." + } + }, + "chat": { + "concurrent": { + "title": "Concurrency Limit", + "desc": "Reverse API concurrency limit." + }, + "timeout": { + "title": "Request Timeout", + "desc": "Reverse API timeout (seconds)." + }, + "stream_timeout": { + "title": "Stream Idle Timeout", + "desc": "Streaming idle timeout (seconds)." + } + }, + "video": { + "concurrent": { + "title": "Concurrency Limit", + "desc": "Reverse API concurrency limit." + }, + "timeout": { + "title": "Request Timeout", + "desc": "Reverse API timeout (seconds)." + }, + "stream_timeout": { + "title": "Stream Idle Timeout", + "desc": "Streaming idle timeout (seconds)." + } + }, + "image": { + "timeout": { + "title": "Request Timeout", + "desc": "WebSocket request timeout (seconds)." + }, + "stream_timeout": { + "title": "Stream Idle Timeout", + "desc": "WebSocket streaming idle timeout (seconds)." + }, + "final_timeout": { + "title": "Final Image Timeout", + "desc": "Timeout (seconds) to wait for final image after receiving medium image." + }, + "blocked_grace_seconds": { + "title": "Review Grace Seconds", + "desc": "Grace seconds for suspected review/censorship after receiving medium image (default 10, configurable)." + }, + "nsfw": { + "title": "NSFW Mode", + "desc": "Whether to enable NSFW for WebSocket requests." + }, + "medium_min_bytes": { + "title": "Medium Min Bytes", + "desc": "Minimum byte size for medium quality image detection." + }, + "final_min_bytes": { + "title": "Final Min Bytes", + "desc": "Minimum byte size for final image detection (typically JPG > 100KB)." + }, + "blocked_parallel_enabled": { + "title": "Enable Parallel Compensation", + "desc": "Whether to enable parallel compensation when suspected review/blocking occurs." + }, + "blocked_parallel_attempts": { + "title": "Block Compensation Concurrency", + "desc": "Number of parallel compensation attempts when suspected review/blocking prevents final image." + } + }, + "imagine_fast": { + "n": { + "title": "Generation Count", + "desc": "Server-side unified generation count for grok-imagine-1.0-fast only (1-10)." + }, + "size": { + "title": "Image Size", + "desc": "Server-side unified size for grok-imagine-1.0-fast only." + }, + "response_format": { + "title": "Response Format", + "desc": "Server-side unified response format for grok-imagine-1.0-fast only." + } + }, + "asset": { + "upload_concurrent": { + "title": "Upload Concurrency", + "desc": "Maximum concurrent uploads. Recommended: 30." + }, + "upload_timeout": { + "title": "Upload Timeout", + "desc": "Upload timeout (seconds). Recommended: 60." + }, + "download_concurrent": { + "title": "Download Concurrency", + "desc": "Maximum concurrent downloads. Recommended: 30." + }, + "download_timeout": { + "title": "Download Timeout", + "desc": "Download timeout (seconds). Recommended: 60." + }, + "list_concurrent": { + "title": "Query Concurrency", + "desc": "Maximum concurrent asset queries. Recommended: 10." + }, + "list_timeout": { + "title": "Query Timeout", + "desc": "Asset query timeout (seconds). Recommended: 60." + }, + "list_batch_size": { + "title": "Query Batch Size", + "desc": "Number of tokens per query batch. Recommended: 10." + }, + "delete_concurrent": { + "title": "Delete Concurrency", + "desc": "Maximum concurrent asset deletions. Recommended: 10." + }, + "delete_timeout": { + "title": "Delete Timeout", + "desc": "Asset deletion timeout (seconds). Recommended: 60." + }, + "delete_batch_size": { + "title": "Delete Batch Size", + "desc": "Number of tokens per delete batch. Recommended: 10." + } + }, + "voice": { + "timeout": { + "title": "Request Timeout", + "desc": "Voice request timeout (seconds)." + } + }, + "token": { + "auto_refresh": { + "title": "Auto Refresh", + "desc": "Whether to enable automatic Token refresh." + }, + "refresh_interval_hours": { + "title": "Refresh Interval", + "desc": "Time interval (hours) for regular Token refresh." + }, + "super_refresh_interval_hours": { + "title": "Super Refresh Interval", + "desc": "Time interval (hours) for Super Token refresh." + }, + "fail_threshold": { + "title": "Failure Threshold", + "desc": "Number of consecutive failures before a Token is marked unavailable." + }, + "save_delay_ms": { + "title": "Save Delay", + "desc": "Delay (ms) for batching Token change writes." + }, + "usage_flush_interval_sec": { + "title": "Usage Flush Interval", + "desc": "Minimum interval (seconds) for writing usage data to database." + }, + "reload_interval_sec": { + "title": "Sync Interval", + "desc": "Token state refresh interval (seconds) in multi-worker scenarios." + } + }, + "cache": { + "enable_auto_clean": { + "title": "Auto Clean", + "desc": "Whether to enable automatic cache cleanup based on size limit." + }, + "limit_mb": { + "title": "Clean Threshold", + "desc": "Cache size threshold (MB) that triggers cleanup." + } + }, + "nsfw": { + "concurrent": { + "title": "Concurrency Limit", + "desc": "Maximum concurrent requests for batch NSFW enable. Recommended: 10." + }, + "batch_size": { + "title": "Batch Size", + "desc": "Batch processing count for NSFW enable. Recommended: 50." + }, + "timeout": { + "title": "Request Timeout", + "desc": "Timeout for NSFW-related requests (seconds). Recommended: 60." + } + }, + "usage": { + "concurrent": { + "title": "Concurrency Limit", + "desc": "Maximum concurrent requests for batch usage refresh. Recommended: 10." + }, + "batch_size": { + "title": "Batch Size", + "desc": "Batch processing count for usage refresh. Recommended: 50." + }, + "timeout": { + "title": "Request Timeout", + "desc": "Usage query timeout (seconds). Recommended: 60." + } + } + } + }, + "logs": { + "pageTitle": "Grok2API - Logs", + "title": "Log Viewer", + "subtitle": "Filter structured logs by file, level, and keyword with a cleaner reading experience.", + "filesTitle": "Log Files", + "rawTitle": "Raw Log", + "empty": "No matching log records", + "filters": { + "file": "Log File", + "level": "Level", + "limit": "Rows", + "keyword": "Keyword", + "keywordPlaceholder": "traceID / path / error keyword", + "apply": "Apply", + "reset": "Reset", + "excludeAdminRoutes": "Hide `/v1/admin/*` only on this page" }, - "logs": { - "pageTitle": "Grok2API - Logs", - "title": "Log Viewer", - "subtitle": "Filter structured logs by file, level, and keyword with a cleaner reading experience.", - "filesTitle": "Log Files", - "rawTitle": "Raw Log", - "empty": "No matching log records", - "filters": { - "file": "Log File", - "level": "Level", - "limit": "Rows", - "keyword": "Keyword", - "keywordPlaceholder": "traceID / path / error keyword", - "apply": "Apply", - "reset": "Reset", - "excludeAdminRoutes": "Hide `/v1/admin/*` only on this page" - }, - "levels": { - "all": "All Levels" - }, - "stats": { - "file": "Current File", - "entries": "Loaded Rows", - "entriesHint": "Shown with current filters", - "errorWarn": "Warnings / Errors", - "errorWarnHint": "warning + error", - "size": "Total Size", - "sizeHint": "Log directory usage" - }, - "loadFilesFailed": "Failed to load log files", - "loadFailed": "Failed to load logs", - "noFiles": "No log files", - "fileCount": "{count} log files", - "deleteNone": "Select log files to delete first", - "deleteConfirm": "Delete {count} log files?", - "deleteFailed": "Failed to delete logs", - "deleteResult": "Deleted {deleted} log files, {failed} failed", - "toggle": { - "expand": "Expand", - "collapse": "Collapse" - }, - "autoRefresh": { - "buttonAria": "Auto refresh", - "off": "Auto refresh: Off", - "offShort": "Off", - "interval": "Auto refresh: {seconds}s", - "intervalLabel": "{seconds}s", - "changed": "Auto refresh: {label}", - "disabled": "Auto refresh disabled" - }, - "deleteSelected": "Delete Selected" - }, - "token": { - "pageTitle": "Grok2API - Token Mgmt", - "title": "Token List", - "subtitle": "Manage the Grok2API Token service pool.", - "import": "Import", - "add": "Add", - "statTotal": "Total Tokens", - "statActive": "Active", - "statCooling": "Rate Limited", - "statInvalid": "Invalid", - "statChatQuota": "Chat Remaining", - "statImageQuota": "Image Remaining", - "statVideoQuota": "Video Remaining", - "statVideoUnavailable": "N/A", - "statTotalCalls": "Total Calls", - "tabAll": "All", - "tabActive": "Active", - "tabCooling": "Rate Limited", - "tabExpired": "Expired", - "tabNsfw": "NSFW On", - "tabNoNsfw": "NSFW Off", - "statusFilterAria": "Token status filter", - "tableToken": "Token", - "tableType": "Type", - "tableStatus": "Status", - "tableQuota": "Quota", - "tableNote": "Note", - "tableActions": "Actions", - "refreshStatus": "Refresh status", - "emptyState": "No Tokens yet. Click Import or Add above.", - "emptyFilterState": "No results for current filter. Try a different filter.", - "selectPage": "Select page", - "selectAllFiltered": "Select all", - "clearSelection": "Clear selection", - "prevPage": "Prev", - "nextPage": "Next", - "perPage": "{size} / page", - "pagination": "Page {current} / {total} \u00b7 {count} items", - "batchExport": "Export", - "batchRefresh": "Refresh", - "batchDisable": "Disable", - "batchEnable": "Enable", - "batchDelete": "Delete", - "importTitle": "Batch Import Tokens", - "importTargetPool": "Target Pool", - "importTokenList": "Token List (one per line)", - "importPlaceholder": "Paste Tokens, one per line...", - "startImport": "Start Import", - "editTitle": "Edit Token", - "addTitle": "Add Token", - "editType": "Type", - "editQuota": "Quota", - "editNote": "Note", - "editNotePlaceholder": "Optional note", - "tokenEmpty": "Token cannot be empty", - "tokenExists": "Token already exists", - "confirmDelete": "Are you sure you want to delete this Token?", - "confirmDisable": "Are you sure you want to disable this Token?\n{token}", - "confirmEnable": "Are you sure you want to enable this Token?\n{token}", - "confirmBatchDelete": "Are you sure you want to delete {count} selected Tokens?", - "confirmBatchDisable": "Are you sure you want to disable {count} selected Tokens?", - "confirmBatchEnable": "Are you sure you want to enable {count} selected Tokens?", - "disableToken": "Disable", - "enableToken": "Enable", - "disableDone": "Token disabled", - "enableDone": "Token enabled", - "batchDisableDone": "Batch disable complete", - "batchEnableDone": "Batch enable complete", - "noTokenToDisable": "All selected Tokens are already disabled", - "noTokenToEnable": "All selected Tokens are already enabled", - "deleteDone": "Delete complete", - "refreshDone": "Refresh complete", - "refreshSuccess": "Refresh successful", - "refreshFailed": "Refresh failed", - "refreshError": "Refresh failed: {msg}", - "requestError": "Request error", - "notValidJson": "Response is not valid JSON (HTTP {status})", - "listEmpty": "List is empty", - "stopRefresh": "Refresh stopped", - "stopDelete": "Delete stopped", - "stopNsfw": "NSFW stopped", - "nsfwConfirm": "Enable NSFW mode for {count} selected Tokens?", - "nsfwEnable": "Enable NSFW", - "nsfwDone": "NSFW enable complete", - "nsfwResult": "NSFW enable complete: {ok} succeeded, {fail} failed", - "nsfwFailed": "Enable failed: {msg}", - "emptyResponse": "Empty response (HTTP {status})" - }, - "cache": { - "pageTitle": "Grok2API - Cache Mgmt", - "title": "Cache Management", - "subtitle": "Manage local resources and online asset cache.", - "localImages": "Local Images", - "localVideos": "Local Videos", - "onlineAssets": "Online Assets", - "tableFile": "File", - "tableSize": "Size", - "tableTime": "Time", - "tableActions": "Actions", - "tableToken": "Token", - "tableType": "Type", - "tableAssetCount": "Asset Count", - "tableLastClear": "Last Cleared", - "notLoaded": "Not loaded", - "noAccounts": "No accounts available", - "noFiles": "No files", - "noTokenAvailable": "No Token available", - "cannotConnect": "Cannot connect", - "lastClear": "Last cleared: {time}", - "clear": "Clear", - "clean": "Clean", - "confirmClearImage": "Clear all local image cache?", - "confirmClearVideo": "Clear all local video cache?", - "clearSuccess": "Cleanup successful, freed {size} MB", - "clearFailed": "Cleanup failed", - "confirmDeleteFile": "Are you sure you want to delete this file?", - "confirmBatchDeleteFiles": "Are you sure you want to delete {count} selected files?", - "noFilesSelected": "No files selected", - "deletedFiles": "Deleted {count} files", - "deleteResult": "Delete complete: {success} succeeded, {failed} failed", - "loadStatsFailed": "Failed to load statistics", - "selectAccountsToLoad": "Please select accounts to load", - "selectAccountsToClear": "Please select accounts to clear", - "confirmClearAccounts": "Clear online assets for {count} selected accounts?", - "confirmClearAccount": "Clear online assets for account {label}?", - "batchCleanInProgress": "Batch cleaning online assets, please wait...", - "cleanInProgress": "Cleaning online assets, please wait...", - "batchCleanDone": "Batch cleanup complete", - "cleanResult": "Cleanup complete (succeeded: {success}, failed: {failed})", - "cleanFailedEntry": "Cleanup failed", - "requestTimeout": "Request timed out or failed", - "loadDone": "Load complete", - "loadingStatus": "Loading", - "loadStopped": "Load stopped", - "loadFailedMsg": "Load failed: {msg}", - "stoppedLoadRequests": "Remaining load requests stopped", - "cleanStopped": "Clean stopped", - "cleanFailedMsg": "Clean failed: {msg}", - "stoppedCleanRequests": "Remaining clean requests stopped" + "levels": { + "all": "All Levels" }, - "chat": { - "pageTitle": "Grok2API - Chat", - "title": "Chat", - "subtitle": "Chat with Grok via the chat API", - "sessions": "Sessions", - "newChat": "New", - "collapseSidebar": "Collapse", - "expandSidebar": "Expand", - "sessionList": "Session list", - "newSession": "New Session", - "ready": "Ready", - "emptyState": "Type a message to start a conversation.", - "uploadFile": "Upload file", - "placeholder": "Ask anything", - "settings": "Settings", - "send": "Send", - "temperature": "Temperature", - "topP": "Top P", - "systemPrompt": "System Prompt", - "systemPromptPlaceholder": "Optional, set system prompt", - "fileLabel": "[File]", - "compositeContent": "[Composite]", - "storageFull": "Local storage is full, some sessions were not saved", - "attachmentNoEdit": "Attachment messages cannot be edited yet", - "saveEdit": "Save", - "saveEditTitle": "Save edit", - "cancelEdit": "Cancel", - "cancelEditTitle": "Cancel edit", - "contentEmpty": "Content cannot be empty", - "editMessage": "Edit", - "editMessageTitle": "Edit message content", - "regenerate": "Regenerate", - "regenerateTitle": "Regenerate response from here", - "retryTitle": "Retry last response", - "editAnswer": "Edit", - "editAnswerTitle": "Edit response content", - "copyAnswer": "Copy", - "copyAnswerTitle": "Copy response content", - "feedback": "Feedback", - "feedbackTitle": "Feedback to Grok2API", - "noContentToCopy": "No content to copy", - "noChatToRetry": "No conversation to retry", - "requestFailedCheck": "Request failed, please check service status", - "requestFailedStatus": "Request failed: {status}", - "clickRetry": "Click to retry", - "thinking": "Thinking", - "thinkingSec": "Thinking {sec}s", - "thought": "Thought", - "thinkLabel": "Thinking", - "empty": "(empty)", - "toolWebSearch": "Web search", - "toolImageSearch": "Image search", - "toolAgentThink": "Reasoning", - "toolDefault": "Tool" + "stats": { + "file": "Current File", + "entries": "Loaded Rows", + "entriesHint": "Shown with current filters", + "errorWarn": "Warnings / Errors", + "errorWarnHint": "warning + error", + "size": "Total Size", + "sizeHint": "Log directory usage" }, - "imagine": { - "pageTitle": "Grok2API - Imagine Gallery", - "title": "Imagine Gallery", - "subtitle": "Continuously generate images via WebSocket, real-time base64 gallery.", - "genSettings": "Generation Settings", - "prompt": "Prompt", - "promptPlaceholder": "Describe the image you want, e.g.: futuristic city neon rain at night, wide angle photography", - "autoFollow": "Auto Follow", - "autoFollowDesc": "Scroll to latest", - "autoSave": "Auto Save", - "autoSaveDesc": "Auto-save images", - "aspectRatio": "Aspect Ratio", - "ratio2_3": "2:3 Portrait", - "ratio1_1": "1:1 Square", - "ratio3_2": "3:2 Landscape", - "ratio16_9": "16:9 Wide", - "ratio9_16": "9:16 Tall", - "concurrent": "Concurrency", - "tasks": "{n} tasks", - "concurrentTask1": "1 task", - "concurrentTask2": "2 tasks", - "concurrentTask3": "3 tasks", - "autoFilter": "Auto Filter", - "autoFilterDesc": "Filter substandard images", - "nsfwLabel": "NSFW", - "nsfwOn": "On", - "nsfwOff": "Off", - "saveLocation": "Save Location", - "browserDefault": "Browser default location", - "reverseOrder": "Reverse Order", - "reverseOrderDesc": "Show newest on top", - "connectionStatus": "Connection Status", - "connectionMode": "Connection Mode", - "connectionModeAria": "Connection mode", - "imageCount": "Image Count", - "activeTasks": "Active Tasks", - "avgTime": "Avg Time", - "gallery": "Gallery", - "waitingTitle": "Waiting for connection", - "waitingSubtitle": "Generated images will appear here after starting a task", - "start": "Start", - "batch": "Batch", - "batchDownloadTitle": "Batch download", - "selectAll": "Select all", - "deselectAll": "Deselect all", - "download": "Download", - "startedTasks": "Started {count} concurrent tasks", - "startedTasksSSE": "Started {count} concurrent tasks (SSE)", - "generatingSSE": "Generating (SSE)", - "selectFolder": "Selected folder: {name}", - "selectFolderFailed": "Failed to select folder", - "noImagesSelected": "Please select images to download first", - "jszipFailed": "JSZip library failed to load, please refresh and try again", - "packing": "Packing {count} images...", - "packingBtn": "Packing...", - "packingProgress": "Packing... ({done}/{total})", - "noImagesDownloaded": "No images were downloaded successfully", - "generatingZip": "Generating ZIP...", - "packSuccess": "Successfully packed {count} images", - "packFailed": "Packing failed, please try again" + "loadFilesFailed": "Failed to load log files", + "loadFailed": "Failed to load logs", + "noFiles": "No log files", + "fileCount": "{count} log files", + "deleteNone": "Select log files to delete first", + "deleteConfirm": "Delete {count} log files?", + "deleteFailed": "Failed to delete logs", + "deleteResult": "Deleted {deleted} log files, {failed} failed", + "toggle": { + "expand": "Expand", + "collapse": "Collapse" }, - "video": { - "pageTitle": "Grok2API - Video Generation", - "title": "Video Generation", - "subtitle": "Generate short videos with reference images and preset styles.", - "startGenerate": "Generate", - "genSettings": "Generation Settings", - "prompt": "Prompt", - "promptPlaceholder": "e.g.: neon rain at night on the street, slow motion, film grain", - "referenceImage": "Reference Image", - "referenceImagePlaceholder": "https://... or data:image/...", - "aspectRatio": "Aspect Ratio", - "ratio3_2": "3:2 Landscape", - "ratio2_3": "2:3 Portrait", - "ratio16_9": "16:9 Wide", - "ratio9_16": "9:16 Tall", - "ratio1_1": "1:1 Square", - "duration": "Duration", - "seconds": "{n}s", - "resolution": "Resolution", - "stylePreset": "Style Preset", - "upload": "Upload", - "clearImage": "Clear", - "runStatus": "Run Status", - "elapsedTime": "Elapsed {sec}s", - "elapsedTimeNone": "Elapsed -", - "metaRatio": "Ratio", - "metaDuration": "Duration", - "metaResolution": "Resolution", - "metaPreset": "Preset", - "videoPreview": "Video Preview", - "clearPreview": "Clear", - "waitingGenerate": "Waiting for video generation", - "videoTitle": "Video {n}", - "open": "Open", - "generatingPlaceholder": "Generating...", - "superResolution": "Super Resolution", - "superResolutionInProgress": "Super resolution in progress", - "alreadyGenerating": "Already generating", - "referenceConflict": "Reference image: choose either URL/Base64 or file upload", - "downloadFailed": "Download failed, please check if the video link is accessible", - "sec6": "6s", - "sec10": "10s", - "sec15": "15s" + "autoRefresh": { + "buttonAria": "Auto refresh", + "off": "Auto refresh: Off", + "offShort": "Off", + "interval": "Auto refresh: {seconds}s", + "intervalLabel": "{seconds}s", + "changed": "Auto refresh: {label}", + "disabled": "Auto refresh disabled" }, - "voice": { - "pageTitle": "Grok2API - LiveKit Voice", - "title": "LiveKit Voice", - "subtitle": "LiveKit voice session, connect to Grok Voice.", - "startSession": "Start Session", - "stopSession": "Stop Session", - "connectionSettings": "Connection Settings", - "voiceLabel": "Voice", - "personalityLabel": "Personality", - "speedLabel": "Speed", - "settingsHint": "Tip: Voice / Personality settings are sent to the server before connecting.", - "sessionLog": "Session Log", - "clearLog": "Clear", - "copyLog": "Copy", - "sessionStatus": "Session Status", - "audioOutput": "Audio Output", - "audioHint": "Subscribed audio tracks will be automatically inserted here.", - "livekitSDKError": "Error: LiveKit SDK failed to load properly, please refresh the page", - "livekitLoadFailed": "LiveKit SDK failed to load", - "secureContextBrowser": "Please use a modern browser and allow microphone access", - "secureContextHTTPS": "Please use HTTPS or access from localhost", - "secureContextError": "Current environment does not support microphone access, {hint}", - "connectingStatus": "Connecting", - "fetchingToken": "Fetching Token...", - "fetchTokenFailed": "Failed to get Token: {status}", - "fetchTokenSuccess": "Token obtained successfully", - "participantConnected": "Participant connected: {identity}", - "participantDisconnected": "Participant disconnected: {identity}", - "trackSubscribed": "Track subscribed: {kind}", - "disconnected": "Disconnected", - "connectedToServer": "Connected to LiveKit server", - "inCall": "In call", - "openingMic": "Opening microphone...", - "voiceEnabled": "Voice enabled", - "voiceConnected": "Voice connected successfully", - "errorPrefix": "Error: {msg}", - "logCopied": "Log copied", - "copyLogFailed": "Copy failed, please select manually" - } + "deleteSelected": "Delete Selected" + }, + "token": { + "pageTitle": "Grok2API - Token Mgmt", + "title": "Token List", + "subtitle": "Manage the Grok2API Token service pool.", + "import": "Import", + "add": "Add", + "statTotal": "Total Tokens", + "statActive": "Active", + "statCooling": "Rate Limited", + "statInvalid": "Invalid", + "statChatQuota": "Chat Remaining", + "statImageQuota": "Image Remaining", + "statVideoQuota": "Video Remaining", + "statVideoUnavailable": "N/A", + "statTotalCalls": "Total Calls", + "tabAll": "All", + "tabActive": "Active", + "tabCooling": "Rate Limited", + "tabExpired": "Expired", + "tabNsfw": "NSFW On", + "tabNoNsfw": "NSFW Off", + "statusFilterAria": "Token status filter", + "tableToken": "Token", + "tableType": "Type", + "tableStatus": "Status", + "tableQuota": "Quota", + "tableNote": "Note", + "tableActions": "Actions", + "refreshStatus": "Refresh status", + "emptyState": "No Tokens yet. Click Import or Add above.", + "emptyFilterState": "No results for current filter. Try a different filter.", + "selectPage": "Select page", + "selectAllFiltered": "Select all", + "clearSelection": "Clear selection", + "prevPage": "Prev", + "nextPage": "Next", + "perPage": "{size} / page", + "pagination": "Page {current} / {total} · {count} items", + "batchExport": "Export", + "batchRefresh": "Refresh", + "batchDisable": "Disable", + "batchEnable": "Enable", + "batchDelete": "Delete", + "importTitle": "Batch Import Tokens", + "importTargetPool": "Target Pool", + "importTokenList": "Token List (one per line)", + "importPlaceholder": "Paste Tokens, one per line...", + "startImport": "Start Import", + "editTitle": "Edit Token", + "addTitle": "Add Token", + "editType": "Type", + "editQuota": "Quota", + "editNote": "Note", + "editNotePlaceholder": "Optional note", + "tokenEmpty": "Token cannot be empty", + "tokenExists": "Token already exists", + "confirmDelete": "Are you sure you want to delete this Token?", + "confirmDisable": "Are you sure you want to disable this Token?\n{token}", + "confirmEnable": "Are you sure you want to enable this Token?\n{token}", + "confirmBatchDelete": "Are you sure you want to delete {count} selected Tokens?", + "confirmBatchDisable": "Are you sure you want to disable {count} selected Tokens?", + "confirmBatchEnable": "Are you sure you want to enable {count} selected Tokens?", + "disableToken": "Disable", + "enableToken": "Enable", + "disableDone": "Token disabled", + "enableDone": "Token enabled", + "batchDisableDone": "Batch disable complete", + "batchEnableDone": "Batch enable complete", + "noTokenToDisable": "All selected Tokens are already disabled", + "noTokenToEnable": "All selected Tokens are already enabled", + "deleteDone": "Delete complete", + "refreshDone": "Refresh complete", + "refreshSuccess": "Refresh successful", + "refreshFailed": "Refresh failed", + "refreshError": "Refresh failed: {msg}", + "requestError": "Request error", + "notValidJson": "Response is not valid JSON (HTTP {status})", + "listEmpty": "List is empty", + "stopRefresh": "Refresh stopped", + "stopDelete": "Delete stopped", + "stopNsfw": "NSFW stopped", + "nsfwConfirm": "Enable NSFW mode for {count} selected Tokens?", + "nsfwEnable": "Enable NSFW", + "nsfwDone": "NSFW enable complete", + "nsfwResult": "NSFW enable complete: {ok} succeeded, {fail} failed", + "nsfwFailed": "Enable failed: {msg}", + "emptyResponse": "Empty response (HTTP {status})" + }, + "cache": { + "pageTitle": "Grok2API - Cache Mgmt", + "title": "Cache Management", + "subtitle": "Manage local resources and online asset cache.", + "localImages": "Local Images", + "localVideos": "Local Videos", + "onlineAssets": "Online Assets", + "tableFile": "File", + "tableSize": "Size", + "tableTime": "Time", + "tableActions": "Actions", + "tableToken": "Token", + "tableType": "Type", + "tableAssetCount": "Asset Count", + "tableLastClear": "Last Cleared", + "notLoaded": "Not loaded", + "noAccounts": "No accounts available", + "noFiles": "No files", + "noTokenAvailable": "No Token available", + "cannotConnect": "Cannot connect", + "lastClear": "Last cleared: {time}", + "clear": "Clear", + "clean": "Clean", + "confirmClearImage": "Clear all local image cache?", + "confirmClearVideo": "Clear all local video cache?", + "clearSuccess": "Cleanup successful, freed {size} MB", + "clearFailed": "Cleanup failed", + "confirmDeleteFile": "Are you sure you want to delete this file?", + "confirmBatchDeleteFiles": "Are you sure you want to delete {count} selected files?", + "noFilesSelected": "No files selected", + "deletedFiles": "Deleted {count} files", + "deleteResult": "Delete complete: {success} succeeded, {failed} failed", + "loadStatsFailed": "Failed to load statistics", + "selectAccountsToLoad": "Please select accounts to load", + "selectAccountsToClear": "Please select accounts to clear", + "confirmClearAccounts": "Clear online assets for {count} selected accounts?", + "confirmClearAccount": "Clear online assets for account {label}?", + "batchCleanInProgress": "Batch cleaning online assets, please wait...", + "cleanInProgress": "Cleaning online assets, please wait...", + "batchCleanDone": "Batch cleanup complete", + "cleanResult": "Cleanup complete (succeeded: {success}, failed: {failed})", + "cleanFailedEntry": "Cleanup failed", + "requestTimeout": "Request timed out or failed", + "loadDone": "Load complete", + "loadingStatus": "Loading", + "loadStopped": "Load stopped", + "loadFailedMsg": "Load failed: {msg}", + "stoppedLoadRequests": "Remaining load requests stopped", + "cleanStopped": "Clean stopped", + "cleanFailedMsg": "Clean failed: {msg}", + "stoppedCleanRequests": "Remaining clean requests stopped" + }, + "chat": { + "pageTitle": "Grok2API - Chat", + "title": "Chat", + "subtitle": "Chat with Grok via the chat API", + "sessions": "Sessions", + "newChat": "New", + "collapseSidebar": "Collapse", + "expandSidebar": "Expand", + "sessionList": "Session list", + "newSession": "New Session", + "ready": "Ready", + "emptyState": "Type a message to start a conversation.", + "uploadFile": "Upload file", + "placeholder": "Ask anything", + "settings": "Settings", + "send": "Send", + "temperature": "Temperature", + "topP": "Top P", + "systemPrompt": "System Prompt", + "systemPromptPlaceholder": "Optional, set system prompt", + "fileLabel": "[File]", + "compositeContent": "[Composite]", + "storageFull": "Local storage is full, some sessions were not saved", + "attachmentNoEdit": "Attachment messages cannot be edited yet", + "saveEdit": "Save", + "saveEditTitle": "Save edit", + "cancelEdit": "Cancel", + "cancelEditTitle": "Cancel edit", + "contentEmpty": "Content cannot be empty", + "editMessage": "Edit", + "editMessageTitle": "Edit message content", + "regenerate": "Regenerate", + "regenerateTitle": "Regenerate response from here", + "retryTitle": "Retry last response", + "editAnswer": "Edit", + "editAnswerTitle": "Edit response content", + "copyAnswer": "Copy", + "copyAnswerTitle": "Copy response content", + "feedback": "Feedback", + "feedbackTitle": "Feedback to Grok2API", + "noContentToCopy": "No content to copy", + "noChatToRetry": "No conversation to retry", + "requestFailedCheck": "Request failed, please check service status", + "requestFailedStatus": "Request failed: {status}", + "clickRetry": "Click to retry", + "thinking": "Thinking", + "thinkingSec": "Thinking {sec}s", + "thought": "Thought", + "thinkLabel": "Thinking", + "empty": "(empty)", + "toolWebSearch": "Web search", + "toolImageSearch": "Image search", + "toolAgentThink": "Reasoning", + "toolDefault": "Tool" + }, + "imagine": { + "pageTitle": "Grok2API - Imagine Gallery", + "title": "Imagine Gallery", + "subtitle": "Continuously generate images via WebSocket, real-time base64 gallery.", + "genSettings": "Generation Settings", + "prompt": "Prompt", + "promptPlaceholder": "Describe the image you want, e.g.: futuristic city neon rain at night, wide angle photography", + "autoFollow": "Auto Follow", + "autoFollowDesc": "Scroll to latest", + "autoSave": "Auto Save", + "autoSaveDesc": "Auto-save images", + "aspectRatio": "Aspect Ratio", + "ratio2_3": "2:3 Portrait", + "ratio1_1": "1:1 Square", + "ratio3_2": "3:2 Landscape", + "ratio16_9": "16:9 Wide", + "ratio9_16": "9:16 Tall", + "concurrent": "Concurrency", + "tasks": "{n} tasks", + "concurrentTask1": "1 task", + "concurrentTask2": "2 tasks", + "concurrentTask3": "3 tasks", + "autoFilter": "Auto Filter", + "autoFilterDesc": "Filter substandard images", + "nsfwLabel": "NSFW", + "nsfwOn": "On", + "nsfwOff": "Off", + "saveLocation": "Save Location", + "browserDefault": "Browser default location", + "reverseOrder": "Reverse Order", + "reverseOrderDesc": "Show newest on top", + "connectionStatus": "Connection Status", + "connectionMode": "Connection Mode", + "connectionModeAria": "Connection mode", + "imageCount": "Image Count", + "activeTasks": "Active Tasks", + "avgTime": "Avg Time", + "gallery": "Gallery", + "waitingTitle": "Waiting for connection", + "waitingSubtitle": "Generated images will appear here after starting a task", + "start": "Start", + "batch": "Batch", + "batchDownloadTitle": "Batch download", + "selectAll": "Select all", + "deselectAll": "Deselect all", + "download": "Download", + "startedTasks": "Started {count} concurrent tasks", + "startedTasksSSE": "Started {count} concurrent tasks (SSE)", + "generatingSSE": "Generating (SSE)", + "selectFolder": "Selected folder: {name}", + "selectFolderFailed": "Failed to select folder", + "noImagesSelected": "Please select images to download first", + "jszipFailed": "JSZip library failed to load, please refresh and try again", + "packing": "Packing {count} images...", + "packingBtn": "Packing...", + "packingProgress": "Packing... ({done}/{total})", + "noImagesDownloaded": "No images were downloaded successfully", + "generatingZip": "Generating ZIP...", + "packSuccess": "Successfully packed {count} images", + "packFailed": "Packing failed, please try again" + }, + "video": { + "pageTitle": "Grok2API - Video Generation", + "title": "Video Generation", + "subtitle": "Generate short videos with reference images and preset styles.", + "startGenerate": "Generate", + "genSettings": "Generation Settings", + "prompt": "Prompt", + "promptPlaceholder": "e.g.: neon rain at night on the street, slow motion, film grain", + "referenceImage": "Reference Image", + "referenceImagePlaceholder": "https://... or data:image/...", + "aspectRatio": "Aspect Ratio", + "ratio3_2": "3:2 Landscape", + "ratio2_3": "2:3 Portrait", + "ratio16_9": "16:9 Wide", + "ratio9_16": "9:16 Tall", + "ratio1_1": "1:1 Square", + "duration": "Duration", + "seconds": "{n}s", + "resolution": "Resolution", + "stylePreset": "Style Preset", + "upload": "Upload", + "clearImage": "Clear", + "runStatus": "Run Status", + "elapsedTime": "Elapsed {sec}s", + "elapsedTimeNone": "Elapsed -", + "metaRatio": "Ratio", + "metaDuration": "Duration", + "metaResolution": "Resolution", + "metaPreset": "Preset", + "videoPreview": "Video Preview", + "clearPreview": "Clear", + "waitingGenerate": "Waiting for video generation", + "videoTitle": "Video {n}", + "open": "Open", + "generatingPlaceholder": "Generating...", + "superResolution": "Super Resolution", + "superResolutionInProgress": "Super resolution in progress", + "alreadyGenerating": "Already generating", + "referenceConflict": "Reference image: choose either URL/Base64 or file upload", + "downloadFailed": "Download failed, please check if the video link is accessible", + "sec6": "6s", + "sec10": "10s", + "sec15": "15s" + }, + "voice": { + "pageTitle": "Grok2API - LiveKit Voice", + "title": "LiveKit Voice", + "subtitle": "LiveKit voice session, connect to Grok Voice.", + "startSession": "Start Session", + "stopSession": "Stop Session", + "connectionSettings": "Connection Settings", + "voiceLabel": "Voice", + "personalityLabel": "Personality", + "speedLabel": "Speed", + "settingsHint": "Tip: Voice / Personality settings are sent to the server before connecting.", + "sessionLog": "Session Log", + "clearLog": "Clear", + "copyLog": "Copy", + "sessionStatus": "Session Status", + "audioOutput": "Audio Output", + "audioHint": "Subscribed audio tracks will be automatically inserted here.", + "livekitSDKError": "Error: LiveKit SDK failed to load properly, please refresh the page", + "livekitLoadFailed": "LiveKit SDK failed to load", + "secureContextBrowser": "Please use a modern browser and allow microphone access", + "secureContextHTTPS": "Please use HTTPS or access from localhost", + "secureContextError": "Current environment does not support microphone access, {hint}", + "connectingStatus": "Connecting", + "fetchingToken": "Fetching Token...", + "fetchTokenFailed": "Failed to get Token: {status}", + "fetchTokenSuccess": "Token obtained successfully", + "participantConnected": "Participant connected: {identity}", + "participantDisconnected": "Participant disconnected: {identity}", + "trackSubscribed": "Track subscribed: {kind}", + "disconnected": "Disconnected", + "connectedToServer": "Connected to LiveKit server", + "inCall": "In call", + "openingMic": "Opening microphone...", + "voiceEnabled": "Voice enabled", + "voiceConnected": "Voice connected successfully", + "errorPrefix": "Error: {msg}", + "logCopied": "Log copied", + "copyLogFailed": "Copy failed, please select manually" + } } diff --git a/_public/static/i18n/locales/zh.json b/_public/static/i18n/locales/zh.json index e68b4f8f..57106b02 100644 --- a/_public/static/i18n/locales/zh.json +++ b/_public/static/i18n/locales/zh.json @@ -1,850 +1,851 @@ { - "common": { - "save": "\u4fdd\u5b58", - "cancel": "\u53d6\u6d88", - "confirm": "\u786e\u8ba4", - "delete": "\u5220\u9664", - "loading": "\u52a0\u8f7d\u4e2d...", - "invalidKey": "\u5bc6\u94a5\u65e0\u6548", - "connectionFailed": "\u8fde\u63a5\u5931\u8d25", - "requestFailed": "\u8bf7\u6c42\u5931\u8d25", - "operationSuccess": "\u64cd\u4f5c\u6210\u529f", - "gotIt": "\u6211\u77e5\u9053\u4e86", - "notice": "\u63d0\u793a", - "rateLimitNotice": "GROK \u5b98\u65b9\u7f51\u9875\u66f4\u65b0\u540e\u672a\u771f\u5b9e\u66b4\u9732 rate-limits \u63a5\u53e3\uff0c\u5bfc\u81f4\u65e0\u6cd5\u51c6\u786e\u8ba1\u7b97 Token \u5269\u4f59\uff0c\u8bf7\u8010\u5fc3\u7b49\u5f85\u5b98\u65b9\u63a5\u53e3\u4e0a\u7ebf\uff0c\u76ee\u524d\u81ea\u52a8\u5237\u65b0\u540e\u4f1a\u66f4\u65b0\u4e3a 8 \u6b21", - "pleaseConfirm": "\u8bf7\u786e\u8ba4", - "ok": "\u786e\u5b9a", - "close": "\u5173\u95ed", - "error": "\u9519\u8bef", - "unknownError": "\u672a\u77e5\u9519\u8bef", - "connectionInterrupted": "\u8fde\u63a5\u4e2d\u65ad", - "taskInProgress": "\u5f53\u524d\u6709\u4efb\u52a1\u8fdb\u884c\u4e2d", - "taskNoPause": "\u5f53\u524d\u4efb\u52a1\u4e0d\u652f\u6301\u6682\u505c", - "batchNoPause": "\u5f53\u524d\u6279\u91cf\u4efb\u52a1\u4e0d\u652f\u6301\u6682\u505c", - "waitTaskFinish": "\u8bf7\u7b49\u5f85\u5f53\u524d\u4efb\u52a1\u7ed3\u675f", - "pause": "\u6682\u505c", - "stop": "\u7ec8\u6b62", - "selected": "\u5df2\u9009\u62e9", - "items": "\u9879", - "export": "\u5bfc\u51fa", - "refresh": "\u5237\u65b0", - "load": "\u52a0\u8f7d", - "files": "\u4e2a\u6587\u4ef6", - "view": "\u67e5\u770b", - "retry": "\u91cd\u8bd5", - "copy": "\u590d\u5236", - "edit": "\u7f16\u8f91", - "loadFailed": "\u52a0\u8f7d\u5931\u8d25", - "loadError": "\u52a0\u8f7d\u5931\u8d25: {msg}", - "saveFailed": "\u4fdd\u5b58\u5931\u8d25", - "saveError": "\u4fdd\u5b58\u9519\u8bef: {msg}", - "deleteFailed": "\u5220\u9664\u5931\u8d25", - "deleteSuccess": "\u5220\u9664\u6210\u529f", - "notConnected": "\u672a\u8fde\u63a5", - "connecting": "\u8fde\u63a5\u4e2d", - "connected": "\u8fde\u63a5\u6b63\u5e38", - "connectionError": "\u8fde\u63a5\u9519\u8bef", - "generating": "\u751f\u6210\u4e2d", - "done": "\u5b8c\u6210", - "failed": "\u5931\u8d25", - "stopped": "\u5df2\u505c\u6b62", - "terminated": "\u5df2\u7ec8\u6b62", - "sending": "\u53d1\u9001\u4e2d", - "fileReadFailed": "\u6587\u4ef6\u8bfb\u53d6\u5931\u8d25", - "noFileSelected": "\u672a\u9009\u62e9\u6587\u4ef6", - "configurePublicKey": "\u8bf7\u5148\u914d\u7f6e Public Key", - "enterPrompt": "\u8bf7\u8f93\u5165\u63d0\u793a\u8bcd", - "enterContent": "\u8bf7\u8f93\u5165\u5185\u5bb9", - "alreadyRunning": "\u5df2\u5728\u8fd0\u884c\u4e2d", - "createTaskFailed": "\u521b\u5efa\u4efb\u52a1\u5931\u8d25", - "copyFailed": "\u590d\u5236\u5931\u8d25", - "copied": "\u5df2\u590d\u5236", - "generationFailed": "\u751f\u6210\u5931\u8d25", - "partialError": "\u90e8\u5206\u5f02\u5e38", - "loadingInProgress": "\u6b63\u5728\u52a0\u8f7d\u4e2d\uff0c\u8bf7\u7a0d\u5019", - "cleaningInProgress": "\u6b63\u5728\u6e05\u7406\u4e2d\uff0c\u8bf7\u7a0d\u5019", - "noTokenSelected": "\u672a\u9009\u62e9 Token", - "clearCache": "\u6e05\u7a7a\u7f13\u5b58", - "failureDetails": "\u5931\u8d25\u8be6\u60c5", - "retryFailed": "\u91cd\u8bd5\u5931\u8d25\u9879" + "common": { + "save": "保存", + "cancel": "取消", + "confirm": "确认", + "delete": "删除", + "loading": "加载中...", + "invalidKey": "密钥无效", + "connectionFailed": "连接失败", + "requestFailed": "请求失败", + "operationSuccess": "操作成功", + "gotIt": "我知道了", + "notice": "提示", + "rateLimitNotice": "GROK 官方网页更新后未真实暴露 rate-limits 接口,导致无法准确计算 Token 剩余,请耐心等待官方接口上线,目前自动刷新后会更新为 8 次", + "pleaseConfirm": "请确认", + "ok": "确定", + "close": "关闭", + "error": "错误", + "unknownError": "未知错误", + "connectionInterrupted": "连接中断", + "taskInProgress": "当前有任务进行中", + "taskNoPause": "当前任务不支持暂停", + "batchNoPause": "当前批量任务不支持暂停", + "waitTaskFinish": "请等待当前任务结束", + "pause": "暂停", + "stop": "终止", + "selected": "已选择", + "items": "项", + "export": "导出", + "refresh": "刷新", + "load": "加载", + "files": "个文件", + "view": "查看", + "retry": "重试", + "copy": "复制", + "edit": "编辑", + "loadFailed": "加载失败", + "loadError": "加载失败: {msg}", + "saveFailed": "保存失败", + "saveError": "保存错误: {msg}", + "deleteFailed": "删除失败", + "deleteSuccess": "删除成功", + "notConnected": "未连接", + "connecting": "连接中", + "connected": "连接正常", + "connectionError": "连接错误", + "generating": "生成中", + "done": "完成", + "failed": "失败", + "stopped": "已停止", + "terminated": "已终止", + "sending": "发送中", + "fileReadFailed": "文件读取失败", + "noFileSelected": "未选择文件", + "configurePublicKey": "请先配置 Public Key", + "enterPrompt": "请输入提示词", + "enterContent": "请输入内容", + "alreadyRunning": "已在运行中", + "createTaskFailed": "创建任务失败", + "copyFailed": "复制失败", + "copied": "已复制", + "generationFailed": "生成失败", + "partialError": "部分异常", + "loadingInProgress": "正在加载中,请稍候", + "cleaningInProgress": "正在清理中,请稍候", + "noTokenSelected": "未选择 Token", + "clearCache": "清空缓存", + "failureDetails": "失败详情", + "retryFailed": "重试失败项", + "raw": "原始" + }, + "nav": { + "tokenManage": "Token管理", + "configManage": "配置管理", + "cacheManage": "缓存管理", + "logsView": "日志查看", + "chat": "Chat 聊天", + "imagine": "Imagine 瀑布流", + "video": "Video 视频生成", + "voice": "LiveKit 陪聊", + "feedback": "反馈", + "logout": "退出", + "adminPanel": "管理后台", + "storageMode": "存储模式" + }, + "login": { + "adminPageTitle": "Grok2API - 登录", + "adminTitle": "管理后台", + "adminSubtitle": "请输入后台密码以继续", + "adminPlaceholder": "后台密码 (app_key)", + "continue": "继续", + "publicPageTitle": "Grok2API - Public", + "publicTitle": "Public Access", + "publicSubtitle": "请输入 Public Key(如未配置可直接进入)", + "publicPlaceholder": "Public Key", + "enter": "进入", + "goToAdmin": "进入管理后台" + }, + "config": { + "pageTitle": "Grok2API - 配置管理", + "title": "配置管理", + "subtitle": "管理 API 密钥及系统参数设置。", + "saving": "保存中...", + "saved": "成功", + "configSaved": "配置已保存", + "invalidJson": "无效的 JSON: {field}", + "appKeyRequired": "app_key 不能为空(后台密码)", + "flaresolverrRequired": "启用自动刷新时必须填写 FlareSolverr 地址", + "generate": "生成", + "publicAccess": "功能玩法", + "noDesc": "暂无说明,请参考配置文档。", + "sectionFallback": "{section} 设置", + "sections": { + "app": "应用设置", + "proxy": "代理配置", + "retry": "重试策略", + "chat": "对话配置", + "video": "视频配置", + "image": "图像配置", + "imagine_fast": "Imagine Fast 配置", + "asset": "资产配置", + "voice": "语音配置", + "token": "Token 池管理", + "cache": "缓存管理", + "nsfw": "NSFW 配置", + "usage": "Usage 配置" }, - "nav": { - "tokenManage": "Token\u7ba1\u7406", - "configManage": "\u914d\u7f6e\u7ba1\u7406", - "cacheManage": "\u7f13\u5b58\u7ba1\u7406", - "logsView": "\u65e5\u5fd7\u67e5\u770b", - "chat": "Chat \u804a\u5929", - "imagine": "Imagine \u7011\u5e03\u6d41", - "video": "Video \u89c6\u9891\u751f\u6210", - "voice": "LiveKit \u966a\u804a", - "feedback": "\u53cd\u9988", - "logout": "\u9000\u51fa", - "adminPanel": "\u7ba1\u7406\u540e\u53f0", - "storageMode": "\u5b58\u50a8\u6a21\u5f0f" + "sectionDescs": { + "proxy": "配置不正确将导致 403 错误。服务首次请求 Grok 时的 IP 必须与获取 CF Clearance 时的 IP 一致,后续服务器请求 IP 变化不会导致 403。" }, - "login": { - "adminPageTitle": "Grok2API - \u767b\u5f55", - "adminTitle": "\u7ba1\u7406\u540e\u53f0", - "adminSubtitle": "\u8bf7\u8f93\u5165\u540e\u53f0\u5bc6\u7801\u4ee5\u7ee7\u7eed", - "adminPlaceholder": "\u540e\u53f0\u5bc6\u7801 (app_key)", - "continue": "\u7ee7\u7eed", - "publicPageTitle": "Grok2API - Public", - "publicTitle": "Public Access", - "publicSubtitle": "\u8bf7\u8f93\u5165 Public Key\uff08\u5982\u672a\u914d\u7f6e\u53ef\u76f4\u63a5\u8fdb\u5165\uff09", - "publicPlaceholder": "Public Key", - "enter": "\u8fdb\u5165", - "goToAdmin": "\u8fdb\u5165\u7ba1\u7406\u540e\u53f0" - }, - "config": { - "pageTitle": "Grok2API - \u914d\u7f6e\u7ba1\u7406", - "title": "\u914d\u7f6e\u7ba1\u7406", - "subtitle": "\u7ba1\u7406 API \u5bc6\u94a5\u53ca\u7cfb\u7edf\u53c2\u6570\u8bbe\u7f6e\u3002", - "saving": "\u4fdd\u5b58\u4e2d...", - "saved": "\u6210\u529f", - "configSaved": "\u914d\u7f6e\u5df2\u4fdd\u5b58", - "invalidJson": "\u65e0\u6548\u7684 JSON: {field}", - "appKeyRequired": "app_key \u4e0d\u80fd\u4e3a\u7a7a\uff08\u540e\u53f0\u5bc6\u7801\uff09", - "flaresolverrRequired": "\u542f\u7528\u81ea\u52a8\u5237\u65b0\u65f6\u5fc5\u987b\u586b\u5199 FlareSolverr \u5730\u5740", - "generate": "\u751f\u6210", - "publicAccess": "\u529f\u80fd\u73a9\u6cd5", - "noDesc": "\u6682\u65e0\u8bf4\u660e\uff0c\u8bf7\u53c2\u8003\u914d\u7f6e\u6587\u6863\u3002", - "sectionFallback": "{section} \u8bbe\u7f6e", - "sections": { - "app": "\u5e94\u7528\u8bbe\u7f6e", - "proxy": "\u4ee3\u7406\u914d\u7f6e", - "retry": "\u91cd\u8bd5\u7b56\u7565", - "chat": "\u5bf9\u8bdd\u914d\u7f6e", - "video": "\u89c6\u9891\u914d\u7f6e", - "image": "\u56fe\u50cf\u914d\u7f6e", - "imagine_fast": "Imagine Fast \u914d\u7f6e", - "asset": "\u8d44\u4ea7\u914d\u7f6e", - "voice": "\u8bed\u97f3\u914d\u7f6e", - "token": "Token \u6c60\u7ba1\u7406", - "cache": "\u7f13\u5b58\u7ba1\u7406", - "nsfw": "NSFW \u914d\u7f6e", - "usage": "Usage \u914d\u7f6e" - }, - "sectionDescs": { - "proxy": "\u914d\u7f6e\u4e0d\u6b63\u786e\u5c06\u5bfc\u81f4 403 \u9519\u8bef\u3002\u670d\u52a1\u9996\u6b21\u8bf7\u6c42 Grok \u65f6\u7684 IP \u5fc5\u987b\u4e0e\u83b7\u53d6 CF Clearance \u65f6\u7684 IP \u4e00\u81f4\uff0c\u540e\u7eed\u670d\u52a1\u5668\u8bf7\u6c42 IP \u53d8\u5316\u4e0d\u4f1a\u5bfc\u81f4 403\u3002" - }, - "fields": { - "app": { - "api_key": { - "title": "API \u5bc6\u94a5", - "desc": "\u8c03\u7528 Grok2API \u670d\u52a1\u7684 Token\uff08\u53ef\u9009\uff0c\u652f\u6301\u591a\u4e2a\uff0c\u9017\u53f7\u5206\u9694\u6216\u6570\u7ec4\uff09\u3002" - }, - "app_key": { - "title": "\u540e\u53f0\u5bc6\u7801", - "desc": "\u767b\u5f55 Grok2API \u7ba1\u7406\u540e\u53f0\u7684\u5bc6\u7801\uff08\u5fc5\u586b\uff09\u3002" - }, - "function_enabled": { - "title": "\u542f\u7528\u529f\u80fd\u73a9\u6cd5", - "desc": "\u662f\u5426\u542f\u7528\u529f\u80fd\u73a9\u6cd5\u5165\u53e3\uff08\u5173\u95ed\u5219\u529f\u80fd\u73a9\u6cd5\u9875\u9762\u4e0d\u53ef\u8bbf\u95ee\uff09\u3002" - }, - "function_key": { - "title": "Function \u5bc6\u7801", - "desc": "\u529f\u80fd\u73a9\u6cd5\u9875\u9762\u7684\u8bbf\u95ee\u5bc6\u7801\uff08\u53ef\u9009\uff09\u3002" - }, - "app_url": { - "title": "\u5e94\u7528\u5730\u5740", - "desc": "\u5f53\u524d Grok2API \u670d\u52a1\u7684\u5916\u90e8\u8bbf\u95ee URL\uff0c\u7528\u4e8e\u6587\u4ef6\u94fe\u63a5\u8bbf\u95ee\u3002" - }, - "image_format": { - "title": "\u56fe\u7247\u683c\u5f0f", - "desc": "\u9ed8\u8ba4\u751f\u6210\u7684\u56fe\u7247\u683c\u5f0f\uff08url \u6216 base64\uff09\u3002" - }, - "video_format": { - "title": "\u89c6\u9891\u683c\u5f0f", - "desc": "\u9ed8\u8ba4\u751f\u6210\u7684\u89c6\u9891\u683c\u5f0f\uff08html \u6216 url\uff0curl \u4e3a\u5904\u7406\u540e\u7684\u94fe\u63a5\uff09\u3002" - }, - "temporary": { - "title": "\u4e34\u65f6\u5bf9\u8bdd", - "desc": "\u662f\u5426\u9ed8\u8ba4\u542f\u7528\u4e34\u65f6\u5bf9\u8bdd\u6a21\u5f0f\u3002" - }, - "disable_memory": { - "title": "\u7981\u7528\u8bb0\u5fc6", - "desc": "\u662f\u5426\u9ed8\u8ba4\u7981\u7528 Grok \u8bb0\u5fc6\u529f\u80fd\u3002" - }, - "stream": { - "title": "\u6d41\u5f0f\u54cd\u5e94", - "desc": "\u662f\u5426\u9ed8\u8ba4\u542f\u7528\u6d41\u5f0f\u8f93\u51fa\u3002" - }, - "thinking": { - "title": "\u601d\u7ef4\u94fe", - "desc": "\u662f\u5426\u9ed8\u8ba4\u542f\u7528\u601d\u7ef4\u94fe\u8f93\u51fa\u3002" - }, - "dynamic_statsig": { - "title": "\u52a8\u6001\u6307\u7eb9", - "desc": "\u662f\u5426\u9ed8\u8ba4\u542f\u7528\u52a8\u6001\u751f\u6210 Statsig \u6307\u7eb9\u3002" - }, - "custom_instruction": { - "title": "\u81ea\u5b9a\u4e49\u6307\u4ee4", - "desc": "\u591a\u884c\u6587\u672c\uff0c\u4f1a\u900f\u4f20\u4e3a Grok \u8bf7\u6c42\u53c2\u6570 customPersonality\u3002" - }, - "filter_tags": { - "title": "\u8fc7\u6ee4\u6807\u7b7e", - "desc": "\u8bbe\u7f6e\u81ea\u52a8\u8fc7\u6ee4 Grok \u54cd\u5e94\u4e2d\u7684\u7279\u6b8a\u6807\u7b7e\u3002" - } - }, - "proxy": { - "base_proxy_url": { - "title": "\u57fa\u7840\u4ee3\u7406 URL", - "desc": "\u4ee3\u7406\u8bf7\u6c42\u5230 Grok \u5b98\u7f51\u7684\u57fa\u7840\u670d\u52a1\u5730\u5740\u3002" - }, - "asset_proxy_url": { - "title": "\u8d44\u6e90\u4ee3\u7406 URL", - "desc": "\u4ee3\u7406\u8bf7\u6c42\u5230 Grok \u5b98\u7f51\u7684\u9759\u6001\u8d44\u6e90\uff08\u56fe\u7247/\u89c6\u9891\uff09\u5730\u5740\u3002" - }, - "skip_proxy_ssl_verify": { - "title": "\u8df3\u8fc7\u4ee3\u7406 SSL \u6821\u9a8c", - "desc": "\u4ee3\u7406\u4f7f\u7528\u81ea\u7b7e\u540d\u8bc1\u4e66\u65f6\u542f\u7528\uff08\u4ec5\u653e\u884c\u4ee3\u7406\u8bc1\u4e66\u6821\u9a8c\uff09\u3002" - }, - "enabled": { - "title": "\u542f\u7528 CF \u81ea\u52a8\u5237\u65b0", - "desc": "\u542f\u7528\u540e\u5c06\u901a\u8fc7 FlareSolverr \u81ea\u52a8\u83b7\u53d6 cf_clearance\u3002" - }, - "flaresolverr_url": { - "title": "FlareSolverr \u5730\u5740", - "desc": "FlareSolverr \u670d\u52a1\u7684 HTTP \u5730\u5740\uff08\u5982 http://flaresolverr:8191\uff09\u3002" - }, - "refresh_interval": { - "title": "\u5237\u65b0\u95f4\u9694\uff08\u79d2\uff09", - "desc": "\u81ea\u52a8\u5237\u65b0 cf_clearance \u7684\u65f6\u95f4\u95f4\u9694\uff0c\u5efa\u8bae\u4e0d\u4f4e\u4e8e 300 \u79d2\u3002" - }, - "timeout": { - "title": "\u6311\u6218\u8d85\u65f6\uff08\u79d2\uff09", - "desc": "\u7b49\u5f85 FlareSolverr \u89e3\u51b3 CF \u6311\u6218\u7684\u6700\u5927\u65f6\u95f4\u3002" - }, - "cf_clearance": { - "title": "CF Clearance", - "desc": "Cloudflare Clearance Cookie\uff0c\u7528\u4e8e\u7ed5\u8fc7\u53cd\u722c\u866b\u9a8c\u8bc1\u3002\u542f\u7528\u81ea\u52a8\u5237\u65b0\u65f6\u7531\u7cfb\u7edf\u81ea\u52a8\u7ba1\u7406\u3002" - }, - "browser": { - "title": "\u6d4f\u89c8\u5668\u6307\u7eb9", - "desc": "curl_cffi \u6d4f\u89c8\u5668\u6307\u7eb9\u6807\u8bc6\uff08\u5982 chrome136\uff09\u3002\u542f\u7528\u81ea\u52a8\u5237\u65b0\u65f6\u7531\u7cfb\u7edf\u81ea\u52a8\u7ba1\u7406\u3002" - }, - "user_agent": { - "title": "User-Agent", - "desc": "HTTP \u8bf7\u6c42\u7684 User-Agent \u5b57\u7b26\u4e32\u3002\u542f\u7528\u81ea\u52a8\u5237\u65b0\u65f6\u7531\u7cfb\u7edf\u81ea\u52a8\u7ba1\u7406\u3002" - } - }, - "retry": { - "max_retry": { - "title": "\u6700\u5927\u91cd\u8bd5\u6b21\u6570", - "desc": "\u8bf7\u6c42 Grok \u670d\u52a1\u5931\u8d25\u65f6\u7684\u6700\u5927\u91cd\u8bd5\u6b21\u6570\u3002" - }, - "retry_status_codes": { - "title": "\u91cd\u8bd5\u72b6\u6001\u7801", - "desc": "\u89e6\u53d1\u91cd\u8bd5\u7684 HTTP \u72b6\u6001\u7801\u5217\u8868\u3002" - }, - "reset_session_status_codes": { - "title": "\u91cd\u5efa\u72b6\u6001\u7801", - "desc": "\u89e6\u53d1\u91cd\u5efa session \u7684 HTTP \u72b6\u6001\u7801\u5217\u8868\uff08\u7528\u4e8e\u8f6e\u6362\u4ee3\u7406\uff09\u3002" - }, - "retry_backoff_base": { - "title": "\u9000\u907f\u57fa\u6570", - "desc": "\u91cd\u8bd5\u9000\u907f\u7684\u57fa\u7840\u5ef6\u8fdf\uff08\u79d2\uff09\u3002" - }, - "retry_backoff_factor": { - "title": "\u9000\u907f\u500d\u7387", - "desc": "\u91cd\u8bd5\u9000\u907f\u7684\u6307\u6570\u653e\u5927\u7cfb\u6570\u3002" - }, - "retry_backoff_max": { - "title": "\u9000\u907f\u4e0a\u9650", - "desc": "\u5355\u6b21\u91cd\u8bd5\u7b49\u5f85\u7684\u6700\u5927\u5ef6\u8fdf\uff08\u79d2\uff09\u3002" - }, - "retry_budget": { - "title": "\u9000\u907f\u9884\u7b97", - "desc": "\u5355\u6b21\u8bf7\u6c42\u7684\u6700\u5927\u91cd\u8bd5\u603b\u8017\u65f6\uff08\u79d2\uff09\u3002" - } - }, - "chat": { - "concurrent": { - "title": "\u5e76\u53d1\u4e0a\u9650", - "desc": "Reverse \u63a5\u53e3\u5e76\u53d1\u4e0a\u9650\u3002" - }, - "timeout": { - "title": "\u8bf7\u6c42\u8d85\u65f6", - "desc": "Reverse \u63a5\u53e3\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" - }, - "stream_timeout": { - "title": "\u6d41\u7a7a\u95f2\u8d85\u65f6", - "desc": "\u6d41\u5f0f\u7a7a\u95f2\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" - } - }, - "video": { - "concurrent": { - "title": "\u5e76\u53d1\u4e0a\u9650", - "desc": "Reverse \u63a5\u53e3\u5e76\u53d1\u4e0a\u9650\u3002" - }, - "timeout": { - "title": "\u8bf7\u6c42\u8d85\u65f6", - "desc": "Reverse \u63a5\u53e3\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" - }, - "stream_timeout": { - "title": "\u6d41\u7a7a\u95f2\u8d85\u65f6", - "desc": "\u6d41\u5f0f\u7a7a\u95f2\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" - } - }, - "image": { - "timeout": { - "title": "\u8bf7\u6c42\u8d85\u65f6", - "desc": "WebSocket \u8bf7\u6c42\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" - }, - "stream_timeout": { - "title": "\u6d41\u7a7a\u95f2\u8d85\u65f6", - "desc": "WebSocket \u6d41\u5f0f\u7a7a\u95f2\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" - }, - "final_timeout": { - "title": "\u6700\u7ec8\u56fe\u8d85\u65f6", - "desc": "\u6536\u5230\u4e2d\u7b49\u56fe\u540e\u7b49\u5f85\u6700\u7ec8\u56fe\u7684\u8d85\u65f6\u79d2\u6570\u3002" - }, - "blocked_grace_seconds": { - "title": "\u5ba1\u67e5\u5bbd\u9650\u79d2\u6570", - "desc": "\u6536\u5230\u4e2d\u7b49\u56fe\u540e\uff0c\u5224\u5b9a\u7591\u4f3c\u88ab\u5ba1\u67e5\u7684\u5bbd\u9650\u79d2\u6570\uff08\u9ed8\u8ba4 10 \u79d2\uff0c\u53ef\u81ea\u5b9a\u4e49\uff09\u3002" - }, - "nsfw": { - "title": "NSFW \u6a21\u5f0f", - "desc": "WebSocket \u8bf7\u6c42\u662f\u5426\u542f\u7528 NSFW\u3002" - }, - "medium_min_bytes": { - "title": "\u4e2d\u7b49\u56fe\u6700\u5c0f\u5b57\u8282", - "desc": "\u5224\u5b9a\u4e2d\u7b49\u8d28\u91cf\u56fe\u7684\u6700\u5c0f\u5b57\u8282\u6570\u3002" - }, - "final_min_bytes": { - "title": "\u6700\u7ec8\u56fe\u6700\u5c0f\u5b57\u8282", - "desc": "\u5224\u5b9a\u6700\u7ec8\u56fe\u7684\u6700\u5c0f\u5b57\u8282\u6570\uff08\u901a\u5e38 JPG > 100KB\uff09\u3002" - }, - "blocked_parallel_enabled": { - "title": "\u542f\u7528\u5e76\u884c\u8865\u507f", - "desc": "\u7591\u4f3c\u5ba1\u67e5/\u62e6\u622a\u65f6\uff0c\u662f\u5426\u542f\u7528\u5e76\u884c\u8865\u507f\u751f\u6210\u3002" - }, - "blocked_parallel_attempts": { - "title": "\u62e6\u622a\u8865\u507f\u5e76\u53d1\u6b21\u6570", - "desc": "\u7591\u4f3c\u5ba1\u67e5/\u62e6\u622a\u5bfc\u81f4\u65e0\u6700\u7ec8\u56fe\u65f6\uff0c\u81ea\u52a8\u5e76\u884c\u8865\u507f\u751f\u6210\u6b21\u6570\u3002" - } - }, - "imagine_fast": { - "n": { - "title": "\u751f\u6210\u6570\u91cf", - "desc": "\u4ec5\u7528\u4e8e grok-imagine-1.0-fast \u7684\u670d\u52a1\u7aef\u7edf\u4e00\u751f\u6210\u6570\u91cf\uff081-10\uff09\u3002" - }, - "size": { - "title": "\u56fe\u7247\u5c3a\u5bf8", - "desc": "\u4ec5\u7528\u4e8e grok-imagine-1.0-fast \u7684\u670d\u52a1\u7aef\u7edf\u4e00\u5c3a\u5bf8\u3002" - }, - "response_format": { - "title": "\u54cd\u5e94\u683c\u5f0f", - "desc": "\u4ec5\u7528\u4e8e grok-imagine-1.0-fast \u7684\u670d\u52a1\u7aef\u7edf\u4e00\u8fd4\u56de\u683c\u5f0f\u3002" - } - }, - "asset": { - "upload_concurrent": { - "title": "\u4e0a\u4f20\u5e76\u53d1", - "desc": "\u4e0a\u4f20\u63a5\u53e3\u7684\u6700\u5927\u5e76\u53d1\u6570\u3002\u63a8\u8350 30\u3002" - }, - "upload_timeout": { - "title": "\u4e0a\u4f20\u8d85\u65f6", - "desc": "\u4e0a\u4f20\u63a5\u53e3\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002\u63a8\u8350 60\u3002" - }, - "download_concurrent": { - "title": "\u4e0b\u8f7d\u5e76\u53d1", - "desc": "\u4e0b\u8f7d\u63a5\u53e3\u7684\u6700\u5927\u5e76\u53d1\u6570\u3002\u63a8\u8350 30\u3002" - }, - "download_timeout": { - "title": "\u4e0b\u8f7d\u8d85\u65f6", - "desc": "\u4e0b\u8f7d\u63a5\u53e3\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002\u63a8\u8350 60\u3002" - }, - "list_concurrent": { - "title": "\u67e5\u8be2\u5e76\u53d1", - "desc": "\u8d44\u4ea7\u67e5\u8be2\u63a5\u53e3\u7684\u6700\u5927\u5e76\u53d1\u6570\u3002\u63a8\u8350 10\u3002" - }, - "list_timeout": { - "title": "\u67e5\u8be2\u8d85\u65f6", - "desc": "\u8d44\u4ea7\u67e5\u8be2\u63a5\u53e3\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002\u63a8\u8350 60\u3002" - }, - "list_batch_size": { - "title": "\u67e5\u8be2\u6279\u6b21\u5927\u5c0f", - "desc": "\u5355\u6b21\u67e5\u8be2\u53ef\u5904\u7406\u7684 Token \u6570\u91cf\u3002\u63a8\u8350 10\u3002" - }, - "delete_concurrent": { - "title": "\u5220\u9664\u5e76\u53d1", - "desc": "\u8d44\u4ea7\u5220\u9664\u63a5\u53e3\u7684\u6700\u5927\u5e76\u53d1\u6570\u3002\u63a8\u8350 10\u3002" - }, - "delete_timeout": { - "title": "\u5220\u9664\u8d85\u65f6", - "desc": "\u8d44\u4ea7\u5220\u9664\u63a5\u53e3\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002\u63a8\u8350 60\u3002" - }, - "delete_batch_size": { - "title": "\u5220\u9664\u6279\u6b21\u5927\u5c0f", - "desc": "\u5355\u6b21\u5220\u9664\u53ef\u5904\u7406\u7684 Token \u6570\u91cf\u3002\u63a8\u8350 10\u3002" - } - }, - "voice": { - "timeout": { - "title": "\u8bf7\u6c42\u8d85\u65f6", - "desc": "Voice \u8bf7\u6c42\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002" - } - }, - "token": { - "auto_refresh": { - "title": "\u81ea\u52a8\u5237\u65b0", - "desc": "\u662f\u5426\u5f00\u542f Token \u81ea\u52a8\u5237\u65b0\u673a\u5236\u3002" - }, - "refresh_interval_hours": { - "title": "\u5237\u65b0\u95f4\u9694", - "desc": "\u666e\u901a Token \u5237\u65b0\u7684\u65f6\u95f4\u95f4\u9694\uff08\u5c0f\u65f6\uff09\u3002" - }, - "super_refresh_interval_hours": { - "title": "Super \u5237\u65b0\u95f4\u9694", - "desc": "Super Token \u5237\u65b0\u7684\u65f6\u95f4\u95f4\u9694\uff08\u5c0f\u65f6\uff09\u3002" - }, - "fail_threshold": { - "title": "\u5931\u8d25\u9608\u503c", - "desc": "\u5355\u4e2a Token \u8fde\u7eed\u5931\u8d25\u591a\u5c11\u6b21\u540e\u88ab\u6807\u8bb0\u4e3a\u4e0d\u53ef\u7528\u3002" - }, - "save_delay_ms": { - "title": "\u4fdd\u5b58\u5ef6\u8fdf", - "desc": "Token \u53d8\u66f4\u5408\u5e76\u5199\u5165\u7684\u5ef6\u8fdf\uff08\u6beb\u79d2\uff09\u3002" - }, - "usage_flush_interval_sec": { - "title": "\u7528\u91cf\u843d\u5e93\u95f4\u9694", - "desc": "\u7528\u91cf\u7c7b\u5b57\u6bb5\u5199\u5165\u6570\u636e\u5e93\u7684\u6700\u5c0f\u95f4\u9694\uff08\u79d2\uff09\u3002" - }, - "reload_interval_sec": { - "title": "\u540c\u6b65\u95f4\u9694", - "desc": "\u591a worker \u573a\u666f\u4e0b Token \u72b6\u6001\u5237\u65b0\u95f4\u9694\uff08\u79d2\uff09\u3002" - } - }, - "cache": { - "enable_auto_clean": { - "title": "\u81ea\u52a8\u6e05\u7406", - "desc": "\u662f\u5426\u542f\u7528\u7f13\u5b58\u81ea\u52a8\u6e05\u7406\uff0c\u5f00\u542f\u540e\u6309\u4e0a\u9650\u81ea\u52a8\u56de\u6536\u3002" - }, - "limit_mb": { - "title": "\u6e05\u7406\u9608\u503c", - "desc": "\u7f13\u5b58\u5927\u5c0f\u9608\u503c\uff08MB\uff09\uff0c\u8d85\u8fc7\u9608\u503c\u4f1a\u89e6\u53d1\u6e05\u7406\u3002" - } - }, - "nsfw": { - "concurrent": { - "title": "\u5e76\u53d1\u4e0a\u9650", - "desc": "\u6279\u91cf\u5f00\u542f NSFW \u6a21\u5f0f\u65f6\u7684\u5e76\u53d1\u8bf7\u6c42\u4e0a\u9650\u3002\u63a8\u8350 10\u3002" - }, - "batch_size": { - "title": "\u6279\u6b21\u5927\u5c0f", - "desc": "\u6279\u91cf\u5f00\u542f NSFW \u6a21\u5f0f\u7684\u5355\u6279\u5904\u7406\u6570\u91cf\u3002\u63a8\u8350 50\u3002" - }, - "timeout": { - "title": "\u8bf7\u6c42\u8d85\u65f6", - "desc": "NSFW \u5f00\u542f\u76f8\u5173\u8bf7\u6c42\u7684\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002\u63a8\u8350 60\u3002" - } - }, - "usage": { - "concurrent": { - "title": "\u5e76\u53d1\u4e0a\u9650", - "desc": "\u6279\u91cf\u5237\u65b0\u7528\u91cf\u65f6\u7684\u5e76\u53d1\u8bf7\u6c42\u4e0a\u9650\u3002\u63a8\u8350 10\u3002" - }, - "batch_size": { - "title": "\u6279\u6b21\u5927\u5c0f", - "desc": "\u6279\u91cf\u5237\u65b0\u7528\u91cf\u7684\u5355\u6279\u5904\u7406\u6570\u91cf\u3002\u63a8\u8350 50\u3002" - }, - "timeout": { - "title": "\u8bf7\u6c42\u8d85\u65f6", - "desc": "\u7528\u91cf\u67e5\u8be2\u63a5\u53e3\u7684\u8d85\u65f6\u65f6\u95f4\uff08\u79d2\uff09\u3002\u63a8\u8350 60\u3002" - } - } + "fields": { + "app": { + "api_key": { + "title": "API 密钥", + "desc": "调用 Grok2API 服务的 Token(可选,支持多个,逗号分隔或数组)。" + }, + "app_key": { + "title": "后台密码", + "desc": "登录 Grok2API 管理后台的密码(必填)。" + }, + "function_enabled": { + "title": "启用功能玩法", + "desc": "是否启用功能玩法入口(关闭则功能玩法页面不可访问)。" + }, + "function_key": { + "title": "Function 密码", + "desc": "功能玩法页面的访问密码(可选)。" + }, + "app_url": { + "title": "应用地址", + "desc": "当前 Grok2API 服务的外部访问 URL,用于文件链接访问。" + }, + "image_format": { + "title": "图片格式", + "desc": "默认生成的图片格式(url 或 base64)。" + }, + "video_format": { + "title": "视频格式", + "desc": "默认生成的视频格式(html 或 url,url 为处理后的链接)。" + }, + "temporary": { + "title": "临时对话", + "desc": "是否默认启用临时对话模式。" + }, + "disable_memory": { + "title": "禁用记忆", + "desc": "是否默认禁用 Grok 记忆功能。" + }, + "stream": { + "title": "流式响应", + "desc": "是否默认启用流式输出。" + }, + "thinking": { + "title": "思维链", + "desc": "是否默认启用思维链输出。" + }, + "dynamic_statsig": { + "title": "动态指纹", + "desc": "是否默认启用动态生成 Statsig 指纹。" + }, + "custom_instruction": { + "title": "自定义指令", + "desc": "多行文本,会透传为 Grok 请求参数 customPersonality。" + }, + "filter_tags": { + "title": "过滤标签", + "desc": "设置自动过滤 Grok 响应中的特殊标签。" } + }, + "proxy": { + "base_proxy_url": { + "title": "基础代理 URL", + "desc": "代理请求到 Grok 官网的基础服务地址。" + }, + "asset_proxy_url": { + "title": "资源代理 URL", + "desc": "代理请求到 Grok 官网的静态资源(图片/视频)地址。" + }, + "skip_proxy_ssl_verify": { + "title": "跳过代理 SSL 校验", + "desc": "代理使用自签名证书时启用(仅放行代理证书校验)。" + }, + "enabled": { + "title": "启用 CF 自动刷新", + "desc": "启用后将通过 FlareSolverr 自动获取 cf_clearance。" + }, + "flaresolverr_url": { + "title": "FlareSolverr 地址", + "desc": "FlareSolverr 服务的 HTTP 地址(如 http://flaresolverr:8191)。" + }, + "refresh_interval": { + "title": "刷新间隔(秒)", + "desc": "自动刷新 cf_clearance 的时间间隔,建议不低于 300 秒。" + }, + "timeout": { + "title": "挑战超时(秒)", + "desc": "等待 FlareSolverr 解决 CF 挑战的最大时间。" + }, + "cf_clearance": { + "title": "CF Clearance", + "desc": "Cloudflare Clearance Cookie,用于绕过反爬虫验证。启用自动刷新时由系统自动管理。" + }, + "browser": { + "title": "浏览器指纹", + "desc": "curl_cffi 浏览器指纹标识(如 chrome136)。启用自动刷新时由系统自动管理。" + }, + "user_agent": { + "title": "User-Agent", + "desc": "HTTP 请求的 User-Agent 字符串。启用自动刷新时由系统自动管理。" + } + }, + "retry": { + "max_retry": { + "title": "最大重试次数", + "desc": "请求 Grok 服务失败时的最大重试次数。" + }, + "retry_status_codes": { + "title": "重试状态码", + "desc": "触发重试的 HTTP 状态码列表。" + }, + "reset_session_status_codes": { + "title": "重建状态码", + "desc": "触发重建 session 的 HTTP 状态码列表(用于轮换代理)。" + }, + "retry_backoff_base": { + "title": "退避基数", + "desc": "重试退避的基础延迟(秒)。" + }, + "retry_backoff_factor": { + "title": "退避倍率", + "desc": "重试退避的指数放大系数。" + }, + "retry_backoff_max": { + "title": "退避上限", + "desc": "单次重试等待的最大延迟(秒)。" + }, + "retry_budget": { + "title": "退避预算", + "desc": "单次请求的最大重试总耗时(秒)。" + } + }, + "chat": { + "concurrent": { + "title": "并发上限", + "desc": "Reverse 接口并发上限。" + }, + "timeout": { + "title": "请求超时", + "desc": "Reverse 接口超时时间(秒)。" + }, + "stream_timeout": { + "title": "流空闲超时", + "desc": "流式空闲超时时间(秒)。" + } + }, + "video": { + "concurrent": { + "title": "并发上限", + "desc": "Reverse 接口并发上限。" + }, + "timeout": { + "title": "请求超时", + "desc": "Reverse 接口超时时间(秒)。" + }, + "stream_timeout": { + "title": "流空闲超时", + "desc": "流式空闲超时时间(秒)。" + } + }, + "image": { + "timeout": { + "title": "请求超时", + "desc": "WebSocket 请求超时时间(秒)。" + }, + "stream_timeout": { + "title": "流空闲超时", + "desc": "WebSocket 流式空闲超时时间(秒)。" + }, + "final_timeout": { + "title": "最终图超时", + "desc": "收到中等图后等待最终图的超时秒数。" + }, + "blocked_grace_seconds": { + "title": "审查宽限秒数", + "desc": "收到中等图后,判定疑似被审查的宽限秒数(默认 10 秒,可自定义)。" + }, + "nsfw": { + "title": "NSFW 模式", + "desc": "WebSocket 请求是否启用 NSFW。" + }, + "medium_min_bytes": { + "title": "中等图最小字节", + "desc": "判定中等质量图的最小字节数。" + }, + "final_min_bytes": { + "title": "最终图最小字节", + "desc": "判定最终图的最小字节数(通常 JPG > 100KB)。" + }, + "blocked_parallel_enabled": { + "title": "启用并行补偿", + "desc": "疑似审查/拦截时,是否启用并行补偿生成。" + }, + "blocked_parallel_attempts": { + "title": "拦截补偿并发次数", + "desc": "疑似审查/拦截导致无最终图时,自动并行补偿生成次数。" + } + }, + "imagine_fast": { + "n": { + "title": "生成数量", + "desc": "仅用于 grok-imagine-1.0-fast 的服务端统一生成数量(1-10)。" + }, + "size": { + "title": "图片尺寸", + "desc": "仅用于 grok-imagine-1.0-fast 的服务端统一尺寸。" + }, + "response_format": { + "title": "响应格式", + "desc": "仅用于 grok-imagine-1.0-fast 的服务端统一返回格式。" + } + }, + "asset": { + "upload_concurrent": { + "title": "上传并发", + "desc": "上传接口的最大并发数。推荐 30。" + }, + "upload_timeout": { + "title": "上传超时", + "desc": "上传接口超时时间(秒)。推荐 60。" + }, + "download_concurrent": { + "title": "下载并发", + "desc": "下载接口的最大并发数。推荐 30。" + }, + "download_timeout": { + "title": "下载超时", + "desc": "下载接口超时时间(秒)。推荐 60。" + }, + "list_concurrent": { + "title": "查询并发", + "desc": "资产查询接口的最大并发数。推荐 10。" + }, + "list_timeout": { + "title": "查询超时", + "desc": "资产查询接口超时时间(秒)。推荐 60。" + }, + "list_batch_size": { + "title": "查询批次大小", + "desc": "单次查询可处理的 Token 数量。推荐 10。" + }, + "delete_concurrent": { + "title": "删除并发", + "desc": "资产删除接口的最大并发数。推荐 10。" + }, + "delete_timeout": { + "title": "删除超时", + "desc": "资产删除接口超时时间(秒)。推荐 60。" + }, + "delete_batch_size": { + "title": "删除批次大小", + "desc": "单次删除可处理的 Token 数量。推荐 10。" + } + }, + "voice": { + "timeout": { + "title": "请求超时", + "desc": "Voice 请求超时时间(秒)。" + } + }, + "token": { + "auto_refresh": { + "title": "自动刷新", + "desc": "是否开启 Token 自动刷新机制。" + }, + "refresh_interval_hours": { + "title": "刷新间隔", + "desc": "普通 Token 刷新的时间间隔(小时)。" + }, + "super_refresh_interval_hours": { + "title": "Super 刷新间隔", + "desc": "Super Token 刷新的时间间隔(小时)。" + }, + "fail_threshold": { + "title": "失败阈值", + "desc": "单个 Token 连续失败多少次后被标记为不可用。" + }, + "save_delay_ms": { + "title": "保存延迟", + "desc": "Token 变更合并写入的延迟(毫秒)。" + }, + "usage_flush_interval_sec": { + "title": "用量落库间隔", + "desc": "用量类字段写入数据库的最小间隔(秒)。" + }, + "reload_interval_sec": { + "title": "同步间隔", + "desc": "多 worker 场景下 Token 状态刷新间隔(秒)。" + } + }, + "cache": { + "enable_auto_clean": { + "title": "自动清理", + "desc": "是否启用缓存自动清理,开启后按上限自动回收。" + }, + "limit_mb": { + "title": "清理阈值", + "desc": "缓存大小阈值(MB),超过阈值会触发清理。" + } + }, + "nsfw": { + "concurrent": { + "title": "并发上限", + "desc": "批量开启 NSFW 模式时的并发请求上限。推荐 10。" + }, + "batch_size": { + "title": "批次大小", + "desc": "批量开启 NSFW 模式的单批处理数量。推荐 50。" + }, + "timeout": { + "title": "请求超时", + "desc": "NSFW 开启相关请求的超时时间(秒)。推荐 60。" + } + }, + "usage": { + "concurrent": { + "title": "并发上限", + "desc": "批量刷新用量时的并发请求上限。推荐 10。" + }, + "batch_size": { + "title": "批次大小", + "desc": "批量刷新用量的单批处理数量。推荐 50。" + }, + "timeout": { + "title": "请求超时", + "desc": "用量查询接口的超时时间(秒)。推荐 60。" + } + } + } + }, + "logs": { + "pageTitle": "Grok2API - 日志查看", + "title": "日志查看", + "subtitle": "按文件、级别和关键词快速筛选,并以更易读的方式查看结构化日志。", + "filesTitle": "日志文件", + "rawTitle": "原始日志", + "empty": "没有匹配的日志记录", + "filters": { + "file": "日志文件", + "level": "级别", + "limit": "条数", + "keyword": "关键词", + "keywordPlaceholder": "traceID / path / 错误关键词", + "apply": "应用筛选", + "reset": "重置", + "excludeAdminRoutes": "仅在此页过滤 `/v1/admin/*`" }, - "logs": { - "pageTitle": "Grok2API - \u65e5\u5fd7\u67e5\u770b", - "title": "\u65e5\u5fd7\u67e5\u770b", - "subtitle": "\u6309\u6587\u4ef6\u3001\u7ea7\u522b\u548c\u5173\u952e\u8bcd\u5feb\u901f\u7b5b\u9009\uff0c\u5e76\u4ee5\u66f4\u6613\u8bfb\u7684\u65b9\u5f0f\u67e5\u770b\u7ed3\u6784\u5316\u65e5\u5fd7\u3002", - "filesTitle": "\u65e5\u5fd7\u6587\u4ef6", - "rawTitle": "\u539f\u59cb\u65e5\u5fd7", - "empty": "\u6ca1\u6709\u5339\u914d\u7684\u65e5\u5fd7\u8bb0\u5f55", - "filters": { - "file": "\u65e5\u5fd7\u6587\u4ef6", - "level": "\u7ea7\u522b", - "limit": "\u6761\u6570", - "keyword": "\u5173\u952e\u8bcd", - "keywordPlaceholder": "traceID / path / \u9519\u8bef\u5173\u952e\u8bcd", - "apply": "\u5e94\u7528\u7b5b\u9009", - "reset": "\u91cd\u7f6e", - "excludeAdminRoutes": "\u4ec5\u5728\u6b64\u9875\u8fc7\u6ee4 `/v1/admin/*`" - }, - "levels": { - "all": "\u5168\u90e8\u7ea7\u522b" - }, - "stats": { - "file": "\u5f53\u524d\u6587\u4ef6", - "entries": "\u5df2\u52a0\u8f7d\u6761\u76ee", - "entriesHint": "\u6309\u5f53\u524d\u7b5b\u9009\u5c55\u793a", - "errorWarn": "\u544a\u8b66 / \u9519\u8bef", - "errorWarnHint": "warning + error", - "size": "\u603b\u5927\u5c0f", - "sizeHint": "\u65e5\u5fd7\u76ee\u5f55\u5360\u7528" - }, - "loadFilesFailed": "\u52a0\u8f7d\u65e5\u5fd7\u6587\u4ef6\u5931\u8d25", - "loadFailed": "\u52a0\u8f7d\u65e5\u5fd7\u5931\u8d25", - "noFiles": "\u6682\u65e0\u65e5\u5fd7\u6587\u4ef6", - "fileCount": "{count} \u4e2a\u65e5\u5fd7\u6587\u4ef6", - "deleteNone": "\u8bf7\u5148\u9009\u62e9\u8981\u6e05\u7406\u7684\u65e5\u5fd7\u6587\u4ef6", - "deleteConfirm": "\u786e\u8ba4\u6e05\u7406 {count} \u4e2a\u65e5\u5fd7\u6587\u4ef6\uff1f", - "deleteFailed": "\u6e05\u7406\u65e5\u5fd7\u5931\u8d25", - "deleteResult": "\u5df2\u6e05\u7406 {deleted} \u4e2a\u65e5\u5fd7\u6587\u4ef6\uff0c\u5931\u8d25 {failed} \u4e2a", - "toggle": { - "expand": "\u5c55\u5f00", - "collapse": "\u6536\u8d77" - }, - "autoRefresh": { - "buttonAria": "\u81ea\u52a8\u5237\u65b0", - "off": "\u81ea\u52a8\u5237\u65b0\uff1a\u5173", - "offShort": "\u5173\u95ed", - "interval": "\u81ea\u52a8\u5237\u65b0\uff1a{seconds}s", - "intervalLabel": "{seconds}s", - "changed": "\u81ea\u52a8\u5237\u65b0\uff1a{label}", - "disabled": "\u81ea\u52a8\u5237\u65b0\u5df2\u5173\u95ed" - }, - "deleteSelected": "\u6e05\u7406\u6240\u9009" - }, - "token": { - "pageTitle": "Grok2API - Token \u7ba1\u7406", - "title": "Token \u5217\u8868", - "subtitle": "\u7ba1\u7406 Grok2API \u7684 Token \u670d\u52a1\u53f7\u6c60\u3002", - "import": "\u5bfc\u5165", - "add": "\u6dfb\u52a0", - "statTotal": "Token \u603b\u6570", - "statActive": "Token \u6b63\u5e38", - "statCooling": "Token \u9650\u6d41", - "statInvalid": "Token \u5931\u6548", - "statChatQuota": "Chat \u5269\u4f59", - "statImageQuota": "Image \u5269\u4f59", - "statVideoQuota": "Video \u5269\u4f59", - "statVideoUnavailable": "\u65e0\u6cd5\u7edf\u8ba1", - "statTotalCalls": "\u603b\u8c03\u7528\u6b21\u6570", - "tabAll": "\u5168\u90e8", - "tabActive": "\u6b63\u5e38", - "tabCooling": "\u9650\u6d41", - "tabExpired": "\u5931\u6548", - "tabNsfw": "\u5df2\u5f00 NSFW", - "tabNoNsfw": "\u672a\u5f00 NSFW", - "statusFilterAria": "Token \u72b6\u6001\u7b5b\u9009", - "tableToken": "Token", - "tableType": "\u7c7b\u578b", - "tableStatus": "\u72b6\u6001", - "tableQuota": "\u989d\u5ea6", - "tableNote": "\u5907\u6ce8", - "tableActions": "\u64cd\u4f5c", - "refreshStatus": "\u5237\u65b0\u72b6\u6001", - "emptyState": "\u6682\u65e0 Token\uff0c\u8bf7\u70b9\u51fb\u53f3\u4e0a\u89d2\u5bfc\u5165\u6216\u6dfb\u52a0\u3002", - "emptyFilterState": "\u5f53\u524d\u7b5b\u9009\u65e0\u7ed3\u679c\uff0c\u8bf7\u5207\u6362\u7b5b\u9009\u6761\u4ef6\u3002", - "selectPage": "\u5168\u9009\u672c\u9875", - "selectAllFiltered": "\u5168\u9009\u5168\u90e8", - "clearSelection": "\u53d6\u6d88\u5168\u9009", - "prevPage": "\u4e0a\u4e00\u9875", - "nextPage": "\u4e0b\u4e00\u9875", - "perPage": "{size} / \u9875", - "pagination": "\u7b2c {current} / {total} \u9875 \u00b7 \u5171 {count} \u6761", - "batchExport": "\u5bfc\u51fa", - "batchRefresh": "\u5237\u65b0", - "batchDisable": "\u7981\u7528", - "batchEnable": "\u542f\u7528", - "batchDelete": "\u5220\u9664", - "importTitle": "\u6279\u91cf\u5bfc\u5165 Token", - "importTargetPool": "\u76ee\u6807 Pool", - "importTokenList": "Token \u5217\u8868 (\u6bcf\u884c\u4e00\u4e2a)", - "importPlaceholder": "\u7c98\u8d34 Token\uff0c\u4e00\u884c\u4e00\u4e2a...", - "startImport": "\u5f00\u59cb\u5bfc\u5165", - "editTitle": "\u7f16\u8f91 Token", - "addTitle": "\u6dfb\u52a0 Token", - "editType": "\u7c7b\u578b", - "editQuota": "\u989d\u5ea6", - "editNote": "\u5907\u6ce8", - "editNotePlaceholder": "\u53ef\u9009\u5907\u6ce8", - "tokenEmpty": "Token \u4e0d\u80fd\u4e3a\u7a7a", - "tokenExists": "Token \u5df2\u5b58\u5728", - "confirmDelete": "\u786e\u5b9a\u8981\u5220\u9664\u6b64 Token \u5417\uff1f", - "confirmDisable": "\u786e\u5b9a\u8981\u7981\u7528\u8be5 Token \u5417\uff1f\n{token}", - "confirmEnable": "\u786e\u5b9a\u8981\u542f\u7528\u8be5 Token \u5417\uff1f\n{token}", - "confirmBatchDelete": "\u786e\u5b9a\u8981\u5220\u9664\u9009\u4e2d\u7684 {count} \u4e2a Token \u5417\uff1f", - "confirmBatchDisable": "\u786e\u5b9a\u8981\u7981\u7528\u9009\u4e2d\u7684 {count} \u4e2a Token \u5417\uff1f", - "confirmBatchEnable": "\u786e\u5b9a\u8981\u542f\u7528\u9009\u4e2d\u7684 {count} \u4e2a Token \u5417\uff1f", - "disableToken": "\u7981\u7528", - "enableToken": "\u542f\u7528", - "disableDone": "Token \u5df2\u7981\u7528", - "enableDone": "Token \u5df2\u542f\u7528", - "batchDisableDone": "\u6279\u91cf\u7981\u7528\u5b8c\u6210", - "batchEnableDone": "\u6279\u91cf\u542f\u7528\u5b8c\u6210", - "noTokenToDisable": "\u9009\u4e2d\u7684 Token \u5df2\u5168\u90e8\u662f\u7981\u7528\u72b6\u6001", - "noTokenToEnable": "\u9009\u4e2d\u7684 Token \u5df2\u5168\u90e8\u662f\u542f\u7528\u72b6\u6001", - "deleteDone": "\u5220\u9664\u5b8c\u6210", - "refreshDone": "\u5237\u65b0\u5b8c\u6210", - "refreshSuccess": "\u5237\u65b0\u6210\u529f", - "refreshFailed": "\u5237\u65b0\u5931\u8d25", - "refreshError": "\u5237\u65b0\u5931\u8d25: {msg}", - "requestError": "\u8bf7\u6c42\u9519\u8bef", - "notValidJson": "\u54cd\u5e94\u4e0d\u662f\u6709\u6548 JSON (HTTP {status})", - "listEmpty": "\u5217\u8868\u4e3a\u7a7a", - "stopRefresh": "\u5df2\u7ec8\u6b62\u5237\u65b0", - "stopDelete": "\u5df2\u7ec8\u6b62\u5220\u9664", - "stopNsfw": "\u5df2\u7ec8\u6b62 NSFW", - "nsfwConfirm": "\u662f\u5426\u4e3a\u9009\u4e2d\u7684 {count} \u4e2a Token \u5f00\u542f NSFW \u6a21\u5f0f\uff1f", - "nsfwEnable": "\u5f00\u542f NSFW", - "nsfwDone": "NSFW \u5f00\u542f\u5b8c\u6210", - "nsfwResult": "NSFW \u5f00\u542f\u5b8c\u6210\uff1a\u6210\u529f {ok}\uff0c\u5931\u8d25 {fail}", - "nsfwFailed": "\u5f00\u542f\u5931\u8d25: {msg}", - "emptyResponse": "\u7a7a\u54cd\u5e94 (HTTP {status})" - }, - "cache": { - "pageTitle": "Grok2API - \u7f13\u5b58\u7ba1\u7406", - "title": "\u7f13\u5b58\u7ba1\u7406", - "subtitle": "\u7ba1\u7406\u672c\u5730\u8d44\u6e90\u4e0e\u5728\u7ebf\u8d44\u4ea7\u7f13\u5b58\u3002", - "localImages": "\u672c\u5730\u56fe\u7247", - "localVideos": "\u672c\u5730\u89c6\u9891", - "onlineAssets": "\u5728\u7ebf\u8d44\u4ea7", - "tableFile": "\u6587\u4ef6", - "tableSize": "\u5927\u5c0f", - "tableTime": "\u65f6\u95f4", - "tableActions": "\u64cd\u4f5c", - "tableToken": "Token", - "tableType": "\u7c7b\u578b", - "tableAssetCount": "\u8d44\u4ea7\u6570", - "tableLastClear": "\u4e0a\u6b21\u6e05\u7a7a\u65f6\u95f4", - "notLoaded": "\u672a\u52a0\u8f7d", - "noAccounts": "\u6682\u65e0\u53ef\u7528\u8d26\u53f7", - "noFiles": "\u6682\u65e0\u6587\u4ef6", - "noTokenAvailable": "\u65e0\u53ef\u7528 Token", - "cannotConnect": "\u65e0\u6cd5\u8fde\u63a5", - "lastClear": "\u4e0a\u6b21\u6e05\u7a7a\uff1a{time}", - "clear": "\u6e05\u7a7a", - "clean": "\u6e05\u7406", - "confirmClearImage": "\u786e\u5b9a\u8981\u6e05\u7a7a\u672c\u5730\u56fe\u7247\u7f13\u5b58\u5417\uff1f", - "confirmClearVideo": "\u786e\u5b9a\u8981\u6e05\u7a7a\u672c\u5730\u89c6\u9891\u7f13\u5b58\u5417\uff1f", - "clearSuccess": "\u6e05\u7406\u6210\u529f\uff0c\u91ca\u653e {size} MB", - "clearFailed": "\u6e05\u7406\u5931\u8d25", - "confirmDeleteFile": "\u786e\u5b9a\u8981\u5220\u9664\u8be5\u6587\u4ef6\u5417\uff1f", - "confirmBatchDeleteFiles": "\u786e\u5b9a\u8981\u5220\u9664\u9009\u4e2d\u7684 {count} \u4e2a\u6587\u4ef6\u5417\uff1f", - "noFilesSelected": "\u672a\u9009\u62e9\u6587\u4ef6", - "deletedFiles": "\u5df2\u5220\u9664 {count} \u4e2a\u6587\u4ef6", - "deleteResult": "\u5220\u9664\u5b8c\u6210\uff1a\u6210\u529f {success}\uff0c\u5931\u8d25 {failed}", - "loadStatsFailed": "\u52a0\u8f7d\u7edf\u8ba1\u5931\u8d25", - "selectAccountsToLoad": "\u8bf7\u9009\u62e9\u8981\u52a0\u8f7d\u7684\u8d26\u53f7", - "selectAccountsToClear": "\u8bf7\u9009\u62e9\u8981\u6e05\u7a7a\u7684\u8d26\u53f7", - "confirmClearAccounts": "\u786e\u5b9a\u8981\u6e05\u7a7a\u9009\u4e2d\u7684 {count} \u4e2a\u8d26\u53f7\u5728\u7ebf\u8d44\u4ea7\u5417\uff1f", - "confirmClearAccount": "\u786e\u5b9a\u8981\u6e05\u7a7a\u8d26\u53f7 {label} \u7684\u5728\u7ebf\u8d44\u4ea7\u5417\uff1f", - "batchCleanInProgress": "\u6b63\u5728\u6279\u91cf\u6e05\u7406\u5728\u7ebf\u8d44\u4ea7\uff0c\u8bf7\u7a0d\u5019...", - "cleanInProgress": "\u6b63\u5728\u6e05\u7406\u5728\u7ebf\u8d44\u4ea7\uff0c\u8bf7\u7a0d\u5019...", - "batchCleanDone": "\u6279\u91cf\u6e05\u7406\u5b8c\u6210", - "cleanResult": "\u6e05\u7406\u5b8c\u6210 (\u6210\u529f: {success}, \u5931\u8d25: {failed})", - "cleanFailedEntry": "\u6e05\u7406\u5931\u8d25", - "requestTimeout": "\u8bf7\u6c42\u8d85\u65f6\u6216\u5931\u8d25", - "loadDone": "\u52a0\u8f7d\u5b8c\u6210", - "loadingStatus": "\u52a0\u8f7d\u4e2d", - "loadStopped": "\u5df2\u7ec8\u6b62\u52a0\u8f7d", - "loadFailedMsg": "\u52a0\u8f7d\u5931\u8d25: {msg}", - "stoppedLoadRequests": "\u5df2\u7ec8\u6b62\u5269\u4f59\u52a0\u8f7d\u8bf7\u6c42", - "cleanStopped": "\u5df2\u7ec8\u6b62\u6e05\u7406", - "cleanFailedMsg": "\u6e05\u7406\u5931\u8d25: {msg}", - "stoppedCleanRequests": "\u5df2\u7ec8\u6b62\u5269\u4f59\u6e05\u7406\u8bf7\u6c42" + "levels": { + "all": "全部级别" }, - "chat": { - "pageTitle": "Grok2API - Chat \u804a\u5929", - "title": "Chat \u804a\u5929", - "subtitle": "\u901a\u8fc7 chat \u63a5\u53e3\u4e0e Grok \u8fdb\u884c\u4f1a\u8bdd\u804a\u5929", - "sessions": "\u4f1a\u8bdd", - "newChat": "\u65b0\u589e", - "collapseSidebar": "\u6298\u53e0", - "expandSidebar": "\u5c55\u5f00", - "sessionList": "\u4f1a\u8bdd\u5217\u8868", - "newSession": "\u65b0\u4f1a\u8bdd", - "ready": "\u5c31\u7eea", - "emptyState": "\u8f93\u5165\u4e00\u6761\u6d88\u606f\u5f00\u59cb\u5bf9\u8bdd\u3002", - "uploadFile": "\u4e0a\u4f20\u6587\u4ef6", - "placeholder": "\u8be2\u95ee\u4efb\u4f55\u5185\u5bb9", - "settings": "\u8bbe\u7f6e", - "send": "\u53d1\u9001", - "temperature": "\u6e29\u5ea6", - "topP": "Top P", - "systemPrompt": "System Prompt", - "systemPromptPlaceholder": "\u53ef\u9009\uff0c\u8bbe\u7f6e\u7cfb\u7edf\u63d0\u793a\u8bcd", - "fileLabel": "[\u6587\u4ef6]", - "compositeContent": "[\u590d\u5408\u5185\u5bb9]", - "storageFull": "\u672c\u5730\u5b58\u50a8\u7a7a\u95f4\u4e0d\u8db3\uff0c\u90e8\u5206\u4f1a\u8bdd\u672a\u4fdd\u5b58", - "attachmentNoEdit": "\u9644\u4ef6\u6d88\u606f\u6682\u4e0d\u652f\u6301\u7f16\u8f91", - "saveEdit": "\u4fdd\u5b58", - "saveEditTitle": "\u4fdd\u5b58\u7f16\u8f91", - "cancelEdit": "\u53d6\u6d88", - "cancelEditTitle": "\u53d6\u6d88\u7f16\u8f91", - "contentEmpty": "\u5185\u5bb9\u4e0d\u80fd\u4e3a\u7a7a", - "editMessage": "\u7f16\u8f91", - "editMessageTitle": "\u7f16\u8f91\u6d88\u606f\u5185\u5bb9", - "regenerate": "\u91cd\u65b0\u751f\u6210", - "regenerateTitle": "\u4ece\u6b64\u5904\u91cd\u65b0\u751f\u6210\u56de\u590d", - "retryTitle": "\u91cd\u8bd5\u4e0a\u4e00\u6761\u56de\u7b54", - "editAnswer": "\u7f16\u8f91", - "editAnswerTitle": "\u7f16\u8f91\u56de\u7b54\u5185\u5bb9", - "copyAnswer": "\u590d\u5236", - "copyAnswerTitle": "\u590d\u5236\u56de\u7b54\u5185\u5bb9", - "feedback": "\u53cd\u9988", - "feedbackTitle": "\u53cd\u9988\u5230 Grok2API", - "noContentToCopy": "\u6682\u65e0\u5185\u5bb9\u53ef\u590d\u5236", - "noChatToRetry": "\u6ca1\u6709\u53ef\u91cd\u8bd5\u7684\u5bf9\u8bdd", - "requestFailedCheck": "\u8bf7\u6c42\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u670d\u52a1\u72b6\u6001", - "requestFailedStatus": "\u8bf7\u6c42\u5931\u8d25: {status}", - "clickRetry": "\u70b9\u51fb\u91cd\u8bd5", - "thinking": "\u601d\u8003\u4e2d", - "thinkingSec": "\u601d\u8003 {sec} \u79d2", - "thought": "\u5df2\u601d\u8003", - "thinkLabel": "\u601d\u8003", - "empty": "\uff08\u7a7a\uff09", - "toolWebSearch": "\u7f51\u9875\u641c\u7d22", - "toolImageSearch": "\u56fe\u7247\u641c\u7d22", - "toolAgentThink": "\u601d\u8003\u63a8\u7406", - "toolDefault": "\u5de5\u5177" + "stats": { + "file": "当前文件", + "entries": "已加载条目", + "entriesHint": "按当前筛选展示", + "errorWarn": "告警 / 错误", + "errorWarnHint": "warning + error", + "size": "总大小", + "sizeHint": "日志目录占用" }, - "imagine": { - "pageTitle": "Grok2API - Imagine \u7011\u5e03\u6d41", - "title": "Imagine \u7011\u5e03\u6d41", - "subtitle": "\u901a\u8fc7 WebSocket \u6301\u7eed\u751f\u6210\u56fe\u7247\uff0c\u5b9e\u65f6\u5c55\u793a base64 \u7011\u5e03\u6d41\u3002", - "genSettings": "\u751f\u6210\u8bbe\u7f6e", - "prompt": "\u63d0\u793a\u8bcd", - "promptPlaceholder": "\u63cf\u8ff0\u4f60\u60f3\u8981\u7684\u753b\u9762\uff0c\u4f8b\u5982\uff1a\u672a\u6765\u57ce\u5e02\u9713\u8679\u96e8\u591c\uff0c\u5e7f\u89d2\u6444\u5f71", - "autoFollow": "\u81ea\u52a8\u8ddf\u968f", - "autoFollowDesc": "\u6eda\u52a8\u5230\u6700\u65b0", - "autoSave": "\u81ea\u52a8\u4fdd\u5b58", - "autoSaveDesc": "\u81ea\u52a8\u4fdd\u5b58\u56fe\u7247", - "aspectRatio": "\u5bbd\u9ad8\u6bd4", - "ratio2_3": "2:3 \u7ad6\u56fe", - "ratio1_1": "1:1 \u65b9\u56fe", - "ratio3_2": "3:2 \u6a2a\u56fe", - "ratio16_9": "16:9 \u5bbd\u5c4f", - "ratio9_16": "9:16 \u7ad6\u5c4f", - "concurrent": "\u5e76\u53d1\u6570\u91cf", - "tasks": "{n} \u4e2a\u4efb\u52a1", - "concurrentTask1": "1 \u4e2a\u4efb\u52a1", - "concurrentTask2": "2 \u4e2a\u4efb\u52a1", - "concurrentTask3": "3 \u4e2a\u4efb\u52a1", - "autoFilter": "\u81ea\u52a8\u8fc7\u6ee4", - "autoFilterDesc": "\u8fc7\u6ee4\u4e0d\u8fbe\u6807\u56fe\u7247", - "nsfwLabel": "\u662f\u5426 NSFW", - "nsfwOn": "\u5f00\u542f", - "nsfwOff": "\u5173\u95ed", - "saveLocation": "\u4fdd\u5b58\u4f4d\u7f6e", - "browserDefault": "\u6d4f\u89c8\u5668\u9ed8\u8ba4\u4f4d\u7f6e", - "reverseOrder": "\u53cd\u5411\u65b0\u589e", - "reverseOrderDesc": "\u6700\u65b0\u663e\u793a\u5728\u4e0a\u65b9", - "connectionStatus": "\u8fde\u63a5\u72b6\u6001", - "connectionMode": "\u8fde\u63a5\u6a21\u5f0f", - "connectionModeAria": "\u8fde\u63a5\u6a21\u5f0f", - "imageCount": "\u56fe\u7247\u6570\u91cf", - "activeTasks": "\u6d3b\u8dc3\u4efb\u52a1", - "avgTime": "\u5e73\u5747\u8017\u65f6", - "gallery": "\u7011\u5e03\u6d41", - "waitingTitle": "\u7b49\u5f85\u8fde\u63a5\u4e2d", - "waitingSubtitle": "\u542f\u52a8\u4efb\u52a1\u540e\uff0c\u751f\u6210\u7684\u56fe\u7247\u4f1a\u81ea\u52a8\u6c47\u805a\u5230\u8fd9\u91cc", - "start": "\u5f00\u59cb", - "batch": "\u6279\u91cf", - "batchDownloadTitle": "\u6279\u91cf\u4e0b\u8f7d", - "selectAll": "\u5168\u9009", - "deselectAll": "\u53d6\u6d88\u5168\u9009", - "download": "\u4e0b\u8f7d", - "startedTasks": "\u5df2\u542f\u52a8 {count} \u4e2a\u5e76\u53d1\u4efb\u52a1", - "startedTasksSSE": "\u5df2\u542f\u52a8 {count} \u4e2a\u5e76\u53d1\u4efb\u52a1 (SSE)", - "generatingSSE": "\u751f\u6210\u4e2d (SSE)", - "selectFolder": "\u5df2\u9009\u62e9\u6587\u4ef6\u5939: {name}", - "selectFolderFailed": "\u9009\u62e9\u6587\u4ef6\u5939\u5931\u8d25", - "noImagesSelected": "\u8bf7\u5148\u9009\u62e9\u8981\u4e0b\u8f7d\u7684\u56fe\u7247", - "jszipFailed": "JSZip \u5e93\u52a0\u8f7d\u5931\u8d25\uff0c\u8bf7\u5237\u65b0\u9875\u9762\u91cd\u8bd5", - "packing": "\u6b63\u5728\u6253\u5305 {count} \u5f20\u56fe\u7247...", - "packingBtn": "\u6253\u5305\u4e2d...", - "packingProgress": "\u6253\u5305\u4e2d... ({done}/{total})", - "noImagesDownloaded": "\u6ca1\u6709\u6210\u529f\u83b7\u53d6\u4efb\u4f55\u56fe\u7247", - "generatingZip": "\u751f\u6210\u538b\u7f29\u5305...", - "packSuccess": "\u6210\u529f\u6253\u5305 {count} \u5f20\u56fe\u7247", - "packFailed": "\u6253\u5305\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5" + "loadFilesFailed": "加载日志文件失败", + "loadFailed": "加载日志失败", + "noFiles": "暂无日志文件", + "fileCount": "{count} 个日志文件", + "deleteNone": "请先选择要清理的日志文件", + "deleteConfirm": "确认清理 {count} 个日志文件?", + "deleteFailed": "清理日志失败", + "deleteResult": "已清理 {deleted} 个日志文件,失败 {failed} 个", + "toggle": { + "expand": "展开", + "collapse": "收起" }, - "video": { - "pageTitle": "Grok2API - Video \u89c6\u9891\u751f\u6210", - "title": "Video \u89c6\u9891\u751f\u6210", - "subtitle": "\u751f\u6210\u77ed\u89c6\u9891\uff0c\u652f\u6301\u53c2\u8003\u56fe\u4e0e\u591a\u79cd\u9884\u8bbe\u98ce\u683c\u3002", - "startGenerate": "\u5f00\u59cb\u751f\u6210", - "genSettings": "\u751f\u6210\u8bbe\u7f6e", - "prompt": "\u63d0\u793a\u8bcd", - "promptPlaceholder": "\u4f8b\u5982\uff1a\u8857\u5934\u9713\u8679\u96e8\u591c\uff0c\u6162\u955c\u5934\uff0c\u80f6\u7247\u8d28\u611f", - "referenceImage": "\u53c2\u8003\u56fe", - "referenceImagePlaceholder": "https://... \u6216 data:image/...", - "aspectRatio": "\u753b\u9762\u6bd4\u4f8b", - "ratio3_2": "3:2 \u6a2a\u6784\u56fe", - "ratio2_3": "2:3 \u7ad6\u6784\u56fe", - "ratio16_9": "16:9 \u5bbd\u5c4f", - "ratio9_16": "9:16 \u7ad6\u5c4f", - "ratio1_1": "1:1 \u65b9\u5f62", - "duration": "\u89c6\u9891\u65f6\u957f", - "seconds": "{n} \u79d2", - "resolution": "\u5206\u8fa8\u7387", - "stylePreset": "\u98ce\u683c\u9884\u8bbe", - "upload": "\u4e0a\u4f20", - "clearImage": "\u6e05\u9664", - "runStatus": "\u8fd0\u884c\u72b6\u6001", - "elapsedTime": "\u8017\u65f6 {sec}s", - "elapsedTimeNone": "\u8017\u65f6 -", - "metaRatio": "\u6bd4\u4f8b", - "metaDuration": "\u65f6\u957f", - "metaResolution": "\u5206\u8fa8\u7387", - "metaPreset": "\u9884\u8bbe", - "videoPreview": "\u89c6\u9891\u9884\u89c8", - "clearPreview": "\u6e05\u7a7a", - "waitingGenerate": "\u7b49\u5f85\u751f\u6210\u89c6\u9891", - "videoTitle": "\u89c6\u9891 {n}", - "open": "\u6253\u5f00", - "generatingPlaceholder": "\u751f\u6210\u4e2d\u2026", - "superResolution": "\u8d85\u5206\u8fa8\u7387", - "superResolutionInProgress": "\u8d85\u5206\u8fa8\u7387\u4e2d", - "alreadyGenerating": "\u5df2\u5728\u751f\u6210\u4e2d", - "referenceConflict": "\u53c2\u8003\u56fe\u53ea\u80fd\u9009\u62e9\u5176\u4e00\uff1aURL/Base64 \u6216 \u672c\u5730\u4e0a\u4f20", - "downloadFailed": "\u4e0b\u8f7d\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u89c6\u9891\u94fe\u63a5\u662f\u5426\u53ef\u8bbf\u95ee", - "sec6": "6 \u79d2", - "sec10": "10 \u79d2", - "sec15": "15 \u79d2" + "autoRefresh": { + "buttonAria": "自动刷新", + "off": "自动刷新:关", + "offShort": "关闭", + "interval": "自动刷新:{seconds}s", + "intervalLabel": "{seconds}s", + "changed": "自动刷新:{label}", + "disabled": "自动刷新已关闭" }, - "voice": { - "pageTitle": "Grok2API - LiveKit \u966a\u804a", - "title": "LiveKit \u966a\u804a", - "subtitle": "LiveKit \u8bed\u97f3\u4f1a\u8bdd\uff0c\u8fde\u63a5 Grok Voice\u3002", - "startSession": "\u5f00\u59cb\u4f1a\u8bdd", - "stopSession": "\u505c\u6b62\u4f1a\u8bdd", - "connectionSettings": "\u8fde\u63a5\u8bbe\u7f6e", - "voiceLabel": "\u58f0\u97f3 (Voice)", - "personalityLabel": "\u4e2a\u6027 (Personality)", - "speedLabel": "\u8bed\u901f", - "settingsHint": "\u63d0\u793a\uff1aVoice / Personality \u4f1a\u5728\u5efa\u7acb\u8fde\u63a5\u524d\u53d1\u9001\u5230\u670d\u52a1\u7aef\u3002", - "sessionLog": "\u4f1a\u8bdd\u65e5\u5fd7", - "clearLog": "\u6e05\u7a7a", - "copyLog": "\u590d\u5236", - "sessionStatus": "\u4f1a\u8bdd\u72b6\u6001", - "audioOutput": "\u97f3\u9891\u8f93\u51fa", - "audioHint": "\u8ba2\u9605\u5230\u7684\u97f3\u8f68\u4f1a\u81ea\u52a8\u63d2\u5165\u6b64\u533a\u57df\u3002", - "livekitSDKError": "\u9519\u8bef: LiveKit SDK \u672a\u80fd\u6b63\u786e\u52a0\u8f7d\uff0c\u8bf7\u5237\u65b0\u9875\u9762\u91cd\u8bd5", - "livekitLoadFailed": "LiveKit SDK \u52a0\u8f7d\u5931\u8d25", - "secureContextBrowser": "\u8bf7\u4f7f\u7528\u6700\u65b0\u7248\u6d4f\u89c8\u5668\u5e76\u5141\u8bb8\u9ea6\u514b\u98ce\u6743\u9650", - "secureContextHTTPS": "\u8bf7\u4f7f\u7528 HTTPS \u6216\u5728\u672c\u673a localhost \u8bbf\u95ee", - "secureContextError": "\u5f53\u524d\u73af\u5883\u4e0d\u652f\u6301\u9ea6\u514b\u98ce\u6743\u9650\uff0c{hint}", - "connectingStatus": "\u6b63\u5728\u8fde\u63a5", - "fetchingToken": "\u6b63\u5728\u83b7\u53d6 Token...", - "fetchTokenFailed": "\u83b7\u53d6 Token \u5931\u8d25: {status}", - "fetchTokenSuccess": "\u83b7\u53d6 Token \u6210\u529f", - "participantConnected": "\u53c2\u4e0e\u8005\u5df2\u8fde\u63a5: {identity}", - "participantDisconnected": "\u53c2\u4e0e\u8005\u5df2\u65ad\u5f00: {identity}", - "trackSubscribed": "\u8ba2\u9605\u97f3\u8f68: {kind}", - "disconnected": "\u5df2\u65ad\u5f00\u8fde\u63a5", - "connectedToServer": "\u5df2\u8fde\u63a5\u5230 LiveKit \u670d\u52a1\u5668", - "inCall": "\u901a\u8bdd\u4e2d", - "openingMic": "\u6b63\u5728\u5f00\u542f\u9ea6\u514b\u98ce...", - "voiceEnabled": "\u8bed\u97f3\u5df2\u5f00\u542f", - "voiceConnected": "\u8bed\u97f3\u8fde\u63a5\u6210\u529f", - "errorPrefix": "\u9519\u8bef: {msg}", - "logCopied": "\u65e5\u5fd7\u5df2\u590d\u5236", - "copyLogFailed": "\u590d\u5236\u5931\u8d25\uff0c\u8bf7\u624b\u52a8\u9009\u62e9" - } + "deleteSelected": "清理所选" + }, + "token": { + "pageTitle": "Grok2API - Token 管理", + "title": "Token 列表", + "subtitle": "管理 Grok2API 的 Token 服务号池。", + "import": "导入", + "add": "添加", + "statTotal": "Token 总数", + "statActive": "Token 正常", + "statCooling": "Token 限流", + "statInvalid": "Token 失效", + "statChatQuota": "Chat 剩余", + "statImageQuota": "Image 剩余", + "statVideoQuota": "Video 剩余", + "statVideoUnavailable": "无法统计", + "statTotalCalls": "总调用次数", + "tabAll": "全部", + "tabActive": "正常", + "tabCooling": "限流", + "tabExpired": "失效", + "tabNsfw": "已开 NSFW", + "tabNoNsfw": "未开 NSFW", + "statusFilterAria": "Token 状态筛选", + "tableToken": "Token", + "tableType": "类型", + "tableStatus": "状态", + "tableQuota": "额度", + "tableNote": "备注", + "tableActions": "操作", + "refreshStatus": "刷新状态", + "emptyState": "暂无 Token,请点击右上角导入或添加。", + "emptyFilterState": "当前筛选无结果,请切换筛选条件。", + "selectPage": "全选本页", + "selectAllFiltered": "全选全部", + "clearSelection": "取消全选", + "prevPage": "上一页", + "nextPage": "下一页", + "perPage": "{size} / 页", + "pagination": "第 {current} / {total} 页 · 共 {count} 条", + "batchExport": "导出", + "batchRefresh": "刷新", + "batchDisable": "禁用", + "batchEnable": "启用", + "batchDelete": "删除", + "importTitle": "批量导入 Token", + "importTargetPool": "目标 Pool", + "importTokenList": "Token 列表 (每行一个)", + "importPlaceholder": "粘贴 Token,一行一个...", + "startImport": "开始导入", + "editTitle": "编辑 Token", + "addTitle": "添加 Token", + "editType": "类型", + "editQuota": "额度", + "editNote": "备注", + "editNotePlaceholder": "可选备注", + "tokenEmpty": "Token 不能为空", + "tokenExists": "Token 已存在", + "confirmDelete": "确定要删除此 Token 吗?", + "confirmDisable": "确定要禁用该 Token 吗?\n{token}", + "confirmEnable": "确定要启用该 Token 吗?\n{token}", + "confirmBatchDelete": "确定要删除选中的 {count} 个 Token 吗?", + "confirmBatchDisable": "确定要禁用选中的 {count} 个 Token 吗?", + "confirmBatchEnable": "确定要启用选中的 {count} 个 Token 吗?", + "disableToken": "禁用", + "enableToken": "启用", + "disableDone": "Token 已禁用", + "enableDone": "Token 已启用", + "batchDisableDone": "批量禁用完成", + "batchEnableDone": "批量启用完成", + "noTokenToDisable": "选中的 Token 已全部是禁用状态", + "noTokenToEnable": "选中的 Token 已全部是启用状态", + "deleteDone": "删除完成", + "refreshDone": "刷新完成", + "refreshSuccess": "刷新成功", + "refreshFailed": "刷新失败", + "refreshError": "刷新失败: {msg}", + "requestError": "请求错误", + "notValidJson": "响应不是有效 JSON (HTTP {status})", + "listEmpty": "列表为空", + "stopRefresh": "已终止刷新", + "stopDelete": "已终止删除", + "stopNsfw": "已终止 NSFW", + "nsfwConfirm": "是否为选中的 {count} 个 Token 开启 NSFW 模式?", + "nsfwEnable": "开启 NSFW", + "nsfwDone": "NSFW 开启完成", + "nsfwResult": "NSFW 开启完成:成功 {ok},失败 {fail}", + "nsfwFailed": "开启失败: {msg}", + "emptyResponse": "空响应 (HTTP {status})" + }, + "cache": { + "pageTitle": "Grok2API - 缓存管理", + "title": "缓存管理", + "subtitle": "管理本地资源与在线资产缓存。", + "localImages": "本地图片", + "localVideos": "本地视频", + "onlineAssets": "在线资产", + "tableFile": "文件", + "tableSize": "大小", + "tableTime": "时间", + "tableActions": "操作", + "tableToken": "Token", + "tableType": "类型", + "tableAssetCount": "资产数", + "tableLastClear": "上次清空时间", + "notLoaded": "未加载", + "noAccounts": "暂无可用账号", + "noFiles": "暂无文件", + "noTokenAvailable": "无可用 Token", + "cannotConnect": "无法连接", + "lastClear": "上次清空:{time}", + "clear": "清空", + "clean": "清理", + "confirmClearImage": "确定要清空本地图片缓存吗?", + "confirmClearVideo": "确定要清空本地视频缓存吗?", + "clearSuccess": "清理成功,释放 {size} MB", + "clearFailed": "清理失败", + "confirmDeleteFile": "确定要删除该文件吗?", + "confirmBatchDeleteFiles": "确定要删除选中的 {count} 个文件吗?", + "noFilesSelected": "未选择文件", + "deletedFiles": "已删除 {count} 个文件", + "deleteResult": "删除完成:成功 {success},失败 {failed}", + "loadStatsFailed": "加载统计失败", + "selectAccountsToLoad": "请选择要加载的账号", + "selectAccountsToClear": "请选择要清空的账号", + "confirmClearAccounts": "确定要清空选中的 {count} 个账号在线资产吗?", + "confirmClearAccount": "确定要清空账号 {label} 的在线资产吗?", + "batchCleanInProgress": "正在批量清理在线资产,请稍候...", + "cleanInProgress": "正在清理在线资产,请稍候...", + "batchCleanDone": "批量清理完成", + "cleanResult": "清理完成 (成功: {success}, 失败: {failed})", + "cleanFailedEntry": "清理失败", + "requestTimeout": "请求超时或失败", + "loadDone": "加载完成", + "loadingStatus": "加载中", + "loadStopped": "已终止加载", + "loadFailedMsg": "加载失败: {msg}", + "stoppedLoadRequests": "已终止剩余加载请求", + "cleanStopped": "已终止清理", + "cleanFailedMsg": "清理失败: {msg}", + "stoppedCleanRequests": "已终止剩余清理请求" + }, + "chat": { + "pageTitle": "Grok2API - Chat 聊天", + "title": "Chat 聊天", + "subtitle": "通过 chat 接口与 Grok 进行会话聊天", + "sessions": "会话", + "newChat": "新增", + "collapseSidebar": "折叠", + "expandSidebar": "展开", + "sessionList": "会话列表", + "newSession": "新会话", + "ready": "就绪", + "emptyState": "输入一条消息开始对话。", + "uploadFile": "上传文件", + "placeholder": "询问任何内容", + "settings": "设置", + "send": "发送", + "temperature": "温度", + "topP": "Top P", + "systemPrompt": "System Prompt", + "systemPromptPlaceholder": "可选,设置系统提示词", + "fileLabel": "[文件]", + "compositeContent": "[复合内容]", + "storageFull": "本地存储空间不足,部分会话未保存", + "attachmentNoEdit": "附件消息暂不支持编辑", + "saveEdit": "保存", + "saveEditTitle": "保存编辑", + "cancelEdit": "取消", + "cancelEditTitle": "取消编辑", + "contentEmpty": "内容不能为空", + "editMessage": "编辑", + "editMessageTitle": "编辑消息内容", + "regenerate": "重新生成", + "regenerateTitle": "从此处重新生成回复", + "retryTitle": "重试上一条回答", + "editAnswer": "编辑", + "editAnswerTitle": "编辑回答内容", + "copyAnswer": "复制", + "copyAnswerTitle": "复制回答内容", + "feedback": "反馈", + "feedbackTitle": "反馈到 Grok2API", + "noContentToCopy": "暂无内容可复制", + "noChatToRetry": "没有可重试的对话", + "requestFailedCheck": "请求失败,请检查服务状态", + "requestFailedStatus": "请求失败: {status}", + "clickRetry": "点击重试", + "thinking": "思考中", + "thinkingSec": "思考 {sec} 秒", + "thought": "已思考", + "thinkLabel": "思考", + "empty": "(空)", + "toolWebSearch": "网页搜索", + "toolImageSearch": "图片搜索", + "toolAgentThink": "思考推理", + "toolDefault": "工具" + }, + "imagine": { + "pageTitle": "Grok2API - Imagine 瀑布流", + "title": "Imagine 瀑布流", + "subtitle": "通过 WebSocket 持续生成图片,实时展示 base64 瀑布流。", + "genSettings": "生成设置", + "prompt": "提示词", + "promptPlaceholder": "描述你想要的画面,例如:未来城市霓虹雨夜,广角摄影", + "autoFollow": "自动跟随", + "autoFollowDesc": "滚动到最新", + "autoSave": "自动保存", + "autoSaveDesc": "自动保存图片", + "aspectRatio": "宽高比", + "ratio2_3": "2:3 竖图", + "ratio1_1": "1:1 方图", + "ratio3_2": "3:2 横图", + "ratio16_9": "16:9 宽屏", + "ratio9_16": "9:16 竖屏", + "concurrent": "并发数量", + "tasks": "{n} 个任务", + "concurrentTask1": "1 个任务", + "concurrentTask2": "2 个任务", + "concurrentTask3": "3 个任务", + "autoFilter": "自动过滤", + "autoFilterDesc": "过滤不达标图片", + "nsfwLabel": "是否 NSFW", + "nsfwOn": "开启", + "nsfwOff": "关闭", + "saveLocation": "保存位置", + "browserDefault": "浏览器默认位置", + "reverseOrder": "反向新增", + "reverseOrderDesc": "最新显示在上方", + "connectionStatus": "连接状态", + "connectionMode": "连接模式", + "connectionModeAria": "连接模式", + "imageCount": "图片数量", + "activeTasks": "活跃任务", + "avgTime": "平均耗时", + "gallery": "瀑布流", + "waitingTitle": "等待连接中", + "waitingSubtitle": "启动任务后,生成的图片会自动汇聚到这里", + "start": "开始", + "batch": "批量", + "batchDownloadTitle": "批量下载", + "selectAll": "全选", + "deselectAll": "取消全选", + "download": "下载", + "startedTasks": "已启动 {count} 个并发任务", + "startedTasksSSE": "已启动 {count} 个并发任务 (SSE)", + "generatingSSE": "生成中 (SSE)", + "selectFolder": "已选择文件夹: {name}", + "selectFolderFailed": "选择文件夹失败", + "noImagesSelected": "请先选择要下载的图片", + "jszipFailed": "JSZip 库加载失败,请刷新页面重试", + "packing": "正在打包 {count} 张图片...", + "packingBtn": "打包中...", + "packingProgress": "打包中... ({done}/{total})", + "noImagesDownloaded": "没有成功获取任何图片", + "generatingZip": "生成压缩包...", + "packSuccess": "成功打包 {count} 张图片", + "packFailed": "打包失败,请重试" + }, + "video": { + "pageTitle": "Grok2API - Video 视频生成", + "title": "Video 视频生成", + "subtitle": "生成短视频,支持参考图与多种预设风格。", + "startGenerate": "开始生成", + "genSettings": "生成设置", + "prompt": "提示词", + "promptPlaceholder": "例如:街头霓虹雨夜,慢镜头,胶片质感", + "referenceImage": "参考图", + "referenceImagePlaceholder": "https://... 或 data:image/...", + "aspectRatio": "画面比例", + "ratio3_2": "3:2 横构图", + "ratio2_3": "2:3 竖构图", + "ratio16_9": "16:9 宽屏", + "ratio9_16": "9:16 竖屏", + "ratio1_1": "1:1 方形", + "duration": "视频时长", + "seconds": "{n} 秒", + "resolution": "分辨率", + "stylePreset": "风格预设", + "upload": "上传", + "clearImage": "清除", + "runStatus": "运行状态", + "elapsedTime": "耗时 {sec}s", + "elapsedTimeNone": "耗时 -", + "metaRatio": "比例", + "metaDuration": "时长", + "metaResolution": "分辨率", + "metaPreset": "预设", + "videoPreview": "视频预览", + "clearPreview": "清空", + "waitingGenerate": "等待生成视频", + "videoTitle": "视频 {n}", + "open": "打开", + "generatingPlaceholder": "生成中…", + "superResolution": "超分辨率", + "superResolutionInProgress": "超分辨率中", + "alreadyGenerating": "已在生成中", + "referenceConflict": "参考图只能选择其一:URL/Base64 或 本地上传", + "downloadFailed": "下载失败,请检查视频链接是否可访问", + "sec6": "6 秒", + "sec10": "10 秒", + "sec15": "15 秒" + }, + "voice": { + "pageTitle": "Grok2API - LiveKit 陪聊", + "title": "LiveKit 陪聊", + "subtitle": "LiveKit 语音会话,连接 Grok Voice。", + "startSession": "开始会话", + "stopSession": "停止会话", + "connectionSettings": "连接设置", + "voiceLabel": "声音 (Voice)", + "personalityLabel": "个性 (Personality)", + "speedLabel": "语速", + "settingsHint": "提示:Voice / Personality 会在建立连接前发送到服务端。", + "sessionLog": "会话日志", + "clearLog": "清空", + "copyLog": "复制", + "sessionStatus": "会话状态", + "audioOutput": "音频输出", + "audioHint": "订阅到的音轨会自动插入此区域。", + "livekitSDKError": "错误: LiveKit SDK 未能正确加载,请刷新页面重试", + "livekitLoadFailed": "LiveKit SDK 加载失败", + "secureContextBrowser": "请使用最新版浏览器并允许麦克风权限", + "secureContextHTTPS": "请使用 HTTPS 或在本机 localhost 访问", + "secureContextError": "当前环境不支持麦克风权限,{hint}", + "connectingStatus": "正在连接", + "fetchingToken": "正在获取 Token...", + "fetchTokenFailed": "获取 Token 失败: {status}", + "fetchTokenSuccess": "获取 Token 成功", + "participantConnected": "参与者已连接: {identity}", + "participantDisconnected": "参与者已断开: {identity}", + "trackSubscribed": "订阅音轨: {kind}", + "disconnected": "已断开连接", + "connectedToServer": "已连接到 LiveKit 服务器", + "inCall": "通话中", + "openingMic": "正在开启麦克风...", + "voiceEnabled": "语音已开启", + "voiceConnected": "语音连接成功", + "errorPrefix": "错误: {msg}", + "logCopied": "日志已复制", + "copyLogFailed": "复制失败,请手动选择" + } }