✨ feat(default): redesign dashboard overview
Refresh the overview page with an actionable Get Started guide, live API request details, real usage sparklines, and OpenAI-inspired dashboard panels. Add collapsible setup state, role-aware quick actions, and localized copy so returning users can focus on platform health.
This commit is contained in:
parent
e8cfb546fa
commit
a7d019e3a9
@ -45,6 +45,7 @@ export function SectionPageLayout(props: SectionPageLayoutProps) {
|
||||
)
|
||||
|
||||
let title: ReactNode = null
|
||||
let description: ReactNode = null
|
||||
let actions: ReactNode = null
|
||||
let content: ReactNode = null
|
||||
let breadcrumb: ReactNode = null
|
||||
@ -53,6 +54,8 @@ export function SectionPageLayout(props: SectionPageLayoutProps) {
|
||||
if (!isValidElement(node)) return
|
||||
const child = node as ReactElement<SlotProps>
|
||||
if (child.type === SectionPageLayoutTitle) title = child.props.children
|
||||
else if (child.type === SectionPageLayoutDescription)
|
||||
description = child.props.children
|
||||
else if (child.type === SectionPageLayoutActions)
|
||||
actions = child.props.children
|
||||
else if (child.type === SectionPageLayoutContent)
|
||||
@ -73,6 +76,11 @@ export function SectionPageLayout(props: SectionPageLayoutProps) {
|
||||
<h2 className='truncate text-base font-bold tracking-tight sm:text-lg'>
|
||||
{title}
|
||||
</h2>
|
||||
{description != null && (
|
||||
<p className='text-muted-foreground mt-0.5 line-clamp-2 text-sm'>
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{actions != null && (
|
||||
<div className='flex shrink-0 flex-wrap items-center gap-2 sm:gap-x-4'>
|
||||
|
||||
@ -183,7 +183,11 @@ export function OtpForm({ className, ...props }: OtpFormProps) {
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button type='submit' className='mt-2 w-full' disabled={!isFormValid || isLoading}>
|
||||
<Button
|
||||
type='submit'
|
||||
className='mt-2 w-full'
|
||||
disabled={!isFormValid || isLoading}
|
||||
>
|
||||
{isLoading ? <Loader2 className='h-4 w-4 animate-spin' /> : null}
|
||||
{t('Verify and Sign In')}
|
||||
</Button>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { Save, Settings2 } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { TimeGranularity } from '@/lib/time'
|
||||
@ -45,9 +45,10 @@ export function ModelsChartPreferences(props: ModelsChartPreferencesProps) {
|
||||
props.preferences
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (open) setDraft(props.preferences)
|
||||
}, [open, props.preferences])
|
||||
const handleOpenChange = (nextOpen: boolean) => {
|
||||
if (nextOpen) setDraft(props.preferences)
|
||||
setOpen(nextOpen)
|
||||
}
|
||||
|
||||
const handleSave = () => {
|
||||
props.onPreferencesChange(draft)
|
||||
@ -55,7 +56,7 @@ export function ModelsChartPreferences(props: ModelsChartPreferencesProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||
<DialogTrigger render={<Button variant='outline' size='sm' />}>
|
||||
<Settings2 className='mr-2 h-4 w-4' />
|
||||
{t('Preferences')}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { Filter, RotateCcw, Calendar, Search } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useAuthStore } from '@/stores/auth-store'
|
||||
@ -73,10 +73,15 @@ export function ModelsFilter(props: ModelsFilterProps) {
|
||||
() => props.preferences.defaultTimeRangeDays
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const resetFiltersFromPreferences = () => {
|
||||
setFilters(buildDefaultDashboardFilters(props.preferences))
|
||||
setSelectedRange(props.preferences.defaultTimeRangeDays)
|
||||
}, [props.preferences])
|
||||
}
|
||||
|
||||
const handleOpenChange = (nextOpen: boolean) => {
|
||||
if (nextOpen) resetFiltersFromPreferences()
|
||||
setOpen(nextOpen)
|
||||
}
|
||||
|
||||
const handleApply = () => {
|
||||
props.onFilterChange(
|
||||
@ -121,7 +126,7 @@ export function ModelsFilter(props: ModelsFilterProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||
<DialogTrigger render={<Button variant='outline' size='sm' />}>
|
||||
<Filter className='mr-2 h-4 w-4' />
|
||||
{t('Filter')}
|
||||
|
||||
@ -44,13 +44,15 @@ export function AnnouncementsPanel() {
|
||||
{t('Announcements')}
|
||||
</span>
|
||||
}
|
||||
description={t('Latest platform updates and notices')}
|
||||
loading={loading}
|
||||
empty={!list.length}
|
||||
emptyMessage={t('No announcements at this time')}
|
||||
height='h-56 sm:h-64'
|
||||
height='h-72'
|
||||
contentClassName='p-0'
|
||||
>
|
||||
<ScrollArea className='h-56 sm:h-64'>
|
||||
<div className='-mx-3 sm:-mx-5'>
|
||||
<ScrollArea className='h-72'>
|
||||
<div>
|
||||
{list.map((item: AnnouncementItem, idx: number) => {
|
||||
const key = item.id ?? `announcement-${idx}`
|
||||
return (
|
||||
@ -65,7 +67,7 @@ export function AnnouncementsPanel() {
|
||||
>
|
||||
<div className='flex items-start gap-2.5'>
|
||||
<AnnouncementStatusDot type={item.type} />
|
||||
<div className='min-w-0 flex-1 space-y-1'>
|
||||
<div className='flex min-w-0 flex-1 flex-col gap-1'>
|
||||
<p className='line-clamp-1 text-sm font-medium'>
|
||||
{getPreviewText(item.content)}
|
||||
</p>
|
||||
|
||||
@ -34,13 +34,15 @@ export function ApiInfoPanel() {
|
||||
{t('API Info')}
|
||||
</span>
|
||||
}
|
||||
description={t('Configured routes and latency checks')}
|
||||
loading={loading}
|
||||
empty={!list.length}
|
||||
emptyMessage={t('No API routes configured')}
|
||||
height='h-56 sm:h-64'
|
||||
height='h-72'
|
||||
contentClassName='p-0'
|
||||
>
|
||||
<ScrollArea className='h-56 sm:h-64'>
|
||||
<div className='-mx-3 sm:-mx-5'>
|
||||
<ScrollArea className='h-72'>
|
||||
<div>
|
||||
{list.map((item: ApiInfoItem, idx: number) => (
|
||||
<div
|
||||
key={item.url}
|
||||
|
||||
@ -24,13 +24,15 @@ export function FAQPanel() {
|
||||
{t('FAQ')}
|
||||
</span>
|
||||
}
|
||||
description={t('Answers for common access and billing questions')}
|
||||
loading={loading}
|
||||
empty={!list.length}
|
||||
emptyMessage={t('No FAQ entries available')}
|
||||
height='h-64 sm:h-80'
|
||||
height='h-80'
|
||||
contentClassName='p-0'
|
||||
>
|
||||
<ScrollArea className='h-64 sm:h-80'>
|
||||
<Accordion className='w-full'>
|
||||
<ScrollArea className='h-80'>
|
||||
<Accordion className='w-full px-4 sm:px-5'>
|
||||
{list.map((item: FAQItem, idx: number) => {
|
||||
const key = item.id ?? `faq-${idx}`
|
||||
const value = `item-${key}`
|
||||
|
||||
722
web/default/src/features/dashboard/components/overview/overview-dashboard.tsx
vendored
Normal file
722
web/default/src/features/dashboard/components/overview/overview-dashboard.tsx
vendored
Normal file
@ -0,0 +1,722 @@
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import {
|
||||
ArrowRight,
|
||||
BookOpen,
|
||||
Check,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
Circle,
|
||||
CreditCard,
|
||||
FileText,
|
||||
KeyRound,
|
||||
ListChecks,
|
||||
Play,
|
||||
RadioTower,
|
||||
ShieldCheck,
|
||||
TerminalSquare,
|
||||
Timer,
|
||||
type LucideIcon,
|
||||
} from 'lucide-react'
|
||||
import { motion, useReducedMotion } from 'motion/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useAuthStore } from '@/stores/auth-store'
|
||||
import { getUserModels } from '@/lib/api'
|
||||
import { MOTION_TRANSITION } from '@/lib/motion'
|
||||
import { ROLE } from '@/lib/roles'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { CopyButton } from '@/components/copy-button'
|
||||
import {
|
||||
CardStaggerContainer,
|
||||
CardStaggerItem,
|
||||
} from '@/components/page-transition'
|
||||
import { fetchTokenKey, getApiKeys } from '@/features/keys/api'
|
||||
import type { ApiKey } from '@/features/keys/types'
|
||||
import { useApiInfo } from '../../hooks/use-status-data'
|
||||
import { AnnouncementsPanel } from './announcements-panel'
|
||||
import { ApiInfoPanel } from './api-info-panel'
|
||||
import { FAQPanel } from './faq-panel'
|
||||
import { SummaryCards } from './summary-cards'
|
||||
import { UptimePanel } from './uptime-panel'
|
||||
|
||||
const SETUP_GUIDE_VISIBILITY_STORAGE_KEY =
|
||||
'dashboard_overview_setup_guide_expanded'
|
||||
|
||||
const SETUP_GUIDE_CODE_PATTERN = [
|
||||
'const request = await client.responses.create({',
|
||||
" model: 'gpt-4.1-mini',",
|
||||
" input: 'Start routing traffic',",
|
||||
'})',
|
||||
'',
|
||||
'if (request.output_text) {',
|
||||
' console.log(request.output_text)',
|
||||
'}',
|
||||
].join('\n')
|
||||
|
||||
type DashboardActionPath =
|
||||
| '/keys'
|
||||
| '/wallet'
|
||||
| '/playground'
|
||||
| '/channels'
|
||||
| '/usage-logs'
|
||||
| '/pricing'
|
||||
|
||||
interface StartStep {
|
||||
title: string
|
||||
description: string
|
||||
to: DashboardActionPath
|
||||
icon: LucideIcon
|
||||
completed: boolean
|
||||
}
|
||||
|
||||
interface QuickAction {
|
||||
title: string
|
||||
description: string
|
||||
to: DashboardActionPath
|
||||
icon: LucideIcon
|
||||
adminOnly?: boolean
|
||||
}
|
||||
|
||||
interface RequestExample {
|
||||
endpoint: string
|
||||
model: string
|
||||
keyName: string
|
||||
displayKey: string
|
||||
curl: string
|
||||
ready: boolean
|
||||
}
|
||||
|
||||
interface HeroSignal {
|
||||
label: string
|
||||
value: string
|
||||
icon: LucideIcon
|
||||
}
|
||||
|
||||
function getSavedSetupGuideExpanded(): boolean | null {
|
||||
if (typeof window === 'undefined') return null
|
||||
const saved = window.localStorage.getItem(SETUP_GUIDE_VISIBILITY_STORAGE_KEY)
|
||||
if (saved === 'expanded') return true
|
||||
if (saved === 'collapsed') return false
|
||||
return null
|
||||
}
|
||||
|
||||
function saveSetupGuideExpanded(expanded: boolean): void {
|
||||
if (typeof window === 'undefined') return
|
||||
window.localStorage.setItem(
|
||||
SETUP_GUIDE_VISIBILITY_STORAGE_KEY,
|
||||
expanded ? 'expanded' : 'collapsed'
|
||||
)
|
||||
}
|
||||
|
||||
function getCurrentOrigin(): string {
|
||||
if (typeof window === 'undefined') return ''
|
||||
return window.location.origin
|
||||
}
|
||||
|
||||
function normalizeEndpoint(sourceUrl?: string): string {
|
||||
const fallback = `${getCurrentOrigin()}/v1/chat/completions`
|
||||
const trimmed = sourceUrl?.trim()
|
||||
if (!trimmed) return fallback
|
||||
|
||||
const withoutTrailingSlash = trimmed.replace(/\/+$/, '')
|
||||
if (withoutTrailingSlash.endsWith('/v1/chat/completions')) {
|
||||
return withoutTrailingSlash
|
||||
}
|
||||
if (withoutTrailingSlash.endsWith('/v1')) {
|
||||
return `${withoutTrailingSlash}/chat/completions`
|
||||
}
|
||||
return `${withoutTrailingSlash}/v1/chat/completions`
|
||||
}
|
||||
|
||||
function getPreferredKey(keys: ApiKey[]): ApiKey | null {
|
||||
return keys.find((item) => item.status === 1) ?? keys[0] ?? null
|
||||
}
|
||||
|
||||
function formatDisplayKey(key?: string): string {
|
||||
if (!key) return 'sk-...'
|
||||
if (key.length <= 14) return key
|
||||
return `${key.slice(0, 7)}...${key.slice(-4)}`
|
||||
}
|
||||
|
||||
function buildCurlCommand(args: {
|
||||
endpoint: string
|
||||
apiKey: string
|
||||
model: string
|
||||
}): string {
|
||||
return [
|
||||
`curl ${args.endpoint} \\`,
|
||||
' -H "Content-Type: application/json" \\',
|
||||
` -H "Authorization: Bearer ${args.apiKey}" \\`,
|
||||
` -d '{"model":"${args.model}","messages":[{"role":"user","content":"Say hello in one sentence."}]}'`,
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
function SetupGuideBackdrop(props: { compact?: boolean }) {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
'pointer-events-none absolute inset-0 bg-[linear-gradient(112deg,oklch(0.97_0.04_250/.92)_0%,oklch(0.95_0.08_315/.82)_38%,oklch(0.96_0.12_92/.78)_74%,oklch(0.94_0.1_132/.62)_100%)] dark:opacity-25',
|
||||
props.compact
|
||||
? '[mask-image:linear-gradient(90deg,black_0%,black_48%,transparent_74%)] opacity-55'
|
||||
: 'opacity-85'
|
||||
)}
|
||||
aria-hidden='true'
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
'pointer-events-none absolute inset-y-0 right-0 hidden overflow-hidden font-mono text-lime-100/75 sm:block dark:text-lime-200/25',
|
||||
props.compact ? 'w-1/2 opacity-45' : 'w-[58%] opacity-75'
|
||||
)}
|
||||
aria-hidden='true'
|
||||
>
|
||||
<pre
|
||||
className={cn(
|
||||
'absolute right-3 [mask-image:linear-gradient(90deg,transparent_0%,black_30%,black_82%,transparent_100%)] text-right tracking-[0.38em] whitespace-pre',
|
||||
props.compact
|
||||
? '-top-6 text-[9px] leading-4'
|
||||
: 'top-1 text-[11px] leading-5'
|
||||
)}
|
||||
>
|
||||
{SETUP_GUIDE_CODE_PATTERN}
|
||||
</pre>
|
||||
</div>
|
||||
<div
|
||||
className='from-background/35 to-background/70 dark:from-background/20 dark:to-background/80 pointer-events-none absolute inset-0 bg-linear-to-b via-transparent'
|
||||
aria-hidden='true'
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function StartStepItem(props: {
|
||||
step: StartStep
|
||||
index: number
|
||||
isLast: boolean
|
||||
}) {
|
||||
const Icon = props.step.icon
|
||||
const StatusIcon = props.step.completed ? Check : Circle
|
||||
|
||||
return (
|
||||
<li className='relative flex gap-3 pb-2.5 last:pb-0'>
|
||||
{!props.isLast && (
|
||||
<span
|
||||
className='bg-border absolute top-9 bottom-0 left-4 w-px'
|
||||
aria-hidden='true'
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
className={cn(
|
||||
'bg-background relative z-10 flex size-8 shrink-0 items-center justify-center rounded-full border shadow-xs',
|
||||
props.step.completed && 'border-emerald-500/30 bg-emerald-500/10'
|
||||
)}
|
||||
>
|
||||
<StatusIcon
|
||||
className={
|
||||
props.step.completed ? 'size-4 text-emerald-600' : 'size-4'
|
||||
}
|
||||
aria-hidden='true'
|
||||
/>
|
||||
</span>
|
||||
|
||||
<Link
|
||||
to={props.step.to}
|
||||
className='bg-background/70 hover:bg-muted/50 focus-visible:ring-ring flex min-w-0 flex-1 items-center justify-between gap-3 rounded-xl border px-3 py-2.5 text-left shadow-xs transition-colors outline-none focus-visible:ring-2'
|
||||
>
|
||||
<span className='flex min-w-0 items-start gap-2.5'>
|
||||
<span className='bg-muted mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-lg'>
|
||||
<Icon className='size-3.5' aria-hidden='true' />
|
||||
</span>
|
||||
<span className='flex min-w-0 flex-col gap-0.5'>
|
||||
<span className='flex items-center gap-2 text-sm font-medium'>
|
||||
<span className='text-muted-foreground font-mono text-xs tabular-nums'>
|
||||
{props.index + 1}.
|
||||
</span>
|
||||
<span className='truncate'>{props.step.title}</span>
|
||||
</span>
|
||||
<span className='text-muted-foreground line-clamp-1 text-xs'>
|
||||
{props.step.description}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<ArrowRight
|
||||
className='text-muted-foreground size-4 shrink-0'
|
||||
aria-hidden='true'
|
||||
/>
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
function RequestPreview(props: {
|
||||
example: RequestExample
|
||||
signals: HeroSignal[]
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
const shouldReduceMotion = useReducedMotion()
|
||||
const previewLines = props.example.curl.split('\n').map((line) => {
|
||||
if (line.includes('Authorization: Bearer')) {
|
||||
return ` -H "Authorization: Bearer ${props.example.displayKey}" \\`
|
||||
}
|
||||
return line
|
||||
})
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={shouldReduceMotion ? false : { opacity: 0, y: 10, scale: 0.98 }}
|
||||
animate={shouldReduceMotion ? undefined : { opacity: 1, y: 0, scale: 1 }}
|
||||
transition={MOTION_TRANSITION.slow}
|
||||
className='bg-background/75 relative overflow-hidden rounded-2xl border p-3 shadow-sm backdrop-blur'
|
||||
>
|
||||
{!shouldReduceMotion && (
|
||||
<motion.div
|
||||
className='via-foreground/30 pointer-events-none absolute inset-x-0 top-0 h-px bg-linear-to-r from-transparent to-transparent'
|
||||
animate={{ x: ['-100%', '100%'] }}
|
||||
transition={{ duration: 3.2, repeat: Infinity, ease: 'easeInOut' }}
|
||||
aria-hidden='true'
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className='flex items-center justify-between gap-3 border-b pb-3'>
|
||||
<div className='flex min-w-0 items-center gap-2'>
|
||||
<span className='bg-muted flex size-8 shrink-0 items-center justify-center rounded-lg'>
|
||||
<TerminalSquare className='size-4' aria-hidden='true' />
|
||||
</span>
|
||||
<div className='min-w-0'>
|
||||
<div className='truncate text-sm font-medium'>
|
||||
{t('First API request')}
|
||||
</div>
|
||||
<div className='text-muted-foreground truncate text-xs'>
|
||||
{props.example.ready
|
||||
? props.example.keyName
|
||||
: t('Create an API key to unlock the real request')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{props.example.ready ? (
|
||||
<CopyButton
|
||||
value={props.example.curl}
|
||||
variant='outline'
|
||||
size='sm'
|
||||
className='h-7 gap-1.5 px-2 text-xs'
|
||||
tooltip={t('Copy ready-to-run curl')}
|
||||
successTooltip={t('Copied!')}
|
||||
aria-label={t('Copy ready-to-run curl')}
|
||||
>
|
||||
{t('Copy')}
|
||||
</CopyButton>
|
||||
) : (
|
||||
<Button size='sm' variant='outline' render={<Link to='/keys' />}>
|
||||
{t('Create API Key')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='bg-foreground/[0.035] my-3 rounded-xl p-3 font-mono text-xs'>
|
||||
<div className='mb-2 flex items-center gap-1.5'>
|
||||
<span className='size-2 rounded-full bg-red-400' />
|
||||
<span className='size-2 rounded-full bg-amber-400' />
|
||||
<span className='size-2 rounded-full bg-emerald-400' />
|
||||
</div>
|
||||
<div className='flex flex-col gap-1 overflow-hidden'>
|
||||
{previewLines.map((line, index) => (
|
||||
<code
|
||||
key={`${line}-${index}`}
|
||||
className='text-muted-foreground truncate'
|
||||
title={line}
|
||||
>
|
||||
{line}
|
||||
</code>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='grid gap-2'>
|
||||
{props.signals.map((signal) => {
|
||||
const Icon = signal.icon
|
||||
|
||||
return (
|
||||
<div
|
||||
key={signal.label}
|
||||
className='bg-muted/40 flex items-center justify-between gap-3 rounded-xl px-3 py-2'
|
||||
>
|
||||
<span className='flex min-w-0 items-center gap-2'>
|
||||
<Icon
|
||||
className='text-muted-foreground size-3.5 shrink-0'
|
||||
aria-hidden='true'
|
||||
/>
|
||||
<span className='truncate text-xs font-medium'>
|
||||
{signal.label}
|
||||
</span>
|
||||
</span>
|
||||
<span className='text-muted-foreground shrink-0 text-xs'>
|
||||
{signal.value}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
function QuickActionItem(props: { action: QuickAction }) {
|
||||
const Icon = props.action.icon
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant='outline'
|
||||
className='h-auto justify-start rounded-xl px-3 py-3 text-left'
|
||||
render={<Link to={props.action.to} />}
|
||||
>
|
||||
<span className='bg-muted flex size-9 shrink-0 items-center justify-center rounded-lg'>
|
||||
<Icon className='size-4' aria-hidden='true' />
|
||||
</span>
|
||||
<span className='flex min-w-0 flex-1 flex-col gap-0.5'>
|
||||
<span className='truncate text-sm font-medium'>
|
||||
{props.action.title}
|
||||
</span>
|
||||
<span className='text-muted-foreground line-clamp-2 text-xs leading-relaxed'>
|
||||
{props.action.description}
|
||||
</span>
|
||||
</span>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
function CompactQuickAction(props: { action: QuickAction }) {
|
||||
const Icon = props.action.icon
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
className='bg-background/70 h-8 min-w-24 gap-1.5 px-2.5'
|
||||
render={<Link to={props.action.to} />}
|
||||
>
|
||||
<Icon data-icon='inline-start' />
|
||||
<span>{props.action.title}</span>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export function OverviewDashboard() {
|
||||
const { t } = useTranslation()
|
||||
const user = useAuthStore((state) => state.auth.user)
|
||||
const { items: apiInfoItems } = useApiInfo()
|
||||
const [manualSetupGuideExpanded, setManualSetupGuideExpanded] = useState<
|
||||
boolean | null
|
||||
>(() => getSavedSetupGuideExpanded())
|
||||
|
||||
const requestCount = Number(user?.request_count ?? 0)
|
||||
const remainQuota = Number(user?.quota ?? 0)
|
||||
const usedQuota = Number(user?.used_quota ?? 0)
|
||||
const isAdmin = Boolean(user?.role && user.role >= ROLE.ADMIN)
|
||||
|
||||
const apiKeysQuery = useQuery({
|
||||
queryKey: ['dashboard', 'overview', 'api-keys'],
|
||||
queryFn: async () => {
|
||||
const result = await getApiKeys({ p: 1, size: 10 })
|
||||
return result.success ? (result.data?.items ?? []) : []
|
||||
},
|
||||
staleTime: 60 * 1000,
|
||||
})
|
||||
|
||||
const modelsQuery = useQuery({
|
||||
queryKey: ['dashboard', 'overview', 'user-models'],
|
||||
queryFn: async () => {
|
||||
const result = await getUserModels()
|
||||
return result.success ? (result.data ?? []) : []
|
||||
},
|
||||
staleTime: 5 * 60 * 1000,
|
||||
})
|
||||
|
||||
const preferredKey = useMemo(
|
||||
() => getPreferredKey(apiKeysQuery.data ?? []),
|
||||
[apiKeysQuery.data]
|
||||
)
|
||||
|
||||
const realKeyQuery = useQuery({
|
||||
queryKey: ['dashboard', 'overview', 'token-key', preferredKey?.id],
|
||||
queryFn: async () => {
|
||||
if (!preferredKey?.id) return ''
|
||||
const result = await fetchTokenKey(preferredKey.id)
|
||||
return result.success && result.data?.key ? `sk-${result.data.key}` : ''
|
||||
},
|
||||
enabled: Boolean(preferredKey?.id),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
})
|
||||
|
||||
const startSteps = useMemo<StartStep[]>(
|
||||
() => [
|
||||
{
|
||||
title: t('Create API Key'),
|
||||
description: t('Create a key for your app or service'),
|
||||
to: '/keys',
|
||||
icon: KeyRound,
|
||||
completed: Boolean(preferredKey),
|
||||
},
|
||||
{
|
||||
title: t('Add credits'),
|
||||
description: t('Keep enough balance before production traffic'),
|
||||
to: '/wallet',
|
||||
icon: CreditCard,
|
||||
completed: remainQuota > 0 || usedQuota > 0,
|
||||
},
|
||||
{
|
||||
title: t('Send a request'),
|
||||
description: t('Verify routing with Playground or your client'),
|
||||
to: '/playground',
|
||||
icon: TerminalSquare,
|
||||
completed: requestCount > 0,
|
||||
},
|
||||
],
|
||||
[preferredKey, remainQuota, requestCount, t, usedQuota]
|
||||
)
|
||||
|
||||
const quickActions = useMemo<QuickAction[]>(
|
||||
() => [
|
||||
{
|
||||
title: t('Playground'),
|
||||
description: t('Test models and prompts from the browser'),
|
||||
to: '/playground',
|
||||
icon: Play,
|
||||
},
|
||||
{
|
||||
title: t('Channels'),
|
||||
description: t('Configure upstream providers and routing.'),
|
||||
to: '/channels',
|
||||
icon: RadioTower,
|
||||
adminOnly: true,
|
||||
},
|
||||
{
|
||||
title: t('Usage Logs'),
|
||||
description: t('Inspect requests, errors, and billing details'),
|
||||
to: '/usage-logs',
|
||||
icon: FileText,
|
||||
},
|
||||
{
|
||||
title: t('Pricing'),
|
||||
description: t('Review model rates before scaling traffic'),
|
||||
to: '/pricing',
|
||||
icon: BookOpen,
|
||||
},
|
||||
],
|
||||
[t]
|
||||
)
|
||||
|
||||
const visibleQuickActions = useMemo(
|
||||
() => quickActions.filter((action) => !action.adminOnly || isAdmin),
|
||||
[isAdmin, quickActions]
|
||||
)
|
||||
|
||||
const heroSignals = useMemo<HeroSignal[]>(
|
||||
() => [
|
||||
{
|
||||
label: t('Route active'),
|
||||
value: apiInfoItems.length > 0 ? t('Online') : t('Current domain'),
|
||||
icon: RadioTower,
|
||||
},
|
||||
{
|
||||
label: t('Auth configured'),
|
||||
value: preferredKey ? t('Secured') : t('Needs API key'),
|
||||
icon: ShieldCheck,
|
||||
},
|
||||
{
|
||||
label: t('Model selected'),
|
||||
value: modelsQuery.data?.[0] ?? t('Loading'),
|
||||
icon: Timer,
|
||||
},
|
||||
],
|
||||
[apiInfoItems.length, modelsQuery.data, preferredKey, t]
|
||||
)
|
||||
|
||||
const requestExample = useMemo<RequestExample>(() => {
|
||||
const endpoint = normalizeEndpoint(apiInfoItems[0]?.url)
|
||||
const model = modelsQuery.data?.[0] ?? 'gpt-4o-mini'
|
||||
const apiKey = realKeyQuery.data ?? ''
|
||||
const keyName = preferredKey?.name ?? t('No API key yet')
|
||||
const ready = Boolean(apiKey && model)
|
||||
|
||||
return {
|
||||
endpoint,
|
||||
model,
|
||||
keyName,
|
||||
displayKey: formatDisplayKey(apiKey),
|
||||
ready,
|
||||
curl: buildCurlCommand({
|
||||
endpoint,
|
||||
apiKey: apiKey || 'sk-...',
|
||||
model,
|
||||
}),
|
||||
}
|
||||
}, [apiInfoItems, modelsQuery.data, preferredKey, realKeyQuery.data, t])
|
||||
|
||||
const completedStepCount = startSteps.filter((step) => step.completed).length
|
||||
const setupComplete = completedStepCount === startSteps.length
|
||||
const setupGuideExpanded = manualSetupGuideExpanded ?? !setupComplete
|
||||
|
||||
const handleSetupGuideToggle = () => {
|
||||
const nextExpanded = !setupGuideExpanded
|
||||
setManualSetupGuideExpanded(nextExpanded)
|
||||
saveSetupGuideExpanded(nextExpanded)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex flex-col gap-4'>
|
||||
{setupGuideExpanded ? (
|
||||
<CardStaggerContainer className='grid items-stretch gap-4 xl:grid-cols-[minmax(0,1fr)_22rem]'>
|
||||
<CardStaggerItem className='bg-card h-full overflow-hidden rounded-2xl border shadow-xs'>
|
||||
<div className='relative h-full overflow-hidden p-4 sm:p-5'>
|
||||
<SetupGuideBackdrop />
|
||||
<div className='relative grid gap-5 lg:grid-cols-[minmax(0,1fr)_21rem]'>
|
||||
<div className='flex min-w-0 flex-col gap-5'>
|
||||
<div className='flex flex-wrap items-start justify-between gap-3'>
|
||||
<div className='flex max-w-2xl flex-col gap-1'>
|
||||
<div className='text-muted-foreground flex items-center gap-2 text-xs font-medium tracking-wider uppercase'>
|
||||
<ListChecks className='size-3.5' aria-hidden='true' />
|
||||
{t('Get started')}
|
||||
</div>
|
||||
<h3 className='text-xl font-semibold tracking-tight sm:text-2xl'>
|
||||
{t('Build on your API gateway in minutes')}
|
||||
</h3>
|
||||
<p className='text-muted-foreground max-w-xl text-sm leading-relaxed'>
|
||||
{t(
|
||||
'A focused home for keys, balance, routing, and service health.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className='flex flex-wrap items-center gap-2'>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
onClick={handleSetupGuideToggle}
|
||||
>
|
||||
<ChevronUp data-icon='inline-start' />
|
||||
{t('Hide setup guide')}
|
||||
</Button>
|
||||
<Button size='sm' render={<Link to='/keys' />}>
|
||||
<KeyRound data-icon='inline-start' />
|
||||
{t('Create API Key')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ol className='bg-background/45 rounded-2xl border p-2 backdrop-blur'>
|
||||
{startSteps.map((step, index) => (
|
||||
<StartStepItem
|
||||
key={step.title}
|
||||
step={step}
|
||||
index={index}
|
||||
isLast={index === startSteps.length - 1}
|
||||
/>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<RequestPreview
|
||||
example={requestExample}
|
||||
signals={heroSignals}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardStaggerItem>
|
||||
|
||||
<CardStaggerItem className='bg-card h-full rounded-2xl border p-4 shadow-xs sm:p-5'>
|
||||
<div className='flex h-full flex-col gap-4'>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<div className='text-muted-foreground text-xs font-medium tracking-wider uppercase'>
|
||||
{t('Recommended actions')}
|
||||
</div>
|
||||
<h3 className='text-lg font-semibold tracking-tight'>
|
||||
{t('Keep the platform ready')}
|
||||
</h3>
|
||||
</div>
|
||||
<div className='grid gap-2'>
|
||||
{visibleQuickActions.map((action) => (
|
||||
<QuickActionItem key={action.title} action={action} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardStaggerItem>
|
||||
</CardStaggerContainer>
|
||||
) : (
|
||||
<CardStaggerContainer>
|
||||
<CardStaggerItem className='bg-card overflow-hidden rounded-2xl border shadow-xs'>
|
||||
<div className='relative overflow-hidden px-4 py-3 sm:px-5'>
|
||||
<SetupGuideBackdrop compact />
|
||||
<div className='relative flex flex-wrap items-center justify-between gap-3'>
|
||||
<div className='flex min-w-0 items-center gap-3'>
|
||||
<span className='bg-background/70 flex size-9 shrink-0 items-center justify-center rounded-xl border shadow-xs'>
|
||||
<Check
|
||||
className='size-4 text-emerald-600'
|
||||
aria-hidden='true'
|
||||
/>
|
||||
</span>
|
||||
<div className='min-w-0'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<h3 className='truncate text-sm font-semibold'>
|
||||
{setupComplete
|
||||
? t('Setup guide complete')
|
||||
: t('Setup guide')}
|
||||
</h3>
|
||||
<span className='text-muted-foreground bg-background/60 rounded-full border px-2 py-0.5 text-xs'>
|
||||
{t('Setup progress: {{completed}}/{{total}}', {
|
||||
completed: completedStepCount,
|
||||
total: startSteps.length,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
<p className='text-muted-foreground line-clamp-1 text-xs'>
|
||||
{setupComplete
|
||||
? t(
|
||||
'Your setup guide is collapsed so usage stays in focus.'
|
||||
)
|
||||
: t('Setup guide is collapsed. Expand it anytime.')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex flex-wrap items-center gap-2'>
|
||||
{visibleQuickActions.map((action) => (
|
||||
<CompactQuickAction key={action.title} action={action} />
|
||||
))}
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
className='bg-background/70 h-8 min-w-28'
|
||||
onClick={handleSetupGuideToggle}
|
||||
>
|
||||
<ChevronDown data-icon='inline-start' />
|
||||
{t('Show setup guide')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardStaggerItem>
|
||||
</CardStaggerContainer>
|
||||
)}
|
||||
|
||||
<SummaryCards />
|
||||
|
||||
<CardStaggerContainer className='grid grid-cols-1 gap-4 xl:grid-cols-[minmax(0,1fr)_22rem]'>
|
||||
<div className='grid min-w-0 grid-cols-1 gap-4 lg:grid-cols-2'>
|
||||
<CardStaggerItem>
|
||||
<ApiInfoPanel />
|
||||
</CardStaggerItem>
|
||||
<CardStaggerItem>
|
||||
<AnnouncementsPanel />
|
||||
</CardStaggerItem>
|
||||
<CardStaggerItem>
|
||||
<FAQPanel />
|
||||
</CardStaggerItem>
|
||||
</div>
|
||||
<CardStaggerItem>
|
||||
<UptimePanel />
|
||||
</CardStaggerItem>
|
||||
</CardStaggerContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,21 +1,98 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import { CreditCard } from 'lucide-react'
|
||||
import { ArrowRight, CreditCard } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useAuthStore } from '@/stores/auth-store'
|
||||
import { getCurrencyLabel, isCurrencyDisplayEnabled } from '@/lib/currency'
|
||||
import { formatNumber, formatQuota } from '@/lib/format'
|
||||
import { computeTimeRange } from '@/lib/time'
|
||||
import { useStatus } from '@/hooks/use-status'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { StaggerContainer, StaggerItem } from '@/components/page-transition'
|
||||
import { getUserQuotaDates } from '@/features/dashboard/api'
|
||||
import { useSummaryCardsConfig } from '@/features/dashboard/hooks/use-dashboard-config'
|
||||
import type { QuotaDataItem } from '@/features/dashboard/types'
|
||||
import { StatCard } from '../ui/stat-card'
|
||||
|
||||
const SUMMARY_SPARKLINE_BUCKETS = 12
|
||||
|
||||
type SummarySparklineKey = 'balance' | 'usage' | 'requests'
|
||||
|
||||
function getBucketIndex(
|
||||
timestamp: number,
|
||||
start: number,
|
||||
end: number,
|
||||
bucketCount: number
|
||||
): number {
|
||||
if (end <= start) return 0
|
||||
const ratio = (timestamp - start) / (end - start)
|
||||
return Math.min(bucketCount - 1, Math.max(0, Math.floor(ratio * bucketCount)))
|
||||
}
|
||||
|
||||
function buildSummarySparklines(
|
||||
data: QuotaDataItem[],
|
||||
currentBalance: number,
|
||||
start: number,
|
||||
end: number
|
||||
): Record<SummarySparklineKey, number[]> {
|
||||
const usage = Array.from({ length: SUMMARY_SPARKLINE_BUCKETS }, () => 0)
|
||||
const requests = Array.from({ length: SUMMARY_SPARKLINE_BUCKETS }, () => 0)
|
||||
|
||||
for (const item of data) {
|
||||
const timestamp = Number(item.created_at) || start
|
||||
const index = getBucketIndex(
|
||||
timestamp,
|
||||
start,
|
||||
end,
|
||||
SUMMARY_SPARKLINE_BUCKETS
|
||||
)
|
||||
usage[index] += Number(item.quota) || 0
|
||||
requests[index] += Number(item.count) || 0
|
||||
}
|
||||
|
||||
let balance = currentBalance
|
||||
const balanceTrend = Array.from(
|
||||
{ length: SUMMARY_SPARKLINE_BUCKETS },
|
||||
() => 0
|
||||
)
|
||||
|
||||
for (let index = SUMMARY_SPARKLINE_BUCKETS - 1; index >= 0; index--) {
|
||||
balanceTrend[index] = Math.max(0, balance)
|
||||
balance += usage[index]
|
||||
}
|
||||
|
||||
return {
|
||||
balance: balanceTrend,
|
||||
usage,
|
||||
requests,
|
||||
}
|
||||
}
|
||||
|
||||
export function SummaryCards() {
|
||||
const { t } = useTranslation()
|
||||
const user = useAuthStore((state) => state.auth.user)
|
||||
const { status, loading } = useStatus()
|
||||
|
||||
const summaryTimeRange = useMemo(() => computeTimeRange(1), [])
|
||||
|
||||
const usageTrendQuery = useQuery({
|
||||
queryKey: [
|
||||
'dashboard',
|
||||
'overview',
|
||||
'summary-sparklines',
|
||||
summaryTimeRange.start_timestamp,
|
||||
summaryTimeRange.end_timestamp,
|
||||
],
|
||||
queryFn: async () =>
|
||||
getUserQuotaDates({
|
||||
start_timestamp: summaryTimeRange.start_timestamp,
|
||||
end_timestamp: summaryTimeRange.end_timestamp,
|
||||
default_time: 'hour',
|
||||
}),
|
||||
staleTime: 60 * 1000,
|
||||
})
|
||||
|
||||
const summaryValues = useMemo(() => {
|
||||
const remainQuota = Number(user?.quota ?? 0)
|
||||
const usedQuota = Number(user?.used_quota ?? 0)
|
||||
@ -39,46 +116,104 @@ export function SummaryCards() {
|
||||
: currencyEnabledFromStore
|
||||
const currencyLabel = currencyEnabled ? getCurrencyLabel() : 'Tokens'
|
||||
|
||||
const sparklineData = useMemo(
|
||||
() =>
|
||||
buildSummarySparklines(
|
||||
usageTrendQuery.data?.data ?? [],
|
||||
Number(user?.quota ?? 0),
|
||||
summaryTimeRange.start_timestamp,
|
||||
summaryTimeRange.end_timestamp
|
||||
),
|
||||
[
|
||||
summaryTimeRange.end_timestamp,
|
||||
summaryTimeRange.start_timestamp,
|
||||
usageTrendQuery.data?.data,
|
||||
user?.quota,
|
||||
]
|
||||
)
|
||||
|
||||
const items = useSummaryCardsConfig({
|
||||
...summaryValues,
|
||||
currencyEnabled,
|
||||
currencyLabel,
|
||||
}).map((config, index) => ({
|
||||
title: config.title,
|
||||
value: config.value,
|
||||
desc: config.description,
|
||||
icon: config.icon,
|
||||
isBalance: index === 0,
|
||||
}))
|
||||
}).map((config, index) => {
|
||||
const tones = ['rose', 'teal', 'gray'] as const
|
||||
|
||||
return {
|
||||
title: config.title,
|
||||
value: config.value,
|
||||
desc: config.description,
|
||||
icon: config.icon,
|
||||
tone: tones[index] ?? 'gray',
|
||||
sparkline:
|
||||
config.key === 'balance'
|
||||
? sparklineData.balance
|
||||
: config.key === 'usage'
|
||||
? sparklineData.usage
|
||||
: sparklineData.requests,
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<div className='overflow-hidden rounded-lg border'>
|
||||
<StaggerContainer className='divide-border/60 grid grid-cols-3 divide-x'>
|
||||
{items.map((it) => (
|
||||
<StaggerItem key={it.title} className='px-3 py-3 sm:px-5 sm:py-4'>
|
||||
<StatCard
|
||||
title={it.title}
|
||||
value={it.value}
|
||||
description={it.desc}
|
||||
icon={it.icon}
|
||||
loading={loading}
|
||||
action={
|
||||
it.isBalance ? (
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
className='hidden h-6 gap-1 px-2 text-xs sm:inline-flex'
|
||||
render={<Link to='/wallet' />}
|
||||
>
|
||||
<CreditCard className='size-3' />
|
||||
{t('Recharge')}
|
||||
</Button>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
</StaggerItem>
|
||||
))}
|
||||
</StaggerContainer>
|
||||
<div className='bg-card overflow-hidden rounded-2xl border shadow-xs'>
|
||||
<div className='grid xl:grid-cols-[minmax(0,1fr)_19rem]'>
|
||||
<div className='flex flex-col gap-3 p-4 sm:p-5'>
|
||||
<div className='flex flex-wrap items-start justify-between gap-3'>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<h3 className='text-base font-semibold'>
|
||||
{t('Usage at a glance')}
|
||||
</h3>
|
||||
<p className='text-muted-foreground text-sm'>
|
||||
{t('Monitor balance, usage, and request volume')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<StaggerContainer className='grid gap-3 md:grid-cols-3'>
|
||||
{items.map((it) => (
|
||||
<StaggerItem
|
||||
key={it.title}
|
||||
className='bg-background/60 rounded-xl border p-3'
|
||||
>
|
||||
<StatCard
|
||||
title={it.title}
|
||||
value={it.value}
|
||||
description={it.desc}
|
||||
icon={it.icon}
|
||||
tone={it.tone}
|
||||
sparkline={it.sparkline}
|
||||
loading={loading}
|
||||
/>
|
||||
</StaggerItem>
|
||||
))}
|
||||
</StaggerContainer>
|
||||
</div>
|
||||
|
||||
<div className='flex flex-col justify-between gap-5 border-t bg-amber-50/80 p-4 sm:p-5 xl:border-t-0 xl:border-l dark:bg-amber-950/20'>
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='text-muted-foreground text-sm'>
|
||||
{t('Credit remaining')}
|
||||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<span className='font-mono text-2xl font-semibold tracking-tight'>
|
||||
{summaryValues.remainDisplay}
|
||||
</span>
|
||||
<CreditCard
|
||||
className='text-muted-foreground size-4'
|
||||
aria-hidden='true'
|
||||
/>
|
||||
</div>
|
||||
<p className='text-muted-foreground text-sm leading-relaxed'>
|
||||
{currencyEnabled
|
||||
? `${t('Displayed in')} ${currencyLabel}`
|
||||
: t('Balance is shown in quota units')}
|
||||
</p>
|
||||
</div>
|
||||
<Button className='justify-between' render={<Link to='/wallet' />}>
|
||||
<span>{t('Recharge')}</span>
|
||||
<ArrowRight data-icon='inline-end' />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -81,10 +81,12 @@ export function UptimePanel() {
|
||||
{t('Uptime')}
|
||||
</span>
|
||||
}
|
||||
description={t('Grouped monitor status from Uptime Kuma')}
|
||||
loading={loading}
|
||||
empty={!groups.length}
|
||||
emptyMessage={t('No uptime monitoring configured')}
|
||||
height='h-64 sm:h-80'
|
||||
height='h-80'
|
||||
contentClassName='p-0'
|
||||
headerActions={
|
||||
<Button
|
||||
variant='ghost'
|
||||
@ -100,8 +102,8 @@ export function UptimePanel() {
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<ScrollArea className='h-64 sm:h-80'>
|
||||
<div className='-mx-3 space-y-0 sm:-mx-5'>
|
||||
<ScrollArea className='h-80'>
|
||||
<div>
|
||||
{groups.map((group, groupIdx) => (
|
||||
<div key={group.categoryName}>
|
||||
<div className='bg-muted/30 border-border/60 border-b px-3 py-2 sm:px-5'>
|
||||
|
||||
@ -1,29 +1,63 @@
|
||||
import { type ReactNode } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
|
||||
interface PanelWrapperProps {
|
||||
title: ReactNode
|
||||
description?: ReactNode
|
||||
loading?: boolean
|
||||
empty?: boolean
|
||||
emptyMessage?: string
|
||||
height?: string
|
||||
className?: string
|
||||
contentClassName?: string
|
||||
headerActions?: ReactNode
|
||||
children?: ReactNode
|
||||
}
|
||||
|
||||
function PanelHeader(props: {
|
||||
title: ReactNode
|
||||
description?: ReactNode
|
||||
actions?: ReactNode
|
||||
}) {
|
||||
const heading = (
|
||||
<div className='flex flex-col gap-1'>
|
||||
<div className='text-sm font-semibold'>{props.title}</div>
|
||||
{props.description != null && (
|
||||
<div className='text-muted-foreground text-xs'>{props.description}</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className='border-b px-4 py-3 sm:px-5'>
|
||||
{props.actions != null ? (
|
||||
<div className='flex items-start justify-between gap-2'>
|
||||
{heading}
|
||||
{props.actions}
|
||||
</div>
|
||||
) : (
|
||||
heading
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function PanelWrapper(props: PanelWrapperProps) {
|
||||
const { t } = useTranslation()
|
||||
const resolvedEmptyMessage = props.emptyMessage ?? t('No data available')
|
||||
const height = props.height ?? 'h-64'
|
||||
const frameClassName = cn(
|
||||
'overflow-hidden rounded-2xl border bg-card shadow-xs',
|
||||
props.className
|
||||
)
|
||||
|
||||
if (props.loading) {
|
||||
return (
|
||||
<div className='overflow-hidden rounded-lg border'>
|
||||
<div className='border-b px-3 py-2.5 sm:px-5 sm:py-3'>
|
||||
<div className='text-sm font-semibold'>{props.title}</div>
|
||||
</div>
|
||||
<div className='p-3 sm:p-5'>
|
||||
<div className={frameClassName}>
|
||||
<PanelHeader title={props.title} description={props.description} />
|
||||
<div className={cn('p-4 sm:p-5', props.contentClassName)}>
|
||||
<Skeleton className={`w-full ${height}`} />
|
||||
</div>
|
||||
</div>
|
||||
@ -32,12 +66,14 @@ export function PanelWrapper(props: PanelWrapperProps) {
|
||||
|
||||
if (props.empty) {
|
||||
return (
|
||||
<div className='overflow-hidden rounded-lg border'>
|
||||
<div className='border-b px-3 py-2.5 sm:px-5 sm:py-3'>
|
||||
<div className='text-sm font-semibold'>{props.title}</div>
|
||||
</div>
|
||||
<div className={frameClassName}>
|
||||
<PanelHeader title={props.title} description={props.description} />
|
||||
<div
|
||||
className={`text-muted-foreground flex items-center justify-center text-sm ${height}`}
|
||||
className={cn(
|
||||
'text-muted-foreground flex items-center justify-center px-4 text-sm',
|
||||
height,
|
||||
props.contentClassName
|
||||
)}
|
||||
>
|
||||
{resolvedEmptyMessage}
|
||||
</div>
|
||||
@ -46,18 +82,15 @@ export function PanelWrapper(props: PanelWrapperProps) {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='overflow-hidden rounded-lg border'>
|
||||
<div className='border-b px-3 py-2.5 sm:px-5 sm:py-3'>
|
||||
{props.headerActions ? (
|
||||
<div className='flex items-center justify-between gap-2'>
|
||||
<div className='text-sm font-semibold'>{props.title}</div>
|
||||
{props.headerActions}
|
||||
</div>
|
||||
) : (
|
||||
<div className='text-sm font-semibold'>{props.title}</div>
|
||||
)}
|
||||
<div className={frameClassName}>
|
||||
<PanelHeader
|
||||
title={props.title}
|
||||
description={props.description}
|
||||
actions={props.headerActions}
|
||||
/>
|
||||
<div className={cn('p-4 sm:p-5', props.contentClassName)}>
|
||||
{props.children}
|
||||
</div>
|
||||
<div className='p-3 sm:p-5'>{props.children}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,53 +1,94 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { type LucideIcon } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
|
||||
type StatCardTone = 'rose' | 'teal' | 'gray'
|
||||
|
||||
interface StatCardProps {
|
||||
title: string
|
||||
value: string | number
|
||||
description: string
|
||||
icon: LucideIcon
|
||||
sparkline?: number[]
|
||||
tone?: StatCardTone
|
||||
loading?: boolean
|
||||
error?: boolean
|
||||
action?: React.ReactNode
|
||||
action?: ReactNode
|
||||
}
|
||||
|
||||
const TONE_CLASSES: Record<StatCardTone, string> = {
|
||||
rose: 'from-rose-500/80 via-rose-300/70 to-rose-200/20 dark:from-rose-400/70 dark:via-rose-500/30 dark:to-rose-500/5',
|
||||
teal: 'from-teal-500/80 via-teal-300/70 to-teal-200/20 dark:from-teal-400/70 dark:via-teal-500/30 dark:to-teal-500/5',
|
||||
gray: 'from-muted-foreground/50 via-muted-foreground/20 to-transparent dark:from-muted-foreground/40 dark:via-muted-foreground/20',
|
||||
}
|
||||
|
||||
function normalizeSparkline(values?: number[]): number[] {
|
||||
if (!values?.length) return []
|
||||
|
||||
const sanitized = values.map((value) => Math.max(0, Number(value) || 0))
|
||||
const max = Math.max(...sanitized)
|
||||
if (max <= 0) return sanitized.map(() => 0)
|
||||
|
||||
return sanitized.map((value) => Math.max(8, (value / max) * 100))
|
||||
}
|
||||
|
||||
export function StatCard(props: StatCardProps) {
|
||||
const Icon = props.icon
|
||||
const tone = props.tone ?? 'gray'
|
||||
const sparkline = normalizeSparkline(props.sparkline)
|
||||
|
||||
return (
|
||||
<div className='group flex flex-col gap-1'>
|
||||
<div className='group flex min-h-32 flex-col justify-between gap-3'>
|
||||
<div className='flex items-start justify-between gap-1'>
|
||||
<div className='text-muted-foreground flex items-center gap-1.5 text-xs font-medium tracking-wider uppercase sm:gap-2'>
|
||||
<Icon className='text-muted-foreground/60 size-3.5 shrink-0' />
|
||||
<div className='text-muted-foreground flex items-center gap-1.5 text-xs font-medium sm:gap-2'>
|
||||
<Icon
|
||||
className='text-muted-foreground/60 size-3.5 shrink-0'
|
||||
aria-hidden='true'
|
||||
/>
|
||||
<span className='line-clamp-2 leading-snug'>{props.title}</span>
|
||||
</div>
|
||||
{props.action && <div className='shrink-0'>{props.action}</div>}
|
||||
</div>
|
||||
|
||||
{props.loading ? (
|
||||
<div className='space-y-1.5'>
|
||||
<div className='flex flex-col gap-1.5'>
|
||||
<Skeleton className='h-7 w-24' />
|
||||
<Skeleton className='h-3.5 w-32' />
|
||||
</div>
|
||||
) : props.error ? (
|
||||
<>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<div className='text-muted-foreground mt-0.5 font-mono text-base font-bold tracking-tight break-all tabular-nums sm:text-2xl'>
|
||||
--
|
||||
</div>
|
||||
<p className='text-muted-foreground/60 hidden text-xs md:block'>
|
||||
<p className='text-muted-foreground/60 text-xs'>
|
||||
{props.description}
|
||||
</p>
|
||||
</>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className='text-foreground mt-0.5 font-mono text-base font-bold tracking-tight break-all tabular-nums sm:text-2xl'>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<div className='text-foreground font-mono text-2xl font-semibold tracking-tight break-all tabular-nums'>
|
||||
{props.value}
|
||||
</div>
|
||||
<p className='text-muted-foreground/60 hidden text-xs md:block'>
|
||||
<p className='text-muted-foreground/60 text-xs leading-relaxed'>
|
||||
{props.description}
|
||||
</p>
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className='flex h-8 items-end gap-1' aria-hidden='true'>
|
||||
{sparkline.map((height, index) => (
|
||||
<span
|
||||
key={`${props.title}-spark-${index}`}
|
||||
className={cn(
|
||||
'flex-1 rounded-t-sm bg-linear-to-t',
|
||||
height <= 0 && 'opacity-20',
|
||||
TONE_CLASSES[tone]
|
||||
)}
|
||||
style={{ height: `${height}%` }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
32
web/default/src/features/dashboard/index.tsx
vendored
32
web/default/src/features/dashboard/index.tsx
vendored
@ -6,18 +6,10 @@ import { ROLE } from '@/lib/roles'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { SectionPageLayout } from '@/components/layout'
|
||||
import {
|
||||
CardStaggerContainer,
|
||||
CardStaggerItem,
|
||||
FadeIn,
|
||||
} from '@/components/page-transition'
|
||||
import { FadeIn } from '@/components/page-transition'
|
||||
import { ModelsChartPreferences } from './components/models/models-chart-preferences'
|
||||
import { ModelsFilter } from './components/models/models-filter-dialog'
|
||||
import { AnnouncementsPanel } from './components/overview/announcements-panel'
|
||||
import { ApiInfoPanel } from './components/overview/api-info-panel'
|
||||
import { FAQPanel } from './components/overview/faq-panel'
|
||||
import { SummaryCards } from './components/overview/summary-cards'
|
||||
import { UptimePanel } from './components/overview/uptime-panel'
|
||||
import { OverviewDashboard } from './components/overview/overview-dashboard'
|
||||
import { DEFAULT_TIME_GRANULARITY } from './constants'
|
||||
import {
|
||||
buildDefaultDashboardFilters,
|
||||
@ -215,25 +207,7 @@ export function Dashboard() {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{activeSection === 'overview' && (
|
||||
<>
|
||||
<SummaryCards />
|
||||
<CardStaggerContainer className='grid grid-cols-1 gap-3 sm:gap-4 lg:grid-cols-2'>
|
||||
<CardStaggerItem>
|
||||
<ApiInfoPanel />
|
||||
</CardStaggerItem>
|
||||
<CardStaggerItem>
|
||||
<AnnouncementsPanel />
|
||||
</CardStaggerItem>
|
||||
<CardStaggerItem>
|
||||
<FAQPanel />
|
||||
</CardStaggerItem>
|
||||
<CardStaggerItem>
|
||||
<UptimePanel />
|
||||
</CardStaggerItem>
|
||||
</CardStaggerContainer>
|
||||
</>
|
||||
)}
|
||||
{activeSection === 'overview' && <OverviewDashboard />}
|
||||
{activeSection === 'models' && (
|
||||
<>
|
||||
<FadeIn>
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import type { TimeGranularity } from '@/lib/time'
|
||||
import { getRollingDateRange } from '@/lib/time'
|
||||
import { getRollingDateRange, type TimeGranularity } from '@/lib/time'
|
||||
import {
|
||||
DASHBOARD_CHART_PREFERENCES_STORAGE_KEY,
|
||||
DEFAULT_DASHBOARD_CHART_PREFERENCES,
|
||||
|
||||
@ -9,8 +9,7 @@ export type ModelPerfBadgeData = {
|
||||
avg_tps: number
|
||||
}
|
||||
|
||||
export interface ModelPerfBadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
export interface ModelPerfBadgeProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
perf: ModelPerfBadgeData | undefined
|
||||
}
|
||||
|
||||
@ -47,7 +46,7 @@ export const ModelPerfBadge = memo(function ModelPerfBadge(
|
||||
<div className='text-muted-foreground/55 text-[10px] leading-4'>
|
||||
{t('Latency short')}
|
||||
</div>
|
||||
<div className='text-muted-foreground/80 whitespace-nowrap font-mono text-xs leading-4'>
|
||||
<div className='text-muted-foreground/80 font-mono text-xs leading-4 whitespace-nowrap'>
|
||||
{avg_latency_ms > 0 ? formatLatency(avg_latency_ms) : '—'}
|
||||
</div>
|
||||
</div>
|
||||
@ -55,7 +54,7 @@ export const ModelPerfBadge = memo(function ModelPerfBadge(
|
||||
<div className='text-muted-foreground/55 truncate text-[10px] leading-4'>
|
||||
{t('Throughput short')}
|
||||
</div>
|
||||
<div className='text-muted-foreground/80 whitespace-nowrap font-mono text-xs leading-4'>
|
||||
<div className='text-muted-foreground/80 font-mono text-xs leading-4 whitespace-nowrap'>
|
||||
{formatCompactThroughput(avg_tps)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { parseCurrencyDisplayType } from '@/lib/currency'
|
||||
import type { BillingSettings } from '../types'
|
||||
import { createSectionRegistry } from '../utils/section-registry'
|
||||
import { CheckinSettingsSection } from '../general/checkin-settings-section'
|
||||
import { PricingSection } from '../general/pricing-section'
|
||||
import { QuotaSettingsSection } from '../general/quota-settings-section'
|
||||
import { PaymentSettingsSection } from '../integrations/payment-settings-section'
|
||||
import { RatioSettingsCard } from '../models/ratio-settings-card'
|
||||
import type { BillingSettings } from '../types'
|
||||
import { createSectionRegistry } from '../utils/section-registry'
|
||||
|
||||
const getModelDefaults = (settings: BillingSettings) => ({
|
||||
ModelPrice: settings.ModelPrice,
|
||||
@ -161,8 +161,7 @@ const BILLING_SECTIONS = [
|
||||
WaffoPancakePrivateKey: settings.WaffoPancakePrivateKey ?? '',
|
||||
WaffoPancakeWebhookPublicKey:
|
||||
settings.WaffoPancakeWebhookPublicKey ?? '',
|
||||
WaffoPancakeWebhookTestKey:
|
||||
settings.WaffoPancakeWebhookTestKey ?? '',
|
||||
WaffoPancakeWebhookTestKey: settings.WaffoPancakeWebhookTestKey ?? '',
|
||||
WaffoPancakeStoreID: settings.WaffoPancakeStoreID ?? '',
|
||||
WaffoPancakeProductID: settings.WaffoPancakeProductID ?? '',
|
||||
WaffoPancakeReturnURL: settings.WaffoPancakeReturnURL ?? '',
|
||||
@ -191,14 +190,15 @@ const BILLING_SECTIONS = [
|
||||
|
||||
export type BillingSectionId = (typeof BILLING_SECTIONS)[number]['id']
|
||||
|
||||
const billingRegistry = createSectionRegistry<BillingSectionId, BillingSettings>(
|
||||
{
|
||||
sections: BILLING_SECTIONS,
|
||||
defaultSection: 'quota',
|
||||
basePath: '/system-settings/billing',
|
||||
urlStyle: 'path',
|
||||
}
|
||||
)
|
||||
const billingRegistry = createSectionRegistry<
|
||||
BillingSectionId,
|
||||
BillingSettings
|
||||
>({
|
||||
sections: BILLING_SECTIONS,
|
||||
defaultSection: 'quota',
|
||||
basePath: '/system-settings/billing',
|
||||
urlStyle: 'path',
|
||||
})
|
||||
|
||||
export const BILLING_SECTION_IDS = billingRegistry.sectionIds
|
||||
export const BILLING_DEFAULT_SECTION = billingRegistry.defaultSection
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import * as z from 'zod'
|
||||
import type { ChangeEvent } from 'react'
|
||||
import * as z from 'zod'
|
||||
import type { Resolver } from 'react-hook-form'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
@ -2,13 +2,13 @@ import { memo, useCallback, useState } from 'react'
|
||||
import { type UseFormReturn } from 'react-hook-form'
|
||||
import { Code2, Eye, HelpCircle } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from '@/components/ui/accordion'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
@ -18,8 +18,6 @@ import {
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
@ -27,6 +25,8 @@ import {
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
} from '@/components/ui/sheet'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { GroupRatioVisualEditor } from './group-ratio-visual-editor'
|
||||
import { GroupSpecialUsableRulesEditor } from './group-special-usable-editor'
|
||||
|
||||
@ -72,11 +72,7 @@ export const GroupRatioForm = memo(function GroupRatioForm({
|
||||
return (
|
||||
<div className='space-y-6'>
|
||||
<div className='flex flex-wrap justify-end gap-2'>
|
||||
<Button
|
||||
variant='outline'
|
||||
size='sm'
|
||||
onClick={() => setGuideOpen(true)}
|
||||
>
|
||||
<Button variant='outline' size='sm' onClick={() => setGuideOpen(true)}>
|
||||
<HelpCircle className='mr-2 h-4 w-4' />
|
||||
{t('Usage guide')}
|
||||
</Button>
|
||||
@ -435,7 +431,9 @@ vip 0.5 ${t('No')} ${t('Assigned by administrator on
|
||||
</AccordionItem>
|
||||
|
||||
<AccordionItem value='usable'>
|
||||
<AccordionTrigger>{t('Special usable group rules')}</AccordionTrigger>
|
||||
<AccordionTrigger>
|
||||
{t('Special usable group rules')}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className='space-y-3'>
|
||||
<p className='text-muted-foreground text-sm leading-6'>
|
||||
{t(
|
||||
|
||||
@ -9,12 +9,12 @@ import {
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from '@/components/ui/collapsible'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@ -830,7 +830,9 @@ function GroupPricingTable({
|
||||
<div>
|
||||
<CardTitle>{t('Pricing groups')}</CardTitle>
|
||||
<CardDescription>
|
||||
{t('Edit billing ratios and user-selectable groups in one table.')}
|
||||
{t(
|
||||
'Edit billing ratios and user-selectable groups in one table.'
|
||||
)}
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Button onClick={addRow} size='sm' className='sm:self-start'>
|
||||
@ -900,11 +902,7 @@ function GroupPricingTable({
|
||||
<Checkbox
|
||||
checked={row.selectable}
|
||||
onCheckedChange={(checked) =>
|
||||
updateRow(
|
||||
row._id,
|
||||
'selectable',
|
||||
checked === true
|
||||
)
|
||||
updateRow(row._id, 'selectable', checked === true)
|
||||
}
|
||||
aria-label={t('User selectable')}
|
||||
/>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { ChannelAffinitySection } from '../general/channel-affinity'
|
||||
import { IoNetDeploymentSettingsSection } from '../integrations/ionet-deployment-settings-section'
|
||||
import type { ModelSettings } from '../types'
|
||||
import { createSectionRegistry } from '../utils/section-registry'
|
||||
import { ClaudeSettingsCard } from './claude-settings-card'
|
||||
import { GeminiSettingsCard } from './gemini-settings-card'
|
||||
import { GlobalSettingsCard } from './global-settings-card'
|
||||
import { GrokSettingsCard } from './grok-settings-card'
|
||||
import { ChannelAffinitySection } from '../general/channel-affinity'
|
||||
import { IoNetDeploymentSettingsSection } from '../integrations/ionet-deployment-settings-section'
|
||||
|
||||
function formatJsonForEditor(value: string, fallback: string) {
|
||||
const raw = (value ?? '').toString().trim()
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import type { OperationsSettings } from '../types'
|
||||
import { createSectionRegistry } from '../utils/section-registry'
|
||||
import { SystemBehaviorSection } from '../general/system-behavior-section'
|
||||
import { EmailSettingsSection } from '../integrations/email-settings-section'
|
||||
import { MonitoringSettingsSection } from '../integrations/monitoring-settings-section'
|
||||
@ -7,6 +5,8 @@ import { WorkerSettingsSection } from '../integrations/worker-settings-section'
|
||||
import { LogSettingsSection } from '../maintenance/log-settings-section'
|
||||
import { PerformanceSection } from '../maintenance/performance-section'
|
||||
import { UpdateCheckerSection } from '../maintenance/update-checker-section'
|
||||
import type { OperationsSettings } from '../types'
|
||||
import { createSectionRegistry } from '../utils/section-registry'
|
||||
|
||||
const OPERATIONS_SECTIONS = [
|
||||
{
|
||||
@ -159,5 +159,4 @@ export const OPERATIONS_SECTION_IDS = operationsRegistry.sectionIds
|
||||
export const OPERATIONS_DEFAULT_SECTION = operationsRegistry.defaultSection
|
||||
export const getOperationsSectionNavItems =
|
||||
operationsRegistry.getSectionNavItems
|
||||
export const getOperationsSectionContent =
|
||||
operationsRegistry.getSectionContent
|
||||
export const getOperationsSectionContent = operationsRegistry.getSectionContent
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import type { SecuritySettings } from '../types'
|
||||
import { createSectionRegistry } from '../utils/section-registry'
|
||||
import { RateLimitSection } from '../request-limits/rate-limit-section'
|
||||
import { SensitiveWordsSection } from '../request-limits/sensitive-words-section'
|
||||
import { SSRFSection } from '../request-limits/ssrf-section'
|
||||
import type { SecuritySettings } from '../types'
|
||||
import { createSectionRegistry } from '../utils/section-registry'
|
||||
|
||||
const SECURITY_SECTIONS = [
|
||||
{
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import type { SiteSettings } from '../types'
|
||||
import { createSectionRegistry } from '../utils/section-registry'
|
||||
import { SystemInfoSection } from '../general/system-info-section'
|
||||
import {
|
||||
parseHeaderNavModules,
|
||||
parseSidebarModulesAdmin,
|
||||
@ -9,7 +8,8 @@ import {
|
||||
import { HeaderNavigationSection } from '../maintenance/header-navigation-section'
|
||||
import { NoticeSection } from '../maintenance/notice-section'
|
||||
import { SidebarModulesSection } from '../maintenance/sidebar-modules-section'
|
||||
import { SystemInfoSection } from '../general/system-info-section'
|
||||
import type { SiteSettings } from '../types'
|
||||
import { createSectionRegistry } from '../utils/section-registry'
|
||||
|
||||
const SITE_SECTIONS = [
|
||||
{
|
||||
|
||||
@ -44,12 +44,12 @@ export type SettingsRouteConfigOptions<
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* export const Route = createFileRoute('/_authenticated/system-settings/site')(
|
||||
* export const Route = createFileRoute('/_authenticated/system-settings/site')(
|
||||
* createSettingsRouteConfig({
|
||||
* sectionIds: SITE_SECTION_IDS,
|
||||
* defaultSection: SITE_DEFAULT_SECTION,
|
||||
* component: SiteSettings,
|
||||
* routePath: '/system-settings/site',
|
||||
* sectionIds: SITE_SECTION_IDS,
|
||||
* defaultSection: SITE_DEFAULT_SECTION,
|
||||
* component: SiteSettings,
|
||||
* routePath: '/system-settings/site',
|
||||
* redirectToDefault: true,
|
||||
* })
|
||||
* )
|
||||
|
||||
53
web/default/src/i18n/locales/en.json
vendored
53
web/default/src/i18n/locales/en.json
vendored
@ -98,6 +98,7 @@
|
||||
"7 days ago": "7 days ago",
|
||||
"80,443,8080": "80,443,8080",
|
||||
"A billing multiplier. Lower ratios mean lower API call costs.": "A billing multiplier. Lower ratios mean lower API call costs.",
|
||||
"A focused home for keys, balance, routing, and service health.": "A focused home for keys, balance, routing, and service health.",
|
||||
"About": "About",
|
||||
"Accept Unpriced Models": "Accept Unpriced Models",
|
||||
"Accepts a JSON array of model identifiers that support the Imagine API.": "Accepts a JSON array of model identifiers that support the Imagine API.",
|
||||
@ -145,6 +146,7 @@
|
||||
"Add chat preset": "Add chat preset",
|
||||
"Add condition": "Add condition",
|
||||
"Add Condition": "Add Condition",
|
||||
"Add credits": "Add credits",
|
||||
"Add custom model(s), comma-separated": "Add custom model(s), comma-separated",
|
||||
"Add discount tier": "Add discount tier",
|
||||
"Add each model or tag you want to include.": "Add each model or tag you want to include.",
|
||||
@ -311,6 +313,7 @@
|
||||
"Announcements": "Announcements",
|
||||
"Announcements saved successfully": "Announcements saved successfully",
|
||||
"Answer": "Answer",
|
||||
"Answers for common access and billing questions": "Answers for common access and billing questions",
|
||||
"Anthropic": "Anthropic",
|
||||
"Any Match (OR)": "Any Match (OR)",
|
||||
"API": "API",
|
||||
@ -411,6 +414,7 @@
|
||||
"Audio Preview": "Audio Preview",
|
||||
"Audio ratio": "Audio ratio",
|
||||
"Audio Tokens": "Audio Tokens",
|
||||
"Auth configured": "Auth configured",
|
||||
"Auth Style": "Auth Style",
|
||||
"Authentication": "Authentication",
|
||||
"Authenticator code": "Authenticator code",
|
||||
@ -446,8 +450,6 @@
|
||||
"Available Models": "Available Models",
|
||||
"Available Rewards": "Available Rewards",
|
||||
"Average latency": "Average latency",
|
||||
"Latency": "Latency",
|
||||
"Latency short": "Lat.",
|
||||
"Average latency, TTFT, and success rate by group": "Average latency, TTFT, and success rate by group",
|
||||
"Average RPM": "Average RPM",
|
||||
"Average time-to-first-token (TTFT) by group": "Average time-to-first-token (TTFT) by group",
|
||||
@ -475,6 +477,7 @@
|
||||
"Baidu V2": "Baidu V2",
|
||||
"Balance": "Balance",
|
||||
"Balance and top-up management": "Balance and top-up management",
|
||||
"Balance is shown in quota units": "Balance is shown in quota units",
|
||||
"Balance queried successfully": "Balance queried successfully",
|
||||
"Balance updated successfully": "Balance updated successfully",
|
||||
"Balance updated: {{balance}}": "Balance updated: {{balance}}",
|
||||
@ -549,11 +552,13 @@
|
||||
"Broadcast a global banner to users. Markdown is supported.": "Broadcast a global banner to users. Markdown is supported.",
|
||||
"Broadcast short system notices on the dashboard": "Broadcast short system notices on the dashboard",
|
||||
"Browse and compare": "Browse and compare",
|
||||
"Browse available models and pricing": "Browse available models and pricing",
|
||||
"Browse rankings by category": "Browse rankings by category",
|
||||
"Budget tokens = max tokens × ratio. Accepts a decimal between 0.002 and 1. Recommended to keep aligned with upstream billing.": "Budget tokens = max tokens × ratio. Accepts a decimal between 0.002 and 1. Recommended to keep aligned with upstream billing.",
|
||||
"Budget tokens = max tokens × ratio. Accepts a decimal between 0.1 and 1.": "Budget tokens = max tokens × ratio. Accepts a decimal between 0.1 and 1.",
|
||||
"Budget Tokens Ratio": "Budget Tokens Ratio",
|
||||
"Budgets": "Budgets",
|
||||
"Build on your API gateway in minutes": "Build on your API gateway in minutes",
|
||||
"Built for developers,": "Built for developers,",
|
||||
"Built-in": "Built-in",
|
||||
"Built-in Device": "Built-in Device",
|
||||
@ -846,6 +851,7 @@
|
||||
"Configure xAI Grok model specific settings": "Configure xAI Grok model specific settings",
|
||||
"Configure your account behavior preferences": "Configure your account behavior preferences",
|
||||
"Configure your account preferences and integrations": "Configure your account preferences and integrations",
|
||||
"Configured routes and latency checks": "Configured routes and latency checks",
|
||||
"Confirm": "Confirm",
|
||||
"Confirm Action": "Confirm Action",
|
||||
"Confirm Batch Update": "Confirm Batch Update",
|
||||
@ -937,6 +943,7 @@
|
||||
"Copy model name": "Copy model name",
|
||||
"Copy model names": "Copy model names",
|
||||
"Copy prompt": "Copy prompt",
|
||||
"Copy ready-to-run curl": "Copy ready-to-run curl",
|
||||
"Copy redemption code": "Copy redemption code",
|
||||
"Copy referral link": "Copy referral link",
|
||||
"Copy Request Header": "Copy Request Header",
|
||||
@ -962,9 +969,11 @@
|
||||
"CPU Threshold (%)": "CPU Threshold (%)",
|
||||
"Create": "Create",
|
||||
"Create a copy of:": "Create a copy of:",
|
||||
"Create a key for your app or service": "Create a key for your app or service",
|
||||
"Create a new user group to configure ratio overrides for.": "Create a new user group to configure ratio overrides for.",
|
||||
"Create account": "Create account",
|
||||
"Create an account": "Create an account",
|
||||
"Create an API key to unlock the real request": "Create an API key to unlock the real request",
|
||||
"Create and review invite or credit codes.": "Create and review invite or credit codes.",
|
||||
"Create API Key": "Create API Key",
|
||||
"Create cache": "Create cache",
|
||||
@ -996,6 +1005,7 @@
|
||||
"Credential generated": "Credential generated",
|
||||
"Credential refreshed": "Credential refreshed",
|
||||
"Credentials": "Credentials",
|
||||
"Credit remaining": "Credit remaining",
|
||||
"Creem API key (leave blank unless updating)": "Creem API key (leave blank unless updating)",
|
||||
"Creem Gateway": "Creem Gateway",
|
||||
"Creem Payment": "Creem Payment",
|
||||
@ -1009,6 +1019,7 @@
|
||||
"Current Balance": "Current Balance",
|
||||
"Current Billing": "Current Billing",
|
||||
"Current Cache Size": "Current Cache Size",
|
||||
"Current domain": "Current domain",
|
||||
"Current email: {{email}}. Enter a new email to change.": "Current email: {{email}}. Enter a new email to change.",
|
||||
"Current key": "Current key",
|
||||
"Current legacy JSON is invalid, cannot append": "Current legacy JSON is invalid, cannot append",
|
||||
@ -1211,6 +1222,7 @@
|
||||
"Display name shown to users.": "Display name shown to users.",
|
||||
"Display Options": "Display Options",
|
||||
"Display Token Statistics": "Display Token Statistics",
|
||||
"Displayed in": "Displayed in",
|
||||
"Displays the mobile sidebar.": "Displays the mobile sidebar.",
|
||||
"Do not over-trust this feature. IP may be spoofed. Please use with nginx, CDN and other gateways.": "Do not over-trust this feature. IP may be spoofed. Please use with nginx, CDN and other gateways.",
|
||||
"Do not repeat check-in; only once per day": "Do not repeat check-in; only once per day",
|
||||
@ -1714,6 +1726,7 @@
|
||||
"Finance": "Finance",
|
||||
"Fine-tune Midjourney integration and guardrails.": "Fine-tune Midjourney integration and guardrails.",
|
||||
"Finish Time": "Finish Time",
|
||||
"First API request": "First API request",
|
||||
"First/Last Frame to Video": "First/Last Frame to Video",
|
||||
"Fix Abilities": "Fix Abilities",
|
||||
"Fixed abilities: {{success}} succeeded, {{fails}} failed": "Fixed abilities: {{success}} succeeded, {{fails}} failed",
|
||||
@ -1800,6 +1813,7 @@
|
||||
"Generation quality preset": "Generation quality preset",
|
||||
"Generic cache": "Generic cache",
|
||||
"Get notified when balance falls below this value": "Get notified when balance falls below this value",
|
||||
"Get started": "Get started",
|
||||
"Get Started": "Get Started",
|
||||
"GitHub": "GitHub",
|
||||
"Give the group a recognizable name and optional description.": "Give the group a recognizable name and optional description.",
|
||||
@ -1856,6 +1870,7 @@
|
||||
"Group-based rate limits": "Group-based rate limits",
|
||||
"Group:": "Group:",
|
||||
"Group: {{ratio}}x": "Group: {{ratio}}x",
|
||||
"Grouped monitor status from Uptime Kuma": "Grouped monitor status from Uptime Kuma",
|
||||
"Groups": "Groups",
|
||||
"Groups *": "Groups *",
|
||||
"Groups that users can select when creating API keys.": "Groups that users can select when creating API keys.",
|
||||
@ -1884,6 +1899,7 @@
|
||||
"Hidden — verify to reveal": "Hidden — verify to reveal",
|
||||
"Hide": "Hide",
|
||||
"Hide API key": "Hide API key",
|
||||
"Hide setup guide": "Hide setup guide",
|
||||
"High Performance": "High Performance",
|
||||
"High-risk operation confirmation": "High-risk Operation Confirmation",
|
||||
"High-risk status code retry confirmation text": "I CONFIRM",
|
||||
@ -1990,6 +2006,7 @@
|
||||
"Input tokens": "Input tokens",
|
||||
"Input Tokens": "Input Tokens",
|
||||
"Inset": "Inset",
|
||||
"Inspect requests, errors, and billing details": "Inspect requests, errors, and billing details",
|
||||
"Inspect user prompts": "Inspect user prompts",
|
||||
"Instance": "Instance",
|
||||
"Integrations": "Integrations",
|
||||
@ -2063,8 +2080,10 @@
|
||||
"JustSong": "JustSong",
|
||||
"K": "K",
|
||||
"Keep enabled if you need to proxy requests for different upstream accounts.": "Keep enabled if you need to proxy requests for different upstream accounts.",
|
||||
"Keep enough balance before production traffic": "Keep enough balance before production traffic",
|
||||
"Keep original value": "Keep original value",
|
||||
"Keep original value (skip if target exists)": "Keep original value (skip if target exists)",
|
||||
"Keep the platform ready": "Keep the platform ready",
|
||||
"Keep this above 1 minute to avoid heavy database load": "Keep this above 1 minute to avoid heavy database load",
|
||||
"Keep-alive Ping": "Keep-alive Ping",
|
||||
"Key": "Key",
|
||||
@ -2090,7 +2109,11 @@
|
||||
"Last updated:": "Last updated:",
|
||||
"Last Used": "Last Used",
|
||||
"Last used:": "Last used:",
|
||||
"Latency": "Latency",
|
||||
"Latency check": "Latency check",
|
||||
"Latency short": "Lat.",
|
||||
"Latency trend (last 24h)": "Latency trend (last 24h)",
|
||||
"Latest platform updates and notices": "Latest platform updates and notices",
|
||||
"Lavender Dream": "Lavender Dream",
|
||||
"Layout": "Layout",
|
||||
"lead": "lead",
|
||||
@ -2141,6 +2164,7 @@
|
||||
"Load Balancing": "Load Balancing",
|
||||
"Load template...": "Load template...",
|
||||
"Loader": "Loader",
|
||||
"Loading": "Loading",
|
||||
"Loading configuration": "Loading configuration",
|
||||
"Loading content settings...": "Loading content settings...",
|
||||
"Loading current models...": "Loading current models...",
|
||||
@ -2314,6 +2338,7 @@
|
||||
"Model ratios reset successfully": "Model ratios reset successfully",
|
||||
"Model Regex": "Model Regex",
|
||||
"Model Regex (one per line)": "Model Regex (one per line)",
|
||||
"Model selected": "Model selected",
|
||||
"Model Square": "Model Square",
|
||||
"Model Tags": "Model Tags",
|
||||
"Model to use for testing": "Model to use for testing",
|
||||
@ -2344,6 +2369,7 @@
|
||||
"Module availability": "Module availability",
|
||||
"MokaAI": "MokaAI",
|
||||
"Monitor": "Monitor",
|
||||
"Monitor balance, usage, and request volume": "Monitor balance, usage, and request volume",
|
||||
"Monitoring & Alerts": "Monitoring & Alerts",
|
||||
"Month": "Month",
|
||||
"Month number": "Month number",
|
||||
@ -2406,6 +2432,7 @@
|
||||
"name@example.com": "name@example.com",
|
||||
"Native format": "Native format",
|
||||
"Need a code?": "Need a code?",
|
||||
"Needs API key": "Needs API key",
|
||||
"Nested JSON defining per-group rules for adding (+:), removing (-:), or appending usable groups.": "Nested JSON defining per-group rules for adding (+:), removing (-:), or appending usable groups.",
|
||||
"Nested JSON: source group →": "Nested JSON: source group →",
|
||||
"Network proxy for this channel (supports socks5 protocol)": "Network proxy for this channel (supports socks5 protocol)",
|
||||
@ -2438,6 +2465,7 @@
|
||||
"No announcements at this time": "No announcements at this time",
|
||||
"No announcements yet. Click \"Add Announcement\" to create one.": "No announcements yet. Click \"Add Announcement\" to create one.",
|
||||
"No API Domains yet. Click \"Add API\" to create one.": "No API Domains yet. Click \"Add API\" to create one.",
|
||||
"No API key yet": "No API key yet",
|
||||
"No API keys available. Create your first API key to get started.": "No API keys available. Create your first API key to get started.",
|
||||
"No API Keys Found": "No API Keys Found",
|
||||
"No API routes configured": "No API routes configured",
|
||||
@ -2632,6 +2660,7 @@
|
||||
"One IP or CIDR range per line": "One IP or CIDR range per line",
|
||||
"One IP per line (empty for no restriction)": "One IP per line (empty for no restriction)",
|
||||
"one keyword per line": "one keyword per line",
|
||||
"Online": "Online",
|
||||
"Online payment is not enabled. Please contact the administrator.": "Online payment is not enabled. Please contact the administrator.",
|
||||
"Online topup is not enabled. Please use redemption code or contact administrator.": "Online topup is not enabled. Please use redemption code or contact administrator.",
|
||||
"Only allow specific email domains": "Only allow specific email domains",
|
||||
@ -3082,6 +3111,7 @@
|
||||
"Raw Quota": "Raw Quota",
|
||||
"Re-enable on success": "Re-enable on success",
|
||||
"Re-login": "Re-login",
|
||||
"Ready": "Ready",
|
||||
"Ready to initialize": "Ready to initialize",
|
||||
"Ready to simplify": "Ready to simplify",
|
||||
"Real exchange rate between USD and your payment gateway currency": "Real exchange rate between USD and your payment gateway currency",
|
||||
@ -3097,6 +3127,7 @@
|
||||
"Recharge Amount": "Recharge Amount",
|
||||
"Recharge Amount (USD)": "Recharge Amount (USD)",
|
||||
"Recommended": "Recommended",
|
||||
"Recommended actions": "Recommended actions",
|
||||
"Recommended to keep this high to avoid upstream throttling.": "Recommended to keep this high to avoid upstream throttling.",
|
||||
"Record IP Address": "Record IP Address",
|
||||
"Record quota usage": "Record quota usage",
|
||||
@ -3281,6 +3312,7 @@
|
||||
"Revenue": "Revenue",
|
||||
"Review & initialize": "Review & initialize",
|
||||
"Review current version and fetch release notes.": "Review current version and fetch release notes.",
|
||||
"Review model rates before scaling traffic": "Review model rates before scaling traffic",
|
||||
"Review your payment details": "Review your payment details",
|
||||
"Review your purchase details before proceeding.": "Review your purchase details before proceeding.",
|
||||
"Rewards will be added directly to your balance": "Rewards will be added directly to your balance",
|
||||
@ -3291,8 +3323,10 @@
|
||||
"Root": "Root",
|
||||
"Rose Garden": "Rose Garden",
|
||||
"Route": "Route",
|
||||
"Route active": "Route active",
|
||||
"Route Description": "Route Description",
|
||||
"Route is required": "Route is required",
|
||||
"Route, auth, and balance check in one place": "Route, auth, and balance check in one place",
|
||||
"Routing & Overrides": "Routing & Overrides",
|
||||
"Routing Strategy": "Routing Strategy",
|
||||
"Rows per page": "Rows per page",
|
||||
@ -3401,6 +3435,7 @@
|
||||
"Secret environment variables (JSON)": "Secret environment variables (JSON)",
|
||||
"Secret Key": "Secret Key",
|
||||
"Secure & Reliable": "Secure & Reliable",
|
||||
"Secured": "Secured",
|
||||
"Security": "Security",
|
||||
"Security & Limits": "Security & Limits",
|
||||
"Security Check": "Security Check",
|
||||
@ -3480,6 +3515,7 @@
|
||||
"Selected when creating a token and used as the default billing group for API calls.": "Selected when creating a token and used as the default billing group for API calls.",
|
||||
"Self-Use Mode": "Self-Use Mode",
|
||||
"Send": "Send",
|
||||
"Send a request": "Send a request",
|
||||
"Send code": "Send code",
|
||||
"Send email alerts when a user falls below this quota": "Send email alerts when a user falls below this quota",
|
||||
"Sending...": "Sending...",
|
||||
@ -3520,7 +3556,11 @@
|
||||
"Settings": "Settings",
|
||||
"Settings & Preferences": "Settings & Preferences",
|
||||
"Settings updated successfully": "Settings updated successfully",
|
||||
"Setup guide": "Setup guide",
|
||||
"Setup guide complete": "Setup guide complete",
|
||||
"Setup guide is collapsed. Expand it anytime.": "Setup guide is collapsed. Expand it anytime.",
|
||||
"Setup Instructions": "Setup Instructions",
|
||||
"Setup progress: {{completed}}/{{total}}": "Setup progress: {{completed}}/{{total}}",
|
||||
"Setup Two-Factor Authentication": "Setup Two-Factor Authentication",
|
||||
"Share": "Share",
|
||||
"Share your link and earn rewards": "Share your link and earn rewards",
|
||||
@ -3531,6 +3571,7 @@
|
||||
"Show all providers including unbound": "Show all providers including unbound",
|
||||
"Show only bound providers": "Show only bound providers",
|
||||
"Show prices in currency instead of quota.": "Show prices in currency instead of quota.",
|
||||
"Show setup guide": "Show setup guide",
|
||||
"Show token usage statistics in the UI": "Show token usage statistics in the UI",
|
||||
"Showcase core capabilities with demo credentials and limited access.": "Showcase core capabilities with demo credentials and limited access.",
|
||||
"Showing": "Showing",
|
||||
@ -3605,11 +3646,11 @@
|
||||
"Statistical tokens": "Statistical tokens",
|
||||
"Statistics reset": "Statistics reset",
|
||||
"Status": "Status",
|
||||
"Status short": "Status",
|
||||
"Status & Sync": "Status & Sync",
|
||||
"Status Code": "Status Code",
|
||||
"Status Code Mapping": "Status Code Mapping",
|
||||
"Status Page Slug": "Status Page Slug",
|
||||
"Status short": "Status",
|
||||
"Status:": "Status:",
|
||||
"Stay": "Stay",
|
||||
"Stay tuned though!": "Stay tuned though!",
|
||||
@ -3754,6 +3795,7 @@
|
||||
"Test Latency": "Test Latency",
|
||||
"Test Mode": "Test Mode",
|
||||
"Test Model": "Test Model",
|
||||
"Test models and prompts from the browser": "Test models and prompts from the browser",
|
||||
"Testing all enabled channels started. Please refresh to see results.": "Testing all enabled channels started. Please refresh to see results.",
|
||||
"Testing...": "Testing...",
|
||||
"Text": "Text",
|
||||
@ -3837,8 +3879,8 @@
|
||||
"This year": "This year",
|
||||
"Three steps to get started": "Three steps to get started",
|
||||
"Throughput": "Throughput",
|
||||
"Throughput short": "TPS",
|
||||
"Throughput by group": "Throughput by group",
|
||||
"Throughput short": "TPS",
|
||||
"Throughput trend": "Throughput trend",
|
||||
"Tier": "Tier",
|
||||
"Tier conditions": "Tier conditions",
|
||||
@ -4092,6 +4134,7 @@
|
||||
"URL is required": "URL is required",
|
||||
"URL to your logo image (optional)": "URL to your logo image (optional)",
|
||||
"Usage": "Usage",
|
||||
"Usage at a glance": "Usage at a glance",
|
||||
"Usage guide": "Usage guide",
|
||||
"Usage logs": "Usage logs",
|
||||
"Usage Logs": "Usage Logs",
|
||||
@ -4189,6 +4232,7 @@
|
||||
"Verification required to reveal the saved key.": "Verification required to reveal the saved key.",
|
||||
"Verify": "Verify",
|
||||
"Verify and Sign In": "Verify and Sign In",
|
||||
"Verify routing with Playground or your client": "Verify routing with Playground or your client",
|
||||
"Verify Setup": "Verify Setup",
|
||||
"Verify your database connection": "Verify your database connection",
|
||||
"Version Overrides": "Version Overrides",
|
||||
@ -4346,6 +4390,7 @@
|
||||
"Your GitHub OAuth Client Secret": "Your GitHub OAuth Client Secret",
|
||||
"Your new backup codes are ready": "Your new backup codes are ready",
|
||||
"Your Referral Link": "Your Referral Link",
|
||||
"Your setup guide is collapsed so usage stays in focus.": "Your setup guide is collapsed so usage stays in focus.",
|
||||
"Your system access token for API authentication. Keep it secure and don't share it with others.": "Your system access token for API authentication. Keep it secure and don't share it with others.",
|
||||
"Your Telegram Bot Token": "Your Telegram Bot Token",
|
||||
"Your Turnstile secret key": "Your Turnstile secret key",
|
||||
|
||||
53
web/default/src/i18n/locales/fr.json
vendored
53
web/default/src/i18n/locales/fr.json
vendored
@ -98,6 +98,7 @@
|
||||
"7 days ago": "Il y a 7 jours",
|
||||
"80,443,8080": "80,443,8080",
|
||||
"A billing multiplier. Lower ratios mean lower API call costs.": "Un multiplicateur de facturation. Plus le ratio est faible, plus le coût des appels API est bas.",
|
||||
"A focused home for keys, balance, routing, and service health.": "Un accueil dédié aux clés, au solde, au routage et à l'état du service.",
|
||||
"About": "À propos",
|
||||
"Accept Unpriced Models": "Accepter les modèles non tarifés",
|
||||
"Accepts a JSON array of model identifiers that support the Imagine API.": "Accepte un tableau JSON d'identifiants de modèles qui prennent en charge l'API Imagine.",
|
||||
@ -145,6 +146,7 @@
|
||||
"Add chat preset": "Ajouter un préréglage de chat",
|
||||
"Add condition": "Ajouter une condition",
|
||||
"Add Condition": "Ajouter une condition",
|
||||
"Add credits": "Ajouter des crédits",
|
||||
"Add custom model(s), comma-separated": "Ajouter un ou plusieurs modèles personnalisés, séparés par des virgules",
|
||||
"Add discount tier": "Ajouter un niveau de réduction",
|
||||
"Add each model or tag you want to include.": "Ajoutez chaque modèle ou étiquette que vous souhaitez inclure.",
|
||||
@ -311,6 +313,7 @@
|
||||
"Announcements": "Annonces",
|
||||
"Announcements saved successfully": "Annonces enregistrées avec succès",
|
||||
"Answer": "Réponse",
|
||||
"Answers for common access and billing questions": "Réponses aux questions courantes sur l'accès et la facturation",
|
||||
"Anthropic": "Anthropic",
|
||||
"Any Match (OR)": "N'importe laquelle (OR)",
|
||||
"API": "API",
|
||||
@ -411,6 +414,7 @@
|
||||
"Audio Preview": "Aperçu audio",
|
||||
"Audio ratio": "Ratio audio",
|
||||
"Audio Tokens": "Jetons audio",
|
||||
"Auth configured": "Authentification configurée",
|
||||
"Auth Style": "Style d'authentification",
|
||||
"Authentication": "Authentification",
|
||||
"Authenticator code": "Code d'authentification",
|
||||
@ -446,8 +450,6 @@
|
||||
"Available Models": "Modèles disponibles",
|
||||
"Available Rewards": "Récompenses disponibles",
|
||||
"Average latency": "Latence moyenne",
|
||||
"Latency": "Latence",
|
||||
"Latency short": "Lat.",
|
||||
"Average latency, TTFT, and success rate by group": "Latence moyenne, TTFT et taux de réussite par groupe",
|
||||
"Average RPM": "RPM moyen",
|
||||
"Average time-to-first-token (TTFT) by group": "Temps moyen jusqu’au premier token (TTFT) par groupe",
|
||||
@ -475,6 +477,7 @@
|
||||
"Baidu V2": "Baidu V2",
|
||||
"Balance": "Solde",
|
||||
"Balance and top-up management": "Gestion du solde et des recharges",
|
||||
"Balance is shown in quota units": "Le solde est affiché en unités de quota",
|
||||
"Balance queried successfully": "Solde interrogé avec succès",
|
||||
"Balance updated successfully": "Solde mis à jour avec succès",
|
||||
"Balance updated: {{balance}}": "Solde mis à jour : {{balance}}",
|
||||
@ -549,11 +552,13 @@
|
||||
"Broadcast a global banner to users. Markdown is supported.": "Diffuser une bannière globale aux utilisateurs. Le Markdown est pris en charge.",
|
||||
"Broadcast short system notices on the dashboard": "Diffuser de courtes notifications système sur le tableau de bord",
|
||||
"Browse and compare": "Parcourir et comparer",
|
||||
"Browse available models and pricing": "Parcourir les modèles disponibles et les tarifs",
|
||||
"Browse rankings by category": "Parcourir les classements par catégorie",
|
||||
"Budget tokens = max tokens × ratio. Accepts a decimal between 0.002 and 1. Recommended to keep aligned with upstream billing.": "Jetons budgétaires = jetons max × ratio. Accepte un nombre décimal entre 0,002 et 1. Il est recommandé de rester aligné avec la facturation en amont.",
|
||||
"Budget tokens = max tokens × ratio. Accepts a decimal between 0.1 and 1.": "Jetons budgétaires = jetons max × ratio. Accepte un nombre décimal entre 0,1 et 1.",
|
||||
"Budget Tokens Ratio": "Ratio de jetons budgétaires",
|
||||
"Budgets": "Budgets",
|
||||
"Build on your API gateway in minutes": "Construisez sur votre passerelle API en quelques minutes",
|
||||
"Built for developers,": "Conçu pour les développeurs,",
|
||||
"Built-in": "Intégré",
|
||||
"Built-in Device": "Appareil intégré",
|
||||
@ -846,6 +851,7 @@
|
||||
"Configure xAI Grok model specific settings": "Configurer les paramètres spécifiques du modèle xAI Grok",
|
||||
"Configure your account behavior preferences": "Configurer les préférences de comportement de votre compte",
|
||||
"Configure your account preferences and integrations": "Configurer les préférences et les intégrations de votre compte",
|
||||
"Configured routes and latency checks": "Routes configurées et contrôles de latence",
|
||||
"Confirm": "Confirmer",
|
||||
"Confirm Action": "Confirmer l'action",
|
||||
"Confirm Batch Update": "Confirmer la mise à jour par lot",
|
||||
@ -937,6 +943,7 @@
|
||||
"Copy model name": "Copier le nom du modèle",
|
||||
"Copy model names": "Copier les noms des modèles",
|
||||
"Copy prompt": "Copier le prompt",
|
||||
"Copy ready-to-run curl": "Copier le curl prêt à exécuter",
|
||||
"Copy redemption code": "Copier le code de rachat",
|
||||
"Copy referral link": "Copier le lien de parrainage",
|
||||
"Copy Request Header": "Copier un en-tête de requête",
|
||||
@ -962,9 +969,11 @@
|
||||
"CPU Threshold (%)": "Seuil CPU (%)",
|
||||
"Create": "Créer",
|
||||
"Create a copy of:": "Créer une copie de :",
|
||||
"Create a key for your app or service": "Créer une clé pour votre application ou service",
|
||||
"Create a new user group to configure ratio overrides for.": "Créer un nouveau groupe d'utilisateurs pour configurer les remplacements de ratio.",
|
||||
"Create account": "Créer un compte",
|
||||
"Create an account": "Créer un compte",
|
||||
"Create an API key to unlock the real request": "Créez une clé API pour débloquer la requête réelle",
|
||||
"Create and review invite or credit codes.": "Créer et examiner les codes d'invitation ou de crédit.",
|
||||
"Create API Key": "Créer une clé API",
|
||||
"Create cache": "Créer le cache",
|
||||
@ -996,6 +1005,7 @@
|
||||
"Credential generated": "Identifiant généré",
|
||||
"Credential refreshed": "Identifiant actualisé",
|
||||
"Credentials": "Identifiants",
|
||||
"Credit remaining": "Crédit restant",
|
||||
"Creem API key (leave blank unless updating)": "Clé API Creem (laissez vide sauf si mise à jour)",
|
||||
"Creem Gateway": "Passerelle Creem",
|
||||
"Creem Payment": "Paiement Creem",
|
||||
@ -1009,6 +1019,7 @@
|
||||
"Current Balance": "Solde actuel",
|
||||
"Current Billing": "Facturation actuelle",
|
||||
"Current Cache Size": "Taille actuelle du cache",
|
||||
"Current domain": "Domaine actuel",
|
||||
"Current email: {{email}}. Enter a new email to change.": "E-mail actuel : {{email}}. Saisissez un nouvel e-mail pour le modifier.",
|
||||
"Current key": "Clé actuelle",
|
||||
"Current legacy JSON is invalid, cannot append": "Le JSON ancien format actuel n'est pas valide, impossible d'ajouter",
|
||||
@ -1211,6 +1222,7 @@
|
||||
"Display name shown to users.": "Nom d'affichage affiché aux utilisateurs.",
|
||||
"Display Options": "Options d'affichage",
|
||||
"Display Token Statistics": "Afficher les statistiques des jetons",
|
||||
"Displayed in": "Affiché en",
|
||||
"Displays the mobile sidebar.": "Affiche la barre latérale mobile.",
|
||||
"Do not over-trust this feature. IP may be spoofed. Please use with nginx, CDN and other gateways.": "Ne faites pas trop confiance à cette fonctionnalité. L'IP peut être usurpée. Veuillez l'utiliser avec nginx, CDN et autres passerelles.",
|
||||
"Do not repeat check-in; only once per day": "Ne répétez pas le check-in ; une seule fois par jour",
|
||||
@ -1714,6 +1726,7 @@
|
||||
"Finance": "Finance",
|
||||
"Fine-tune Midjourney integration and guardrails.": "Affiner l'intégration et les garde-fous de Midjourney.",
|
||||
"Finish Time": "Heure de fin",
|
||||
"First API request": "Première requête API",
|
||||
"First/Last Frame to Video": "Première/Dernière image vers vidéo",
|
||||
"Fix Abilities": "Corriger les capacités",
|
||||
"Fixed abilities: {{success}} succeeded, {{fails}} failed": "Correction des capacités : {{success}} réussie(s), {{fails}} échouée(s)",
|
||||
@ -1800,6 +1813,7 @@
|
||||
"Generation quality preset": "Préréglage de qualité de génération",
|
||||
"Generic cache": "Cache générique",
|
||||
"Get notified when balance falls below this value": "Recevoir une notification lorsque le solde tombe en dessous de cette valeur",
|
||||
"Get started": "Commencer",
|
||||
"Get Started": "Commencer",
|
||||
"GitHub": "GitHub",
|
||||
"Give the group a recognizable name and optional description.": "Donnez au groupe un nom reconnaissable et une description facultative.",
|
||||
@ -1856,6 +1870,7 @@
|
||||
"Group-based rate limits": "Limites de débit basées sur le groupe",
|
||||
"Group:": "Groupe :",
|
||||
"Group: {{ratio}}x": "Groupe : {{ratio}}x",
|
||||
"Grouped monitor status from Uptime Kuma": "État des moniteurs groupés depuis Uptime Kuma",
|
||||
"Groups": "Groupes",
|
||||
"Groups *": "Groupes *",
|
||||
"Groups that users can select when creating API keys.": "Groupes que les utilisateurs peuvent sélectionner lors de la création de clés API.",
|
||||
@ -1884,6 +1899,7 @@
|
||||
"Hidden — verify to reveal": "Masqué — vérifiez pour révéler",
|
||||
"Hide": "Masquer",
|
||||
"Hide API key": "Masquer la clé API",
|
||||
"Hide setup guide": "Masquer le guide de configuration",
|
||||
"High Performance": "Hautes performances",
|
||||
"High-risk operation confirmation": "Confirmation d'opération à haut risque",
|
||||
"High-risk status code retry confirmation text": "JE CONFIRME",
|
||||
@ -1990,6 +2006,7 @@
|
||||
"Input tokens": "Jetons d’entrée",
|
||||
"Input Tokens": "Tokens d'entrée",
|
||||
"Inset": "Encastré",
|
||||
"Inspect requests, errors, and billing details": "Inspecter les requêtes, les erreurs et les détails de facturation",
|
||||
"Inspect user prompts": "Inspecter les invites utilisateur",
|
||||
"Instance": "Instance",
|
||||
"Integrations": "Intégrations",
|
||||
@ -2063,8 +2080,10 @@
|
||||
"JustSong": "JustSong",
|
||||
"K": "K",
|
||||
"Keep enabled if you need to proxy requests for different upstream accounts.": "Gardez activé si vous devez proxifier les requêtes pour différents comptes en amont.",
|
||||
"Keep enough balance before production traffic": "Gardez un solde suffisant avant le trafic de production",
|
||||
"Keep original value": "Conserver la valeur originale",
|
||||
"Keep original value (skip if target exists)": "Conserver la valeur originale (ignorer si la cible existe)",
|
||||
"Keep the platform ready": "Gardez la plateforme prête",
|
||||
"Keep this above 1 minute to avoid heavy database load": "Gardez cette valeur au-dessus de 1 minute pour éviter une charge excessive de la base de données",
|
||||
"Keep-alive Ping": "Ping de maintien de connexion",
|
||||
"Key": "Clé",
|
||||
@ -2090,7 +2109,11 @@
|
||||
"Last updated:": "Dernière mise à jour :",
|
||||
"Last Used": "Dernière utilisation",
|
||||
"Last used:": "Dernière utilisation :",
|
||||
"Latency": "Latence",
|
||||
"Latency check": "Contrôle de latence",
|
||||
"Latency short": "Lat.",
|
||||
"Latency trend (last 24h)": "Tendance de latence (24 dernières heures)",
|
||||
"Latest platform updates and notices": "Dernières mises à jour et annonces de la plateforme",
|
||||
"Lavender Dream": "Rêve de lavande",
|
||||
"Layout": "Disposition",
|
||||
"lead": "tête",
|
||||
@ -2141,6 +2164,7 @@
|
||||
"Load Balancing": "Équilibrage de charge",
|
||||
"Load template...": "Charger le modèle...",
|
||||
"Loader": "Chargeur",
|
||||
"Loading": "Chargement",
|
||||
"Loading configuration": "Chargement de la configuration",
|
||||
"Loading content settings...": "Chargement des paramètres de contenu...",
|
||||
"Loading current models...": "Chargement des modèles actuels...",
|
||||
@ -2314,6 +2338,7 @@
|
||||
"Model ratios reset successfully": "Ratios des modèles réinitialisés avec succès",
|
||||
"Model Regex": "Regex du modèle",
|
||||
"Model Regex (one per line)": "Regex du modèle (un par ligne)",
|
||||
"Model selected": "Modèle sélectionné",
|
||||
"Model Square": "Place des modèles",
|
||||
"Model Tags": "Tags de modèle",
|
||||
"Model to use for testing": "Modèle à utiliser pour les tests",
|
||||
@ -2344,6 +2369,7 @@
|
||||
"Module availability": "Disponibilité du module",
|
||||
"MokaAI": "MokaAI",
|
||||
"Monitor": "Surveiller",
|
||||
"Monitor balance, usage, and request volume": "Surveillez le solde, l'utilisation et le volume de requêtes",
|
||||
"Monitoring & Alerts": "Surveillance & Alertes",
|
||||
"Month": "Mois",
|
||||
"Month number": "Numéro du mois",
|
||||
@ -2406,6 +2432,7 @@
|
||||
"name@example.com": "name@example.com",
|
||||
"Native format": "Format natif",
|
||||
"Need a code?": "Besoin d'un code ?",
|
||||
"Needs API key": "Clé API requise",
|
||||
"Nested JSON defining per-group rules for adding (+:), removing (-:), or appending usable groups.": "JSON imbriqué définissant des règles par groupe pour ajouter (+:), supprimer (-:), ou ajouter des groupes utilisables.",
|
||||
"Nested JSON: source group →": "JSON imbriqué : groupe source →",
|
||||
"Network proxy for this channel (supports socks5 protocol)": "Proxy réseau pour ce canal (supporte le protocole socks5)",
|
||||
@ -2438,6 +2465,7 @@
|
||||
"No announcements at this time": "Aucune annonce pour le moment",
|
||||
"No announcements yet. Click \"Add Announcement\" to create one.": "Aucune annonce pour l'instant. Cliquez sur « Ajouter une annonce » pour en créer une.",
|
||||
"No API Domains yet. Click \"Add API\" to create one.": "Aucun domaine API pour l'instant. Cliquez sur \"Ajouter une API\" pour en créer un.",
|
||||
"No API key yet": "Aucune clé API pour le moment",
|
||||
"No API keys available. Create your first API key to get started.": "Aucune clé API disponible. Créez votre première clé API pour commencer.",
|
||||
"No API Keys Found": "Aucune clé API trouvée",
|
||||
"No API routes configured": "Aucune route API configurée",
|
||||
@ -2632,6 +2660,7 @@
|
||||
"One IP or CIDR range per line": "Une IP ou plage CIDR par ligne",
|
||||
"One IP per line (empty for no restriction)": "Une IP par ligne (laisser vide pour aucune restriction)",
|
||||
"one keyword per line": "un mot-clé par ligne",
|
||||
"Online": "En ligne",
|
||||
"Online payment is not enabled. Please contact the administrator.": "Le paiement en ligne n'est pas activé. Veuillez contacter l'administrateur.",
|
||||
"Online topup is not enabled. Please use redemption code or contact administrator.": "La recharge en ligne n'est pas activée. Veuillez utiliser un code d'échange ou contacter l'administrateur.",
|
||||
"Only allow specific email domains": "Autoriser uniquement des domaines d'e-mail spécifiques",
|
||||
@ -3082,6 +3111,7 @@
|
||||
"Raw Quota": "Quota brut",
|
||||
"Re-enable on success": "Réactiver en cas de succès",
|
||||
"Re-login": "Se reconnecter",
|
||||
"Ready": "Prêt",
|
||||
"Ready to initialize": "Prêt à initialiser",
|
||||
"Ready to simplify": "Prêt à simplifier",
|
||||
"Real exchange rate between USD and your payment gateway currency": "Taux de change réel entre l'USD et la devise de votre passerelle de paiement",
|
||||
@ -3097,6 +3127,7 @@
|
||||
"Recharge Amount": "Montant de la recharge",
|
||||
"Recharge Amount (USD)": "Montant de la recharge (USD)",
|
||||
"Recommended": "Recommandé",
|
||||
"Recommended actions": "Actions recommandées",
|
||||
"Recommended to keep this high to avoid upstream throttling.": "Il est recommandé de maintenir cette valeur élevée pour éviter la limitation en amont.",
|
||||
"Record IP Address": "Enregistrer l'adresse IP",
|
||||
"Record quota usage": "Enregistrer l'utilisation du quota",
|
||||
@ -3281,6 +3312,7 @@
|
||||
"Revenue": "Revenu",
|
||||
"Review & initialize": "Vérifier et initialiser",
|
||||
"Review current version and fetch release notes.": "Examiner la version actuelle et récupérer les notes de publication.",
|
||||
"Review model rates before scaling traffic": "Consulter les tarifs des modèles avant d'augmenter le trafic",
|
||||
"Review your payment details": "Vérifier vos détails de paiement",
|
||||
"Review your purchase details before proceeding.": "Vérifiez les détails de votre achat avant de continuer.",
|
||||
"Rewards will be added directly to your balance": "Les récompenses seront ajoutées directement à votre solde",
|
||||
@ -3291,8 +3323,10 @@
|
||||
"Root": "Racine",
|
||||
"Rose Garden": "Jardin de roses",
|
||||
"Route": "Route",
|
||||
"Route active": "Route active",
|
||||
"Route Description": "Description de la route",
|
||||
"Route is required": "La route est requise",
|
||||
"Route, auth, and balance check in one place": "Routage, authentification et solde au même endroit",
|
||||
"Routing & Overrides": "Routage et surcharges",
|
||||
"Routing Strategy": "Stratégie de routage",
|
||||
"Rows per page": "Lignes par page",
|
||||
@ -3401,6 +3435,7 @@
|
||||
"Secret environment variables (JSON)": "Variables d'environnement secrètes (JSON)",
|
||||
"Secret Key": "Clé secrète",
|
||||
"Secure & Reliable": "Sécurisé et fiable",
|
||||
"Secured": "Sécurisé",
|
||||
"Security": "Sécurité",
|
||||
"Security & Limits": "Sécurité et limites",
|
||||
"Security Check": "Vérification de sécurité",
|
||||
@ -3480,6 +3515,7 @@
|
||||
"Selected when creating a token and used as the default billing group for API calls.": "Sélectionné lors de la création d’un jeton et utilisé comme groupe de facturation par défaut pour les appels API.",
|
||||
"Self-Use Mode": "Mode d'utilisation personnelle",
|
||||
"Send": "Envoyer",
|
||||
"Send a request": "Envoyer une requête",
|
||||
"Send code": "Envoyer le code",
|
||||
"Send email alerts when a user falls below this quota": "Envoyer des alertes par e-mail lorsqu'un utilisateur descend en dessous de ce quota",
|
||||
"Sending...": "Envoi en cours...",
|
||||
@ -3520,7 +3556,11 @@
|
||||
"Settings": "Paramètres",
|
||||
"Settings & Preferences": "Paramètres et préférences",
|
||||
"Settings updated successfully": "Paramètres mis à jour avec succès",
|
||||
"Setup guide": "Guide de configuration",
|
||||
"Setup guide complete": "Guide de configuration terminé",
|
||||
"Setup guide is collapsed. Expand it anytime.": "Le guide de configuration est réduit. Vous pouvez le rouvrir à tout moment.",
|
||||
"Setup Instructions": "Instructions de configuration",
|
||||
"Setup progress: {{completed}}/{{total}}": "Progression : {{completed}}/{{total}}",
|
||||
"Setup Two-Factor Authentication": "Configurer l'authentification à deux facteurs",
|
||||
"Share": "Part",
|
||||
"Share your link and earn rewards": "Partagez votre lien et gagnez des récompenses",
|
||||
@ -3531,6 +3571,7 @@
|
||||
"Show all providers including unbound": "Afficher tous les fournisseurs (y compris non liés)",
|
||||
"Show only bound providers": "Afficher uniquement les fournisseurs liés",
|
||||
"Show prices in currency instead of quota.": "Afficher les prix en devise au lieu du quota.",
|
||||
"Show setup guide": "Afficher le guide de configuration",
|
||||
"Show token usage statistics in the UI": "Afficher les statistiques d'utilisation des jetons dans l'interface utilisateur",
|
||||
"Showcase core capabilities with demo credentials and limited access.": "Présenter les fonctionnalités principales avec des identifiants de démonstration et un accès limité.",
|
||||
"Showing": "Affichage de",
|
||||
@ -3605,11 +3646,11 @@
|
||||
"Statistical tokens": "Jetons statistiques",
|
||||
"Statistics reset": "Statistiques réinitialisées",
|
||||
"Status": "Statut",
|
||||
"Status short": "État",
|
||||
"Status & Sync": "Statut et synchronisation",
|
||||
"Status Code": "Code de statut",
|
||||
"Status Code Mapping": "Mappage des codes d'état",
|
||||
"Status Page Slug": "Slug de la page d'état",
|
||||
"Status short": "État",
|
||||
"Status:": "Statut :",
|
||||
"Stay": "Rester",
|
||||
"Stay tuned though!": "Restez à l'écoute cependant !",
|
||||
@ -3754,6 +3795,7 @@
|
||||
"Test Latency": "Tester la latence",
|
||||
"Test Mode": "Mode test",
|
||||
"Test Model": "Tester le modèle",
|
||||
"Test models and prompts from the browser": "Tester les modèles et les prompts depuis le navigateur",
|
||||
"Testing all enabled channels started. Please refresh to see results.": "Test de tous les canaux activés démarré. Veuillez actualiser pour voir les résultats.",
|
||||
"Testing...": "Test en cours...",
|
||||
"Text": "Texte",
|
||||
@ -3837,8 +3879,8 @@
|
||||
"This year": "Cette année",
|
||||
"Three steps to get started": "Trois étapes pour commencer",
|
||||
"Throughput": "Débit",
|
||||
"Throughput short": "TPS",
|
||||
"Throughput by group": "Débit par groupe",
|
||||
"Throughput short": "TPS",
|
||||
"Throughput trend": "Tendance du débit",
|
||||
"Tier": "Palier",
|
||||
"Tier conditions": "Conditions du palier",
|
||||
@ -4092,6 +4134,7 @@
|
||||
"URL is required": "L'URL est requise",
|
||||
"URL to your logo image (optional)": "URL de votre image de logo (facultatif)",
|
||||
"Usage": "Utilisation",
|
||||
"Usage at a glance": "Vue d'ensemble de l'utilisation",
|
||||
"Usage guide": "Guide d'utilisation",
|
||||
"Usage logs": "Journaux d'utilisation",
|
||||
"Usage Logs": "Journaux d'utilisation",
|
||||
@ -4189,6 +4232,7 @@
|
||||
"Verification required to reveal the saved key.": "Vérification requise pour révéler la clé enregistrée.",
|
||||
"Verify": "Vérifier",
|
||||
"Verify and Sign In": "Vérifier et se connecter",
|
||||
"Verify routing with Playground or your client": "Vérifiez le routage avec Playground ou votre client",
|
||||
"Verify Setup": "Vérifier la configuration",
|
||||
"Verify your database connection": "Vérifiez votre connexion à la base de données",
|
||||
"Version Overrides": "Remplacements de version",
|
||||
@ -4346,6 +4390,7 @@
|
||||
"Your GitHub OAuth Client Secret": "Votre Secret Client OAuth GitHub",
|
||||
"Your new backup codes are ready": "Vos nouveaux codes de secours sont prêts",
|
||||
"Your Referral Link": "Votre lien de parrainage",
|
||||
"Your setup guide is collapsed so usage stays in focus.": "Le guide de configuration est réduit afin de garder l'utilisation au premier plan.",
|
||||
"Your system access token for API authentication. Keep it secure and don't share it with others.": "Votre jeton d'accès système pour l'authentification API. Gardez-le en sécurité et ne le partagez pas avec d'autres.",
|
||||
"Your Telegram Bot Token": "Votre Jeton de Bot Telegram",
|
||||
"Your Turnstile secret key": "Votre clé secrète Turnstile",
|
||||
|
||||
53
web/default/src/i18n/locales/ja.json
vendored
53
web/default/src/i18n/locales/ja.json
vendored
@ -98,6 +98,7 @@
|
||||
"7 days ago": "7日前",
|
||||
"80,443,8080": "80,443,8080",
|
||||
"A billing multiplier. Lower ratios mean lower API call costs.": "課金倍率です。倍率が低いほど API 呼び出しコストは低くなります。",
|
||||
"A focused home for keys, balance, routing, and service health.": "キー、残高、ルーティング、サービス状態を集約したホームです。",
|
||||
"About": "このサービスについて",
|
||||
"Accept Unpriced Models": "価格設定されていないモデルを許可",
|
||||
"Accepts a JSON array of model identifiers that support the Imagine API.": "Imagine APIをサポートするモデル識別子のJSON配列を受け入れます。",
|
||||
@ -145,6 +146,7 @@
|
||||
"Add chat preset": "チャットプリセットを追加",
|
||||
"Add condition": "条件を追加",
|
||||
"Add Condition": "条件を追加",
|
||||
"Add credits": "クレジットを追加",
|
||||
"Add custom model(s), comma-separated": "カスタムモデルを追加 (コンマ区切り)",
|
||||
"Add discount tier": "割引ティアを追加",
|
||||
"Add each model or tag you want to include.": "含めたい各モデルまたはタグを追加。",
|
||||
@ -311,6 +313,7 @@
|
||||
"Announcements": "お知らせ",
|
||||
"Announcements saved successfully": "お知らせが正常に保存されました",
|
||||
"Answer": "回答",
|
||||
"Answers for common access and billing questions": "アクセスと請求に関するよくある質問への回答",
|
||||
"Anthropic": "Anthropic",
|
||||
"Any Match (OR)": "いずれか一致(OR)",
|
||||
"API": "API",
|
||||
@ -411,6 +414,7 @@
|
||||
"Audio Preview": "音声プレビュー",
|
||||
"Audio ratio": "音声倍率",
|
||||
"Audio Tokens": "音声トークン",
|
||||
"Auth configured": "認証設定済み",
|
||||
"Auth Style": "認証スタイル",
|
||||
"Authentication": "認証",
|
||||
"Authenticator code": "認証コード",
|
||||
@ -446,8 +450,6 @@
|
||||
"Available Models": "利用可能なモデル",
|
||||
"Available Rewards": "利用可能な報酬",
|
||||
"Average latency": "平均レイテンシ",
|
||||
"Latency": "レイテンシ",
|
||||
"Latency short": "遅延",
|
||||
"Average latency, TTFT, and success rate by group": "グループ別の平均レイテンシ、TTFT、成功率",
|
||||
"Average RPM": "平均RPM",
|
||||
"Average time-to-first-token (TTFT) by group": "グループ別の平均 Time to First Token(TTFT)",
|
||||
@ -475,6 +477,7 @@
|
||||
"Baidu V2": "Baidu V 2",
|
||||
"Balance": "残高",
|
||||
"Balance and top-up management": "残高とチャージ管理",
|
||||
"Balance is shown in quota units": "残高はクォータ単位で表示されます",
|
||||
"Balance queried successfully": "残高の取得に成功しました",
|
||||
"Balance updated successfully": "残高が正常に更新されました",
|
||||
"Balance updated: {{balance}}": "残高更新:{{balance}}",
|
||||
@ -549,11 +552,13 @@
|
||||
"Broadcast a global banner to users. Markdown is supported.": "ユーザーにグローバルバナーをブロードキャストします。Markdownがサポートされています。",
|
||||
"Broadcast short system notices on the dashboard": "ダッシュボードに短いシステム通知をブロードキャストします",
|
||||
"Browse and compare": "参照と比較",
|
||||
"Browse available models and pricing": "利用可能なモデルと料金を確認",
|
||||
"Browse rankings by category": "カテゴリ別にランキングを表示",
|
||||
"Budget tokens = max tokens × ratio. Accepts a decimal between 0.002 and 1. Recommended to keep aligned with upstream billing.": "予算トークン = 最大トークン × 比率。0.002から1までの小数を指定できます。アップストリームの請求と一致させることを推奨します。",
|
||||
"Budget tokens = max tokens × ratio. Accepts a decimal between 0.1 and 1.": "予算トークン = 最大トークン × 比率。0.1から1までの小数を指定できます。",
|
||||
"Budget Tokens Ratio": "予算トークン比率",
|
||||
"Budgets": "予算",
|
||||
"Build on your API gateway in minutes": "数分で API ゲートウェイ上に構築できます",
|
||||
"Built for developers,": "開発者のために構築、",
|
||||
"Built-in": "組み込み",
|
||||
"Built-in Device": "内蔵デバイス",
|
||||
@ -846,6 +851,7 @@
|
||||
"Configure xAI Grok model specific settings": "xAI Grok モデル固有の設定を構成",
|
||||
"Configure your account behavior preferences": "アカウントの動作設定を設定します。",
|
||||
"Configure your account preferences and integrations": "アカウントの設定と統合を設定します。",
|
||||
"Configured routes and latency checks": "設定済みルートとレイテンシ確認",
|
||||
"Confirm": "確認",
|
||||
"Confirm Action": "アクションの確認",
|
||||
"Confirm Batch Update": "バッチ更新の確認",
|
||||
@ -937,6 +943,7 @@
|
||||
"Copy model name": "モデル名をコピー",
|
||||
"Copy model names": "モデル名をコピー",
|
||||
"Copy prompt": "プロンプトをコピー",
|
||||
"Copy ready-to-run curl": "そのまま実行できる curl をコピー",
|
||||
"Copy redemption code": "引き換えコードをコピー",
|
||||
"Copy referral link": "紹介リンクをコピー",
|
||||
"Copy Request Header": "リクエストヘッダーをコピー",
|
||||
@ -962,9 +969,11 @@
|
||||
"CPU Threshold (%)": "CPU 閾値 (%)",
|
||||
"Create": "新規作成",
|
||||
"Create a copy of:": "コピーを作成:",
|
||||
"Create a key for your app or service": "アプリまたはサービス用のキーを作成",
|
||||
"Create a new user group to configure ratio overrides for.": "レートの上書きを設定するための新しいユーザーグループを作成します。",
|
||||
"Create account": "アカウントを作成",
|
||||
"Create an account": "アカウントを作成",
|
||||
"Create an API key to unlock the real request": "実際のリクエストを使うには API キーを作成してください",
|
||||
"Create and review invite or credit codes.": "招待コードまたはクレジットコードを作成および確認。",
|
||||
"Create API Key": "APIキーを作成",
|
||||
"Create cache": "キャッシュを作成",
|
||||
@ -996,6 +1005,7 @@
|
||||
"Credential generated": "認証情報を生成しました",
|
||||
"Credential refreshed": "認証情報を更新しました",
|
||||
"Credentials": "認証情報",
|
||||
"Credit remaining": "残りクレジット",
|
||||
"Creem API key (leave blank unless updating)": "Creem API キー (更新しない限り空白のまま)",
|
||||
"Creem Gateway": "Creem ゲートウェイ",
|
||||
"Creem Payment": "Creem 決済",
|
||||
@ -1009,6 +1019,7 @@
|
||||
"Current Balance": "現在の残高",
|
||||
"Current Billing": "現在の請求",
|
||||
"Current Cache Size": "現在のキャッシュサイズ",
|
||||
"Current domain": "現在のドメイン",
|
||||
"Current email: {{email}}. Enter a new email to change.": "現在のメール: {{email}}。変更するには新しいメールアドレスを入力してください。",
|
||||
"Current key": "現在のキー",
|
||||
"Current legacy JSON is invalid, cannot append": "現在の旧形式JSONが無効なため、追加できません",
|
||||
@ -1211,6 +1222,7 @@
|
||||
"Display name shown to users.": "ユーザーに表示される表示名。",
|
||||
"Display Options": "表示オプション",
|
||||
"Display Token Statistics": "トークン統計を表示",
|
||||
"Displayed in": "表示単位",
|
||||
"Displays the mobile sidebar.": "モバイルサイドバーを表示します。",
|
||||
"Do not over-trust this feature. IP may be spoofed. Please use with nginx, CDN and other gateways.": "この機能を過信しないでください。IPは偽装される可能性があります。nginx、CDNなどのゲートウェイと併用してください。",
|
||||
"Do not repeat check-in; only once per day": "チェックインを繰り返さないでください;1日1回のみ",
|
||||
@ -1714,6 +1726,7 @@
|
||||
"Finance": "金融",
|
||||
"Fine-tune Midjourney integration and guardrails.": "Midjourneyの統合とガードレールを微調整します。",
|
||||
"Finish Time": "完了時刻",
|
||||
"First API request": "最初の API リクエスト",
|
||||
"First/Last Frame to Video": "先頭/末尾フレームから動画",
|
||||
"Fix Abilities": "アビリティを修正",
|
||||
"Fixed abilities: {{success}} succeeded, {{fails}} failed": "能力修正:{{success}} 件成功、{{fails}} 件失敗",
|
||||
@ -1800,6 +1813,7 @@
|
||||
"Generation quality preset": "生成品質プリセット",
|
||||
"Generic cache": "汎用キャッシュ",
|
||||
"Get notified when balance falls below this value": "残高がこの値を下回ったときに通知を受け取る",
|
||||
"Get started": "はじめる",
|
||||
"Get Started": "開始する",
|
||||
"GitHub": "GitHub",
|
||||
"Give the group a recognizable name and optional description.": "グループに認識しやすい名前とオプションの説明を付けます。",
|
||||
@ -1856,6 +1870,7 @@
|
||||
"Group-based rate limits": "グループベースのレート制限",
|
||||
"Group:": "グループ:",
|
||||
"Group: {{ratio}}x": "グループ:{{ratio}}x",
|
||||
"Grouped monitor status from Uptime Kuma": "Uptime Kuma からのグループ別監視状態",
|
||||
"Groups": "グループ",
|
||||
"Groups *": "グループ *",
|
||||
"Groups that users can select when creating API keys.": "ユーザーが API キー作成時に選択できるグループ。",
|
||||
@ -1884,6 +1899,7 @@
|
||||
"Hidden — verify to reveal": "非表示 — 確認して表示",
|
||||
"Hide": "非表示にする",
|
||||
"Hide API key": "APIキーを非表示",
|
||||
"Hide setup guide": "セットアップガイドを非表示",
|
||||
"High Performance": "高パフォーマンス",
|
||||
"High-risk operation confirmation": "高リスク操作の確認",
|
||||
"High-risk status code retry confirmation text": "確認します",
|
||||
@ -1990,6 +2006,7 @@
|
||||
"Input tokens": "入力トークン",
|
||||
"Input Tokens": "入力トークン",
|
||||
"Inset": "インセット",
|
||||
"Inspect requests, errors, and billing details": "リクエスト、エラー、請求詳細を確認",
|
||||
"Inspect user prompts": "ユーザープロンプトの検査",
|
||||
"Instance": "インスタンス",
|
||||
"Integrations": "統合",
|
||||
@ -2063,8 +2080,10 @@
|
||||
"JustSong": "JustSong",
|
||||
"K": "K",
|
||||
"Keep enabled if you need to proxy requests for different upstream accounts.": "異なる上流アカウントのリクエストをプロキシする必要がある場合は有効にしたままにしてください。",
|
||||
"Keep enough balance before production traffic": "本番トラフィック前に十分な残高を確保",
|
||||
"Keep original value": "元の値を保持",
|
||||
"Keep original value (skip if target exists)": "元の値を保持(ターゲットが存在する場合はスキップ)",
|
||||
"Keep the platform ready": "プラットフォームを準備状態に保つ",
|
||||
"Keep this above 1 minute to avoid heavy database load": "データベースへの負荷を避けるため、これを1分以上に保ってください",
|
||||
"Keep-alive Ping": "キープアライブPing",
|
||||
"Key": "キー",
|
||||
@ -2090,7 +2109,11 @@
|
||||
"Last updated:": "最終更新日:",
|
||||
"Last Used": "最終使用",
|
||||
"Last used:": "最終使用日:",
|
||||
"Latency": "レイテンシ",
|
||||
"Latency check": "レイテンシ確認",
|
||||
"Latency short": "遅延",
|
||||
"Latency trend (last 24h)": "レイテンシ推移(直近 24 時間)",
|
||||
"Latest platform updates and notices": "最新のプラットフォーム更新と通知",
|
||||
"Lavender Dream": "ラベンダードリーム",
|
||||
"Layout": "レイアウト",
|
||||
"lead": "リード",
|
||||
@ -2141,6 +2164,7 @@
|
||||
"Load Balancing": "ロードバランシング",
|
||||
"Load template...": "テンプレートをロード...",
|
||||
"Loader": "ローダー",
|
||||
"Loading": "読み込み中",
|
||||
"Loading configuration": "設定を読み込んでいます",
|
||||
"Loading content settings...": "コンテンツ設定をロード中...",
|
||||
"Loading current models...": "現在のモデルをロード中...",
|
||||
@ -2314,6 +2338,7 @@
|
||||
"Model ratios reset successfully": "モデル比率が正常にリセットされました",
|
||||
"Model Regex": "モデル正規表現",
|
||||
"Model Regex (one per line)": "モデル正規表現(1行に1つ)",
|
||||
"Model selected": "選択済みモデル",
|
||||
"Model Square": "モデル広場",
|
||||
"Model Tags": "モデルタグ",
|
||||
"Model to use for testing": "テストに使用するモデル",
|
||||
@ -2344,6 +2369,7 @@
|
||||
"Module availability": "モジュールの可用性",
|
||||
"MokaAI": "MokaAI",
|
||||
"Monitor": "モニタリング",
|
||||
"Monitor balance, usage, and request volume": "残高、使用量、リクエスト数を監視",
|
||||
"Monitoring & Alerts": "監視とアラート",
|
||||
"Month": "月",
|
||||
"Month number": "月番号",
|
||||
@ -2406,6 +2432,7 @@
|
||||
"name@example.com": "name@example.com",
|
||||
"Native format": "ネイティブ形式",
|
||||
"Need a code?": "コードが必要ですか?",
|
||||
"Needs API key": "API キーが必要",
|
||||
"Nested JSON defining per-group rules for adding (+:), removing (-:), or appending usable groups.": "追加 (+:)、削除 (-:)、または使用可能なグループの追加を行うグループごとのルールを定義するネストされたJSON。",
|
||||
"Nested JSON: source group →": "ネストされたJSON: ソースグループ →",
|
||||
"Network proxy for this channel (supports socks5 protocol)": "このチャネルのネットワークプロキシ (socks5プロトコルをサポート)",
|
||||
@ -2438,6 +2465,7 @@
|
||||
"No announcements at this time": "現在のお知らせはありません",
|
||||
"No announcements yet. Click \"Add Announcement\" to create one.": "お知らせはまだありません。「お知らせを追加」をクリックして作成してください。",
|
||||
"No API Domains yet. Click \"Add API\" to create one.": "まだAPIドメインはありません。「APIを追加」をクリックして作成してください。",
|
||||
"No API key yet": "API キーはまだありません",
|
||||
"No API keys available. Create your first API key to get started.": "利用可能なAPIキーがありません。最初のAPIキーを作成して開始してください。",
|
||||
"No API Keys Found": "APIキーが見つかりません",
|
||||
"No API routes configured": "APIルートが設定されていません",
|
||||
@ -2632,6 +2660,7 @@
|
||||
"One IP or CIDR range per line": "1行に1つのIPまたはCIDR範囲",
|
||||
"One IP per line (empty for no restriction)": "1行に1つのIP (制限なしの場合は空欄)",
|
||||
"one keyword per line": "1行に1つのキーワード",
|
||||
"Online": "オンライン",
|
||||
"Online payment is not enabled. Please contact the administrator.": "オンライン決済が有効になっていません。管理者にお問い合わせください。",
|
||||
"Online topup is not enabled. Please use redemption code or contact administrator.": "オンラインチャージは有効になっていません。引き換えコードを使用するか、管理者に連絡してください。",
|
||||
"Only allow specific email domains": "特定のEメール ドメインのみを許可する",
|
||||
@ -3082,6 +3111,7 @@
|
||||
"Raw Quota": "元のクォータ",
|
||||
"Re-enable on success": "成功時に再有効化",
|
||||
"Re-login": "再ログイン",
|
||||
"Ready": "準備完了",
|
||||
"Ready to initialize": "初期化準備完了",
|
||||
"Ready to simplify": "シンプルにする準備は",
|
||||
"Real exchange rate between USD and your payment gateway currency": "USDと決済ゲートウェイ通貨の間の実質為替レート",
|
||||
@ -3097,6 +3127,7 @@
|
||||
"Recharge Amount": "チャージ額",
|
||||
"Recharge Amount (USD)": "チャージ額 (USD)",
|
||||
"Recommended": "推奨",
|
||||
"Recommended actions": "おすすめの操作",
|
||||
"Recommended to keep this high to avoid upstream throttling.": "アップストリームのスロットリングを避けるため、これを高く保つことを推奨します。",
|
||||
"Record IP Address": "IPアドレスを記録",
|
||||
"Record quota usage": "クォータ使用量を記録",
|
||||
@ -3281,6 +3312,7 @@
|
||||
"Revenue": "収益",
|
||||
"Review & initialize": "確認して初期化",
|
||||
"Review current version and fetch release notes.": "現在のバージョンを確認し、リリースノートを取得します。",
|
||||
"Review model rates before scaling traffic": "トラフィック拡大前にモデル料金を確認",
|
||||
"Review your payment details": "支払い詳細を確認",
|
||||
"Review your purchase details before proceeding.": "続行前に購入詳細を確認してください。",
|
||||
"Rewards will be added directly to your balance": "報酬は直接残高に追加されます",
|
||||
@ -3291,8 +3323,10 @@
|
||||
"Root": "ルート",
|
||||
"Rose Garden": "ローズガーデン",
|
||||
"Route": "ルート",
|
||||
"Route active": "ルート有効",
|
||||
"Route Description": "ルートの説明",
|
||||
"Route is required": "ルートは必須です",
|
||||
"Route, auth, and balance check in one place": "ルート、認証、残高確認を一か所に集約",
|
||||
"Routing & Overrides": "ルーティングと上書き",
|
||||
"Routing Strategy": "ルーティング戦略",
|
||||
"Rows per page": "ページあたりの行数",
|
||||
@ -3401,6 +3435,7 @@
|
||||
"Secret environment variables (JSON)": "シークレット環境変数(JSON)",
|
||||
"Secret Key": "シークレットキー",
|
||||
"Secure & Reliable": "セキュア&信頼性",
|
||||
"Secured": "保護済み",
|
||||
"Security": "セキュリティ",
|
||||
"Security & Limits": "セキュリティと制限",
|
||||
"Security Check": "セキュリティチェック",
|
||||
@ -3480,6 +3515,7 @@
|
||||
"Selected when creating a token and used as the default billing group for API calls.": "トークン作成時に選択され、API 呼び出しのデフォルト課金グループとして使われます。",
|
||||
"Self-Use Mode": "セルフユースモード",
|
||||
"Send": "送信",
|
||||
"Send a request": "リクエストを送信",
|
||||
"Send code": "コードを送信",
|
||||
"Send email alerts when a user falls below this quota": "ユーザーがこのクォータを下回ったときにメールアラートを送信",
|
||||
"Sending...": "送信中...",
|
||||
@ -3520,7 +3556,11 @@
|
||||
"Settings": "設定",
|
||||
"Settings & Preferences": "設定と環境設定",
|
||||
"Settings updated successfully": "設定が正常に更新されました",
|
||||
"Setup guide": "セットアップガイド",
|
||||
"Setup guide complete": "セットアップガイド完了",
|
||||
"Setup guide is collapsed. Expand it anytime.": "セットアップガイドは折りたたまれています。いつでも展開できます。",
|
||||
"Setup Instructions": "セットアップ手順",
|
||||
"Setup progress: {{completed}}/{{total}}": "セットアップ進捗: {{completed}}/{{total}}",
|
||||
"Setup Two-Factor Authentication": "2要素認証を設定",
|
||||
"Share": "シェア",
|
||||
"Share your link and earn rewards": "リンクを共有して報酬を獲得",
|
||||
@ -3531,6 +3571,7 @@
|
||||
"Show all providers including unbound": "未バインドを含むすべてのプロバイダーを表示",
|
||||
"Show only bound providers": "バインド済みのプロバイダーのみ表示",
|
||||
"Show prices in currency instead of quota.": "クォータではなく通貨で価格を表示。",
|
||||
"Show setup guide": "セットアップガイドを表示",
|
||||
"Show token usage statistics in the UI": "UIでトークン使用統計を表示",
|
||||
"Showcase core capabilities with demo credentials and limited access.": "デモ用の認証情報と制限付きアクセスでコア機能を紹介します。",
|
||||
"Showing": "表示",
|
||||
@ -3605,11 +3646,11 @@
|
||||
"Statistical tokens": "統計トークン",
|
||||
"Statistics reset": "統計をリセットしました",
|
||||
"Status": "ステータス",
|
||||
"Status short": "状態",
|
||||
"Status & Sync": "ステータスと同期",
|
||||
"Status Code": "ステータスコード",
|
||||
"Status Code Mapping": "ステータスコードマッピング",
|
||||
"Status Page Slug": "ステータスページスラッグ",
|
||||
"Status short": "状態",
|
||||
"Status:": "ステータス:",
|
||||
"Stay": "残る",
|
||||
"Stay tuned though!": "引き続きご期待ください!",
|
||||
@ -3754,6 +3795,7 @@
|
||||
"Test Latency": "レイテンシをテスト",
|
||||
"Test Mode": "テストモード",
|
||||
"Test Model": "モデルをテスト",
|
||||
"Test models and prompts from the browser": "ブラウザでモデルとプロンプトをテスト",
|
||||
"Testing all enabled channels started. Please refresh to see results.": "有効な全チャネルのテストを開始しました。結果を確認するにはページを更新してください。",
|
||||
"Testing...": "テスト中...",
|
||||
"Text": "テキスト",
|
||||
@ -3837,8 +3879,8 @@
|
||||
"This year": "今年",
|
||||
"Three steps to get started": "3ステップで始める",
|
||||
"Throughput": "スループット",
|
||||
"Throughput short": "TPS",
|
||||
"Throughput by group": "グループ別スループット",
|
||||
"Throughput short": "TPS",
|
||||
"Throughput trend": "スループット推移",
|
||||
"Tier": "ティア",
|
||||
"Tier conditions": "階層条件",
|
||||
@ -4092,6 +4134,7 @@
|
||||
"URL is required": "URL は必須です",
|
||||
"URL to your logo image (optional)": "ロゴ画像のURL (オプション)",
|
||||
"Usage": "使用量",
|
||||
"Usage at a glance": "使用状況の概要",
|
||||
"Usage guide": "使用ガイド",
|
||||
"Usage logs": "使用ログ",
|
||||
"Usage Logs": "利用履歴",
|
||||
@ -4189,6 +4232,7 @@
|
||||
"Verification required to reveal the saved key.": "保存されたキーを表示するには、認証が必要です。",
|
||||
"Verify": "認証",
|
||||
"Verify and Sign In": "確認してサインイン",
|
||||
"Verify routing with Playground or your client": "Playground またはクライアントでルーティングを確認",
|
||||
"Verify Setup": "設定を確認",
|
||||
"Verify your database connection": "データベース接続を確認",
|
||||
"Version Overrides": "バージョンオーバーライド",
|
||||
@ -4346,6 +4390,7 @@
|
||||
"Your GitHub OAuth Client Secret": "あなたのGitHub OAuthクライアントシークレット",
|
||||
"Your new backup codes are ready": "新しいバックアップコードの準備ができました",
|
||||
"Your Referral Link": "あなたの紹介リンク",
|
||||
"Your setup guide is collapsed so usage stays in focus.": "利用状況に集中できるよう、セットアップガイドを折りたたみました。",
|
||||
"Your system access token for API authentication. Keep it secure and don't share it with others.": "API認証用のシステムアクセストークンです。安全に保管し、他者と共有しないでください。",
|
||||
"Your Telegram Bot Token": "あなたのTelegramボットトークン",
|
||||
"Your Turnstile secret key": "あなたのTurnstileシークレットキー",
|
||||
|
||||
53
web/default/src/i18n/locales/ru.json
vendored
53
web/default/src/i18n/locales/ru.json
vendored
@ -98,6 +98,7 @@
|
||||
"7 days ago": "7 дней назад",
|
||||
"80,443,8080": "80,443,8080",
|
||||
"A billing multiplier. Lower ratios mean lower API call costs.": "Множитель тарификации. Чем ниже коэффициент, тем ниже стоимость вызовов API.",
|
||||
"A focused home for keys, balance, routing, and service health.": "Единый экран для ключей, баланса, маршрутов и состояния сервиса.",
|
||||
"About": "О проекте",
|
||||
"Accept Unpriced Models": "Принимать модели без цены",
|
||||
"Accepts a JSON array of model identifiers that support the Imagine API.": "Принимает JSON-массив идентификаторов моделей, поддерживающих Imagine API.",
|
||||
@ -145,6 +146,7 @@
|
||||
"Add chat preset": "Добавить предустановку чата",
|
||||
"Add condition": "Добавить условие",
|
||||
"Add Condition": "Добавить условие",
|
||||
"Add credits": "Добавить средства",
|
||||
"Add custom model(s), comma-separated": "Добавить пользовательскую модель(и), через запятую",
|
||||
"Add discount tier": "Добавить уровень скидки",
|
||||
"Add each model or tag you want to include.": "Добавьте каждую модель или тег, который хотите включить.",
|
||||
@ -311,6 +313,7 @@
|
||||
"Announcements": "Объявления",
|
||||
"Announcements saved successfully": "Объявления успешно сохранены",
|
||||
"Answer": "Ответ",
|
||||
"Answers for common access and billing questions": "Ответы на частые вопросы о доступе и оплате",
|
||||
"Anthropic": "Anthropic",
|
||||
"Any Match (OR)": "Любое совпадение (OR)",
|
||||
"API": "API",
|
||||
@ -411,6 +414,7 @@
|
||||
"Audio Preview": "Предпросмотр аудио",
|
||||
"Audio ratio": "Коэффициент аудио",
|
||||
"Audio Tokens": "Аудио токены",
|
||||
"Auth configured": "Аутентификация настроена",
|
||||
"Auth Style": "Стиль аутентификации",
|
||||
"Authentication": "Аутентификация",
|
||||
"Authenticator code": "Код аутентификатора",
|
||||
@ -446,8 +450,6 @@
|
||||
"Available Models": "Доступные модели",
|
||||
"Available Rewards": "Доступные награды",
|
||||
"Average latency": "Средняя задержка",
|
||||
"Latency": "Задержка",
|
||||
"Latency short": "Зад.",
|
||||
"Average latency, TTFT, and success rate by group": "Средняя задержка, TTFT и доля успешных запросов по группам",
|
||||
"Average RPM": "Среднее число оборотов в минуту",
|
||||
"Average time-to-first-token (TTFT) by group": "Среднее время до первого токена (TTFT) по группам",
|
||||
@ -475,6 +477,7 @@
|
||||
"Baidu V2": "Baidu V2",
|
||||
"Balance": "Баланс",
|
||||
"Balance and top-up management": "Управление балансом и пополнением",
|
||||
"Balance is shown in quota units": "Баланс показан в единицах квоты",
|
||||
"Balance queried successfully": "Баланс успешно запрошен",
|
||||
"Balance updated successfully": "Баланс успешно обновлён",
|
||||
"Balance updated: {{balance}}": "Баланс обновлён: {{balance}}",
|
||||
@ -549,11 +552,13 @@
|
||||
"Broadcast a global banner to users. Markdown is supported.": "Транслировать глобальный баннер пользователям. Поддерживается Markdown.",
|
||||
"Broadcast short system notices on the dashboard": "Транслировать короткие системные уведомления на панели управления",
|
||||
"Browse and compare": "Просмотр и сравнение",
|
||||
"Browse available models and pricing": "Просмотрите доступные модели и цены",
|
||||
"Browse rankings by category": "Просмотр рейтингов по категориям",
|
||||
"Budget tokens = max tokens × ratio. Accepts a decimal between 0.002 and 1. Recommended to keep aligned with upstream billing.": "Бюджетные токены = макс. токены × соотношение. Принимает десятичное число от 0.002 до 1. Рекомендуется поддерживать в соответствии с биллингом вышестоящего провайдера.",
|
||||
"Budget tokens = max tokens × ratio. Accepts a decimal between 0.1 and 1.": "Бюджетные токены = макс. токены × соотношение. Принимает десятичное число от 0.1 до 1.",
|
||||
"Budget Tokens Ratio": "Соотношение бюджетных токенов",
|
||||
"Budgets": "Бюджеты",
|
||||
"Build on your API gateway in minutes": "Начните работу с API-шлюзом за несколько минут",
|
||||
"Built for developers,": "Создано для разработчиков,",
|
||||
"Built-in": "Встроенный",
|
||||
"Built-in Device": "Встроенное устройство",
|
||||
@ -846,6 +851,7 @@
|
||||
"Configure xAI Grok model specific settings": "Настроить параметры модели xAI Grok",
|
||||
"Configure your account behavior preferences": "Настроить предпочтения поведения вашей учетной записи",
|
||||
"Configure your account preferences and integrations": "Настроить параметры и интеграции вашей учетной записи",
|
||||
"Configured routes and latency checks": "Настроенные маршруты и проверки задержки",
|
||||
"Confirm": "Подтверждение",
|
||||
"Confirm Action": "Подтвердить действие",
|
||||
"Confirm Batch Update": "Подтвердить пакетное обновление",
|
||||
@ -937,6 +943,7 @@
|
||||
"Copy model name": "Скопировать имя модели",
|
||||
"Copy model names": "Скопировать имена моделей",
|
||||
"Copy prompt": "Копировать промпт",
|
||||
"Copy ready-to-run curl": "Скопировать готовый curl",
|
||||
"Copy redemption code": "Скопировать код активации",
|
||||
"Copy referral link": "Скопировать реферальную ссылку",
|
||||
"Copy Request Header": "Копировать заголовок запроса",
|
||||
@ -962,9 +969,11 @@
|
||||
"CPU Threshold (%)": "Порог CPU (%)",
|
||||
"Create": "Создать",
|
||||
"Create a copy of:": "Создать копию:",
|
||||
"Create a key for your app or service": "Создайте ключ для приложения или сервиса",
|
||||
"Create a new user group to configure ratio overrides for.": "Создайте новую группу пользователей для настройки переопределений соотношений.",
|
||||
"Create account": "Создать аккаунт",
|
||||
"Create an account": "Создать аккаунт",
|
||||
"Create an API key to unlock the real request": "Создайте API-ключ, чтобы открыть реальный запрос",
|
||||
"Create and review invite or credit codes.": "Создать и просмотреть коды приглашений или кредитов.",
|
||||
"Create API Key": "Создать ключ API",
|
||||
"Create cache": "Создать кеш",
|
||||
@ -996,6 +1005,7 @@
|
||||
"Credential generated": "Учётные данные созданы",
|
||||
"Credential refreshed": "Учётные данные обновлены",
|
||||
"Credentials": "Учетные данные",
|
||||
"Credit remaining": "Остаток средств",
|
||||
"Creem API key (leave blank unless updating)": "Ключ API Creem (оставьте пустым, если не обновляете)",
|
||||
"Creem Gateway": "Шлюз Creem",
|
||||
"Creem Payment": "Платеж Creem",
|
||||
@ -1009,6 +1019,7 @@
|
||||
"Current Balance": "Текущий баланс",
|
||||
"Current Billing": "Текущие счета",
|
||||
"Current Cache Size": "Текущий размер кэша",
|
||||
"Current domain": "Текущий домен",
|
||||
"Current email: {{email}}. Enter a new email to change.": "Текущий email: {{email}}. Введите новый email для изменения.",
|
||||
"Current key": "Текущий ключ",
|
||||
"Current legacy JSON is invalid, cannot append": "Текущий JSON старого формата невалиден, добавление невозможно",
|
||||
@ -1211,6 +1222,7 @@
|
||||
"Display name shown to users.": "Отображаемое название для пользователей.",
|
||||
"Display Options": "Параметры отображения",
|
||||
"Display Token Statistics": "Показать статистику токенов",
|
||||
"Displayed in": "Отображается в",
|
||||
"Displays the mobile sidebar.": "Отображает мобильную боковую панель.",
|
||||
"Do not over-trust this feature. IP may be spoofed. Please use with nginx, CDN and other gateways.": "Не доверяйте этой функции слишком сильно. IP может быть подделан. Используйте с nginx, CDN и другими шлюзами.",
|
||||
"Do not repeat check-in; only once per day": "Не повторяйте отметку; только один раз в день",
|
||||
@ -1714,6 +1726,7 @@
|
||||
"Finance": "Финансы",
|
||||
"Fine-tune Midjourney integration and guardrails.": "Тонкая настройка интеграции Midjourney и защитных механизмов.",
|
||||
"Finish Time": "Время завершения",
|
||||
"First API request": "Первый API-запрос",
|
||||
"First/Last Frame to Video": "Первый/последний кадр в видео",
|
||||
"Fix Abilities": "Исправить способности",
|
||||
"Fixed abilities: {{success}} succeeded, {{fails}} failed": "Исправление способностей: {{success}} успешно, {{fails}} неудачно",
|
||||
@ -1800,6 +1813,7 @@
|
||||
"Generation quality preset": "Пресет качества генерации",
|
||||
"Generic cache": "Общий кэш",
|
||||
"Get notified when balance falls below this value": "Получать уведомления, когда баланс опускается ниже этого значения",
|
||||
"Get started": "Начало работы",
|
||||
"Get Started": "Начать",
|
||||
"GitHub": "GitHub",
|
||||
"Give the group a recognizable name and optional description.": "Дайте группе узнаваемое имя и необязательное описание.",
|
||||
@ -1856,6 +1870,7 @@
|
||||
"Group-based rate limits": "Лимиты скорости на основе групп",
|
||||
"Group:": "Группа:",
|
||||
"Group: {{ratio}}x": "Группа: {{ratio}}x",
|
||||
"Grouped monitor status from Uptime Kuma": "Состояние групп мониторинга из Uptime Kuma",
|
||||
"Groups": "Группы",
|
||||
"Groups *": "Группы *",
|
||||
"Groups that users can select when creating API keys.": "Группы, которые пользователи могут выбрать при создании ключей API.",
|
||||
@ -1884,6 +1899,7 @@
|
||||
"Hidden — verify to reveal": "Скрыто — подтвердите, чтобы показать",
|
||||
"Hide": "Скрыть",
|
||||
"Hide API key": "Скрыть API ключ",
|
||||
"Hide setup guide": "Скрыть руководство по настройке",
|
||||
"High Performance": "Высокая производительность",
|
||||
"High-risk operation confirmation": "Подтверждение высокорисковой операции",
|
||||
"High-risk status code retry confirmation text": "Я ПОДТВЕРЖДАЮ",
|
||||
@ -1990,6 +2006,7 @@
|
||||
"Input tokens": "Входные токены",
|
||||
"Input Tokens": "Входные токены",
|
||||
"Inset": "Встроенная",
|
||||
"Inspect requests, errors, and billing details": "Проверяйте запросы, ошибки и детали оплаты",
|
||||
"Inspect user prompts": "Просмотр запросов пользователя",
|
||||
"Instance": "Экземпляр",
|
||||
"Integrations": "Интеграции",
|
||||
@ -2063,8 +2080,10 @@
|
||||
"JustSong": "JustSong",
|
||||
"K": "K",
|
||||
"Keep enabled if you need to proxy requests for different upstream accounts.": "Оставьте включённым, если нужно проксировать запросы для разных upstream-аккаунтов.",
|
||||
"Keep enough balance before production traffic": "Поддерживайте достаточный баланс перед рабочим трафиком",
|
||||
"Keep original value": "Сохранить исходное значение",
|
||||
"Keep original value (skip if target exists)": "Сохранить исходное значение (пропустить если цель существует)",
|
||||
"Keep the platform ready": "Поддерживайте платформу в готовности",
|
||||
"Keep this above 1 minute to avoid heavy database load": "Держите это значение выше 1 минуты, чтобы избежать высокой нагрузки на базу данных",
|
||||
"Keep-alive Ping": "Пинг Keep-alive",
|
||||
"Key": "Ключ",
|
||||
@ -2090,7 +2109,11 @@
|
||||
"Last updated:": "Последнее обновление:",
|
||||
"Last Used": "Последнее использование",
|
||||
"Last used:": "Последнее использование:",
|
||||
"Latency": "Задержка",
|
||||
"Latency check": "Проверка задержки",
|
||||
"Latency short": "Зад.",
|
||||
"Latency trend (last 24h)": "Тренд задержки (за 24 часа)",
|
||||
"Latest platform updates and notices": "Последние обновления и уведомления платформы",
|
||||
"Lavender Dream": "Лавандовая мечта",
|
||||
"Layout": "Макет",
|
||||
"lead": "лидер",
|
||||
@ -2141,6 +2164,7 @@
|
||||
"Load Balancing": "Балансировка нагрузки",
|
||||
"Load template...": "Загрузить шаблон...",
|
||||
"Loader": "Загрузчик",
|
||||
"Loading": "Загрузка",
|
||||
"Loading configuration": "Загрузка конфигурации",
|
||||
"Loading content settings...": "Загрузка настроек контента...",
|
||||
"Loading current models...": "Загрузка текущих моделей...",
|
||||
@ -2314,6 +2338,7 @@
|
||||
"Model ratios reset successfully": "Соотношения моделей успешно сброшены",
|
||||
"Model Regex": "Регулярное выражение модели",
|
||||
"Model Regex (one per line)": "Регулярное выражение модели (по одному на строку)",
|
||||
"Model selected": "Модель выбрана",
|
||||
"Model Square": "Витрина моделей",
|
||||
"Model Tags": "Теги моделей",
|
||||
"Model to use for testing": "Модель для использования при тестировании",
|
||||
@ -2344,6 +2369,7 @@
|
||||
"Module availability": "Доступность модуля",
|
||||
"MokaAI": "MokaAI",
|
||||
"Monitor": "Мониторинг",
|
||||
"Monitor balance, usage, and request volume": "Отслеживайте баланс, расход и объем запросов",
|
||||
"Monitoring & Alerts": "Мониторинг и оповещения",
|
||||
"Month": "Месяц",
|
||||
"Month number": "Номер месяца",
|
||||
@ -2406,6 +2432,7 @@
|
||||
"name@example.com": "name@example.com",
|
||||
"Native format": "Собственный формат",
|
||||
"Need a code?": "Нужен код?",
|
||||
"Needs API key": "Нужен API-ключ",
|
||||
"Nested JSON defining per-group rules for adding (+:), removing (-:), or appending usable groups.": "Вложенный JSON, определяющий правила для каждой группы для добавления (+:), удаления (-:) или добавления используемых групп.",
|
||||
"Nested JSON: source group →": "Вложенный JSON: исходная группа →",
|
||||
"Network proxy for this channel (supports socks5 protocol)": "Сетевой прокси для этого канала (поддерживает протокол socks5)",
|
||||
@ -2438,6 +2465,7 @@
|
||||
"No announcements at this time": "Нет объявлений на данный момент",
|
||||
"No announcements yet. Click \"Add Announcement\" to create one.": "Пока нет объявлений. Нажмите \"Добавить объявление\", чтобы создать одно.",
|
||||
"No API Domains yet. Click \"Add API\" to create one.": "Пока нет доменов API. Нажмите \"Добавить API\", чтобы создать один.",
|
||||
"No API key yet": "API-ключ пока не создан",
|
||||
"No API keys available. Create your first API key to get started.": "Нет доступных ключей API. Создайте свой первый ключ API, чтобы начать.",
|
||||
"No API Keys Found": "Ключи API не найдены",
|
||||
"No API routes configured": "Нет настроенных маршрутов API",
|
||||
@ -2632,6 +2660,7 @@
|
||||
"One IP or CIDR range per line": "Один IP или диапазон CIDR на строку",
|
||||
"One IP per line (empty for no restriction)": "Один IP на строку (пусто для отсутствия ограничений)",
|
||||
"one keyword per line": "одно ключевое слово на строку",
|
||||
"Online": "Онлайн",
|
||||
"Online payment is not enabled. Please contact the administrator.": "Онлайн-оплата не включена. Пожалуйста, свяжитесь с администратором.",
|
||||
"Online topup is not enabled. Please use redemption code or contact administrator.": "Онлайн-пополнение не включено. Пожалуйста, используйте код активации или свяжитесь с администратором.",
|
||||
"Only allow specific email domains": "Разрешить только определенные домены электронной почты",
|
||||
@ -3082,6 +3111,7 @@
|
||||
"Raw Quota": "Исходная квота",
|
||||
"Re-enable on success": "Повторно включить при успехе",
|
||||
"Re-login": "Повторный вход",
|
||||
"Ready": "Готово",
|
||||
"Ready to initialize": "Готов к инициализации",
|
||||
"Ready to simplify": "Готовы упростить",
|
||||
"Real exchange rate between USD and your payment gateway currency": "Реальный обменный курс между USD и валютой вашего платежного шлюза",
|
||||
@ -3097,6 +3127,7 @@
|
||||
"Recharge Amount": "Сумма пополнения",
|
||||
"Recharge Amount (USD)": "Сумма пополнения (USD)",
|
||||
"Recommended": "Рекомендуется",
|
||||
"Recommended actions": "Рекомендуемые действия",
|
||||
"Recommended to keep this high to avoid upstream throttling.": "Рекомендуется поддерживать это значение высоким, чтобы избежать регулирования со стороны вышестоящего поставщика.",
|
||||
"Record IP Address": "Записывать IP-адрес",
|
||||
"Record quota usage": "Записывать использование квоты",
|
||||
@ -3281,6 +3312,7 @@
|
||||
"Revenue": "Доход",
|
||||
"Review & initialize": "Проверить и инициализировать",
|
||||
"Review current version and fetch release notes.": "Просмотреть текущую версию и получить примечания к выпуску.",
|
||||
"Review model rates before scaling traffic": "Проверьте тарифы моделей перед масштабированием трафика",
|
||||
"Review your payment details": "Проверьте свои платежные данные",
|
||||
"Review your purchase details before proceeding.": "Просмотрите детали покупки перед продолжением.",
|
||||
"Rewards will be added directly to your balance": "Награды будут добавлены напрямую в ваш баланс",
|
||||
@ -3291,8 +3323,10 @@
|
||||
"Root": "Корень",
|
||||
"Rose Garden": "Розовый сад",
|
||||
"Route": "Маршрут",
|
||||
"Route active": "Маршрут активен",
|
||||
"Route Description": "Описание маршрута",
|
||||
"Route is required": "Маршрут обязателен",
|
||||
"Route, auth, and balance check in one place": "Маршрут, аутентификация и баланс в одном месте",
|
||||
"Routing & Overrides": "Маршрутизация и переопределения",
|
||||
"Routing Strategy": "Стратегия маршрутизации",
|
||||
"Rows per page": "Строк на страницу",
|
||||
@ -3401,6 +3435,7 @@
|
||||
"Secret environment variables (JSON)": "Секретные переменные окружения (JSON)",
|
||||
"Secret Key": "Секретный ключ",
|
||||
"Secure & Reliable": "Безопасно и надежно",
|
||||
"Secured": "Защищено",
|
||||
"Security": "Безопасность",
|
||||
"Security & Limits": "Безопасность и лимиты",
|
||||
"Security Check": "Проверка безопасности",
|
||||
@ -3480,6 +3515,7 @@
|
||||
"Selected when creating a token and used as the default billing group for API calls.": "Выбирается при создании токена и используется как группа тарификации по умолчанию для вызовов API.",
|
||||
"Self-Use Mode": "Режим самоиспользования",
|
||||
"Send": "Отправить",
|
||||
"Send a request": "Отправить запрос",
|
||||
"Send code": "Отправить код",
|
||||
"Send email alerts when a user falls below this quota": "Отправлять оповещения по электронной почте, когда пользователь опускается ниже этой квоты",
|
||||
"Sending...": "Отправка...",
|
||||
@ -3520,7 +3556,11 @@
|
||||
"Settings": "Настройки",
|
||||
"Settings & Preferences": "Настройки и предпочтения",
|
||||
"Settings updated successfully": "Настройки успешно обновлены",
|
||||
"Setup guide": "Руководство по настройке",
|
||||
"Setup guide complete": "Руководство по настройке завершено",
|
||||
"Setup guide is collapsed. Expand it anytime.": "Руководство по настройке свернуто. Его можно открыть в любой момент.",
|
||||
"Setup Instructions": "Инструкции по настройке",
|
||||
"Setup progress: {{completed}}/{{total}}": "Прогресс настройки: {{completed}}/{{total}}",
|
||||
"Setup Two-Factor Authentication": "Настроить двухфакторную аутентификацию",
|
||||
"Share": "Доля",
|
||||
"Share your link and earn rewards": "Поделитесь своей ссылкой и получайте вознаграждения",
|
||||
@ -3531,6 +3571,7 @@
|
||||
"Show all providers including unbound": "Показать всех провайдеров (включая непривязанные)",
|
||||
"Show only bound providers": "Показать только привязанных провайдеров",
|
||||
"Show prices in currency instead of quota.": "Показывать цены в валюте вместо квоты.",
|
||||
"Show setup guide": "Показать руководство по настройке",
|
||||
"Show token usage statistics in the UI": "Показывать статистику использования токенов в пользовательском интерфейсе",
|
||||
"Showcase core capabilities with demo credentials and limited access.": "Демонстрация основных возможностей с демо-учётными данными и ограниченным доступом.",
|
||||
"Showing": "Отображать",
|
||||
@ -3605,11 +3646,11 @@
|
||||
"Statistical tokens": "Статистические токены",
|
||||
"Statistics reset": "Статистика сброшена",
|
||||
"Status": "Статус",
|
||||
"Status short": "Стат.",
|
||||
"Status & Sync": "Статус и синхронизация",
|
||||
"Status Code": "Код статуса",
|
||||
"Status Code Mapping": "Сопоставление кодов состояния",
|
||||
"Status Page Slug": "Slug страницы статуса",
|
||||
"Status short": "Стат.",
|
||||
"Status:": "Статус:",
|
||||
"Stay": "Остаться",
|
||||
"Stay tuned though!": "Оставайтесь на связи!",
|
||||
@ -3754,6 +3795,7 @@
|
||||
"Test Latency": "Проверить задержку",
|
||||
"Test Mode": "Тестовый режим",
|
||||
"Test Model": "Проверить модель",
|
||||
"Test models and prompts from the browser": "Тестируйте модели и промпты в браузере",
|
||||
"Testing all enabled channels started. Please refresh to see results.": "Тестирование всех включенных каналов начато. Пожалуйста, обновите страницу, чтобы увидеть результаты.",
|
||||
"Testing...": "Тестирование...",
|
||||
"Text": "Текст",
|
||||
@ -3837,8 +3879,8 @@
|
||||
"This year": "Этот год",
|
||||
"Three steps to get started": "Три шага для начала работы",
|
||||
"Throughput": "Пропускная способность",
|
||||
"Throughput short": "TPS",
|
||||
"Throughput by group": "Пропускная способность по группам",
|
||||
"Throughput short": "TPS",
|
||||
"Throughput trend": "Тренд пропускной способности",
|
||||
"Tier": "Уровень",
|
||||
"Tier conditions": "Tier conditions",
|
||||
@ -4092,6 +4134,7 @@
|
||||
"URL is required": "URL обязателен",
|
||||
"URL to your logo image (optional)": "URL изображения вашего логотипа (необязательно)",
|
||||
"Usage": "Использование",
|
||||
"Usage at a glance": "Краткий обзор использования",
|
||||
"Usage guide": "Руководство",
|
||||
"Usage logs": "Журналы использования",
|
||||
"Usage Logs": "Журнал использования",
|
||||
@ -4189,6 +4232,7 @@
|
||||
"Verification required to reveal the saved key.": "Требуется подтверждение для отображения сохраненного ключа.",
|
||||
"Verify": "Проверить",
|
||||
"Verify and Sign In": "Подтвердить и войти",
|
||||
"Verify routing with Playground or your client": "Проверьте маршрутизацию через Playground или ваш клиент",
|
||||
"Verify Setup": "Проверить настройку",
|
||||
"Verify your database connection": "Проверьте подключение к базе данных",
|
||||
"Version Overrides": "Переопределения версий",
|
||||
@ -4346,6 +4390,7 @@
|
||||
"Your GitHub OAuth Client Secret": "Ваш секрет клиента GitHub OAuth",
|
||||
"Your new backup codes are ready": "Ваши новые резервные коды готовы",
|
||||
"Your Referral Link": "Ваша реферальная ссылка",
|
||||
"Your setup guide is collapsed so usage stays in focus.": "Руководство свернуто, чтобы основные показатели оставались в фокусе.",
|
||||
"Your system access token for API authentication. Keep it secure and don't share it with others.": "Ваш системный токен доступа для аутентификации API. Храните его в безопасности и не делитесь им с другими.",
|
||||
"Your Telegram Bot Token": "Ваш токен Telegram-бота",
|
||||
"Your Turnstile secret key": "Секретный ключ Turnstile",
|
||||
|
||||
53
web/default/src/i18n/locales/vi.json
vendored
53
web/default/src/i18n/locales/vi.json
vendored
@ -98,6 +98,7 @@
|
||||
"7 days ago": "7 ngày trước",
|
||||
"80,443,8080": "80,443,8080",
|
||||
"A billing multiplier. Lower ratios mean lower API call costs.": "Hệ số tính phí. Tỷ lệ càng thấp thì chi phí gọi API càng thấp.",
|
||||
"A focused home for keys, balance, routing, and service health.": "Trang tổng quan tập trung cho khóa, số dư, định tuyến và trạng thái dịch vụ.",
|
||||
"About": "Giới thiệu",
|
||||
"Accept Unpriced Models": "Chấp nhận các Mô hình chưa định giá",
|
||||
"Accepts a JSON array of model identifiers that support the Imagine API.": "Chấp nhận một mảng JSON gồm các mã định danh mô hình hỗ trợ API Imagine.",
|
||||
@ -145,6 +146,7 @@
|
||||
"Add chat preset": "Thêm mẫu trò chuyện",
|
||||
"Add condition": "Thêm điều kiện",
|
||||
"Add Condition": "Thêm điều kiện",
|
||||
"Add credits": "Thêm tín dụng",
|
||||
"Add custom model(s), comma-separated": "Thêm mô hình tùy chỉnh, phân tách bằng dấu phẩy",
|
||||
"Add discount tier": "Thêm bậc giảm giá",
|
||||
"Add each model or tag you want to include.": "Thêm mỗi mô hình hoặc thẻ bạn muốn đưa vào.",
|
||||
@ -311,6 +313,7 @@
|
||||
"Announcements": "Thông báo",
|
||||
"Announcements saved successfully": "Đã lưu thông báo thành công",
|
||||
"Answer": "Trả lời",
|
||||
"Answers for common access and billing questions": "Câu trả lời cho các câu hỏi thường gặp về truy cập và thanh toán",
|
||||
"Anthropic": "Anthropic",
|
||||
"Any Match (OR)": "Bất kỳ khớp (OR)",
|
||||
"API": "API",
|
||||
@ -411,6 +414,7 @@
|
||||
"Audio Preview": "Xem trước âm thanh",
|
||||
"Audio ratio": "Tỷ lệ âm thanh",
|
||||
"Audio Tokens": "Token âm thanh",
|
||||
"Auth configured": "Đã cấu hình xác thực",
|
||||
"Auth Style": "Kiểu xác thực",
|
||||
"Authentication": "Xác thực",
|
||||
"Authenticator code": "Mã xác thực",
|
||||
@ -446,8 +450,6 @@
|
||||
"Available Models": "Mô hình khả dụng",
|
||||
"Available Rewards": "Phần thưởng hiện có",
|
||||
"Average latency": "Độ trễ trung bình",
|
||||
"Latency": "Độ trễ",
|
||||
"Latency short": "Trễ",
|
||||
"Average latency, TTFT, and success rate by group": "Độ trễ trung bình, TTFT và tỷ lệ thành công theo nhóm",
|
||||
"Average RPM": "RPM trung bình",
|
||||
"Average time-to-first-token (TTFT) by group": "Thời gian trung bình tới token đầu tiên (TTFT) theo nhóm",
|
||||
@ -475,6 +477,7 @@
|
||||
"Baidu V2": "Baidu V2",
|
||||
"Balance": "Cân bằng",
|
||||
"Balance and top-up management": "Quản lý số dư và nạp tiền",
|
||||
"Balance is shown in quota units": "Số dư được hiển thị theo đơn vị hạn mức",
|
||||
"Balance queried successfully": "Truy vấn số dư thành công",
|
||||
"Balance updated successfully": "Đã cập nhật số dư thành công",
|
||||
"Balance updated: {{balance}}": "Số dư đã cập nhật: {{balance}}",
|
||||
@ -549,11 +552,13 @@
|
||||
"Broadcast a global banner to users. Markdown is supported.": "Phát một biểu ngữ toàn cầu đến người dùng. Hỗ trợ Markdown.",
|
||||
"Broadcast short system notices on the dashboard": "Phát các thông báo hệ thống ngắn trên bảng điều khiển",
|
||||
"Browse and compare": "Duyệt và so sánh",
|
||||
"Browse available models and pricing": "Duyệt mô hình khả dụng và giá",
|
||||
"Browse rankings by category": "Duyệt bảng xếp hạng theo danh mục",
|
||||
"Budget tokens = max tokens × ratio. Accepts a decimal between 0.002 and 1. Recommended to keep aligned with upstream billing.": "Số token ngân sách = số token tối đa × tỷ lệ. Chấp nhận một số thập phân từ 0.002 đến 1. Khuyến nghị nên giữ cho phù hợp với cách tính phí của nhà cung cấp.",
|
||||
"Budget tokens = max tokens × ratio. Accepts a decimal between 0.1 and 1.": "Số token ngân sách = số token tối đa × tỷ lệ. Chấp nhận một số thập phân từ 0.1 đến 1.",
|
||||
"Budget Tokens Ratio": "Tỷ lệ Mã thông báo Ngân sách",
|
||||
"Budgets": "Ngân sách",
|
||||
"Build on your API gateway in minutes": "Xây dựng trên cổng API của bạn trong vài phút",
|
||||
"Built for developers,": "Được xây dựng cho nhà phát triển,",
|
||||
"Built-in": "Tích hợp sẵn",
|
||||
"Built-in Device": "Thiết bị tích hợp",
|
||||
@ -846,6 +851,7 @@
|
||||
"Configure xAI Grok model specific settings": "Cấu hình cài đặt riêng cho mô hình xAI Grok",
|
||||
"Configure your account behavior preferences": "Cấu hình tùy chọn hành vi tài khoản của bạn",
|
||||
"Configure your account preferences and integrations": "Cấu hình các tùy chọn và tích hợp tài khoản của bạn",
|
||||
"Configured routes and latency checks": "Tuyến đã cấu hình và kiểm tra độ trễ",
|
||||
"Confirm": "Xác nhận",
|
||||
"Confirm Action": "Xác nhận hành động",
|
||||
"Confirm Batch Update": "Xác nhận Cập nhật Hàng loạt",
|
||||
@ -937,6 +943,7 @@
|
||||
"Copy model name": "Sao chép tên mô hình",
|
||||
"Copy model names": "Sao chép tên mô hình",
|
||||
"Copy prompt": "Sao chép prompt",
|
||||
"Copy ready-to-run curl": "Sao chép curl có thể chạy ngay",
|
||||
"Copy redemption code": "Sao chép mã đổi thưởng",
|
||||
"Copy referral link": "Sao chép liên kết giới thiệu",
|
||||
"Copy Request Header": "Sao chép header yêu cầu",
|
||||
@ -962,9 +969,11 @@
|
||||
"CPU Threshold (%)": "Ngưỡng CPU (%)",
|
||||
"Create": "Tạo",
|
||||
"Create a copy of:": "Tạo bản sao của:",
|
||||
"Create a key for your app or service": "Tạo khóa cho ứng dụng hoặc dịch vụ của bạn",
|
||||
"Create a new user group to configure ratio overrides for.": "Tạo một nhóm người dùng mới để cấu hình ghi đè tỷ lệ.",
|
||||
"Create account": "Tạo tài khoản",
|
||||
"Create an account": "Tạo tài khoản",
|
||||
"Create an API key to unlock the real request": "Tạo khóa API để mở yêu cầu thật",
|
||||
"Create and review invite or credit codes.": "Tạo và xem xét mã mời hoặc mã tín dụng.",
|
||||
"Create API Key": "Tạo Khóa API",
|
||||
"Create cache": "Tạo bộ nhớ đệm",
|
||||
@ -996,6 +1005,7 @@
|
||||
"Credential generated": "Đã tạo thông tin xác thực",
|
||||
"Credential refreshed": "Đã làm mới thông tin xác thực",
|
||||
"Credentials": "Thông tin xác thực",
|
||||
"Credit remaining": "Tín dụng còn lại",
|
||||
"Creem API key (leave blank unless updating)": "Khóa API Creem (để trống trừ khi cập nhật)",
|
||||
"Creem Gateway": "Cổng Creem",
|
||||
"Creem Payment": "Thanh toán Creem",
|
||||
@ -1009,6 +1019,7 @@
|
||||
"Current Balance": "Số Dư Hiện Tại",
|
||||
"Current Billing": "Thanh toán hiện tại",
|
||||
"Current Cache Size": "Kích thước bộ nhớ đệm hiện tại",
|
||||
"Current domain": "Tên miền hiện tại",
|
||||
"Current email: {{email}}. Enter a new email to change.": "Email hiện tại: {{email}}. Nhập email mới để thay đổi.",
|
||||
"Current key": "Khóa hiện tại",
|
||||
"Current legacy JSON is invalid, cannot append": "JSON định dạng cũ hiện tại không hợp lệ, không thể thêm",
|
||||
@ -1211,6 +1222,7 @@
|
||||
"Display name shown to users.": "Tên hiển thị cho người dùng.",
|
||||
"Display Options": "Tùy chọn hiển thị",
|
||||
"Display Token Statistics": "Hiển thị Thống kê Token",
|
||||
"Displayed in": "Hiển thị theo",
|
||||
"Displays the mobile sidebar.": "Hiển thị thanh bên di động.",
|
||||
"Do not over-trust this feature. IP may be spoofed. Please use with nginx, CDN and other gateways.": "Đừng tin tưởng quá mức vào tính năng này. IP có thể bị giả mạo. Hãy sử dụng cùng với nginx, CDN và các gateway khác.",
|
||||
"Do not repeat check-in; only once per day": "Không lặp lại check-in; chỉ một lần mỗi ngày",
|
||||
@ -1714,6 +1726,7 @@
|
||||
"Finance": "Tài chính",
|
||||
"Fine-tune Midjourney integration and guardrails.": "Tinh chỉnh tích hợp Midjourney và các cơ chế bảo vệ.",
|
||||
"Finish Time": "Thời gian hoàn thành",
|
||||
"First API request": "Yêu cầu API đầu tiên",
|
||||
"First/Last Frame to Video": "Khung đầu/cuối sang video",
|
||||
"Fix Abilities": "Sửa Kỹ năng",
|
||||
"Fixed abilities: {{success}} succeeded, {{fails}} failed": "Sửa khả năng: {{success}} thành công, {{fails}} thất bại",
|
||||
@ -1800,6 +1813,7 @@
|
||||
"Generation quality preset": "Mức chất lượng sinh",
|
||||
"Generic cache": "Bộ đệm chung",
|
||||
"Get notified when balance falls below this value": "Nhận thông báo khi số dư giảm xuống dưới giá trị này",
|
||||
"Get started": "Bắt đầu",
|
||||
"Get Started": "Bắt đầu",
|
||||
"GitHub": "GitHub",
|
||||
"Give the group a recognizable name and optional description.": "Đặt cho nhóm một cái tên dễ nhận biết và mô tả tùy chọn.",
|
||||
@ -1856,6 +1870,7 @@
|
||||
"Group-based rate limits": "Giới hạn tỷ lệ dựa trên nhóm",
|
||||
"Group:": "Nhóm:",
|
||||
"Group: {{ratio}}x": "Nhóm: {{ratio}}x",
|
||||
"Grouped monitor status from Uptime Kuma": "Trạng thái giám sát theo nhóm từ Uptime Kuma",
|
||||
"Groups": "Nhóm",
|
||||
"Groups *": "Nhóm *",
|
||||
"Groups that users can select when creating API keys.": "Các nhóm mà người dùng có thể chọn khi tạo khóa API.",
|
||||
@ -1884,6 +1899,7 @@
|
||||
"Hidden — verify to reveal": "Ẩn — xác minh để hiển thị",
|
||||
"Hide": "Ẩn",
|
||||
"Hide API key": "Ẩn khóa API",
|
||||
"Hide setup guide": "Ẩn hướng dẫn thiết lập",
|
||||
"High Performance": "Hiệu suất cao",
|
||||
"High-risk operation confirmation": "Xác nhận thao tác rủi ro cao",
|
||||
"High-risk status code retry confirmation text": "TÔI XÁC NHẬN",
|
||||
@ -1990,6 +2006,7 @@
|
||||
"Input tokens": "Token đầu vào",
|
||||
"Input Tokens": "Token đầu vào",
|
||||
"Inset": "Khung trong",
|
||||
"Inspect requests, errors, and billing details": "Kiểm tra yêu cầu, lỗi và chi tiết thanh toán",
|
||||
"Inspect user prompts": "Kiểm tra lời nhắc của người dùng",
|
||||
"Instance": "Phiên bản",
|
||||
"Integrations": "Tích hợp",
|
||||
@ -2063,8 +2080,10 @@
|
||||
"JustSong": "JustSong",
|
||||
"K": "K",
|
||||
"Keep enabled if you need to proxy requests for different upstream accounts.": "Giữ bật nếu bạn cần proxy yêu cầu cho các tài khoản upstream khác nhau.",
|
||||
"Keep enough balance before production traffic": "Giữ đủ số dư trước khi chạy lưu lượng production",
|
||||
"Keep original value": "Giữ giá trị gốc",
|
||||
"Keep original value (skip if target exists)": "Giữ giá trị gốc (bỏ qua nếu đích đã tồn tại)",
|
||||
"Keep the platform ready": "Giữ nền tảng luôn sẵn sàng",
|
||||
"Keep this above 1 minute to avoid heavy database load": "Giữ cái này trên 1 phút để tránh tải nặng cơ sở dữ liệu",
|
||||
"Keep-alive Ping": "Ping duy trì",
|
||||
"Key": "Khóa",
|
||||
@ -2090,7 +2109,11 @@
|
||||
"Last updated:": "Cập nhật lần cuối:",
|
||||
"Last Used": "Dùng lần cuối",
|
||||
"Last used:": "Lần cuối sử dụng:",
|
||||
"Latency": "Độ trễ",
|
||||
"Latency check": "Kiểm tra độ trễ",
|
||||
"Latency short": "Trễ",
|
||||
"Latency trend (last 24h)": "Xu hướng độ trễ (24 giờ qua)",
|
||||
"Latest platform updates and notices": "Cập nhật và thông báo nền tảng mới nhất",
|
||||
"Lavender Dream": "Mộng hoa oải hương",
|
||||
"Layout": "Bố cục",
|
||||
"lead": "dẫn đầu",
|
||||
@ -2141,6 +2164,7 @@
|
||||
"Load Balancing": "Tải cân bằng",
|
||||
"Load template...": "Tải mẫu...",
|
||||
"Loader": "Trình tải",
|
||||
"Loading": "Đang tải",
|
||||
"Loading configuration": "Đang tải cấu hình",
|
||||
"Loading content settings...": "Đang tải cài đặt nội dung...",
|
||||
"Loading current models...": "Đang tải các mô hình hiện tại...",
|
||||
@ -2314,6 +2338,7 @@
|
||||
"Model ratios reset successfully": "Tỷ lệ mô hình đã được đặt lại thành công",
|
||||
"Model Regex": "Regex mô hình",
|
||||
"Model Regex (one per line)": "Regex mô hình (mỗi dòng một mục)",
|
||||
"Model selected": "Đã chọn mô hình",
|
||||
"Model Square": "Quảng trường mô hình",
|
||||
"Model Tags": "Thẻ mô hình",
|
||||
"Model to use for testing": "Mô hình dùng để kiểm thử",
|
||||
@ -2344,6 +2369,7 @@
|
||||
"Module availability": "Khả dụng của mô-đun",
|
||||
"MokaAI": "MokaAI",
|
||||
"Monitor": "Giám sát",
|
||||
"Monitor balance, usage, and request volume": "Theo dõi số dư, mức dùng và số lượng yêu cầu",
|
||||
"Monitoring & Alerts": "Giám sát & Cảnh báo",
|
||||
"Month": "Tháng",
|
||||
"Month number": "Số tháng",
|
||||
@ -2406,6 +2432,7 @@
|
||||
"name@example.com": "name@example.com",
|
||||
"Native format": "Định dạng gốc",
|
||||
"Need a code?": "Cần mã không?",
|
||||
"Needs API key": "Cần khóa API",
|
||||
"Nested JSON defining per-group rules for adding (+:), removing (-:), or appending usable groups.": "JSON lồng nhau xác định quy tắc theo nhóm để thêm (+:), xóa (-:), hoặc nối các nhóm có thể sử dụng.",
|
||||
"Nested JSON: source group →": "JSON lồng nhau: nhóm nguồn →",
|
||||
"Network proxy for this channel (supports socks5 protocol)": "Proxy mạng cho kênh này (hỗ trợ giao thức socks5)",
|
||||
@ -2438,6 +2465,7 @@
|
||||
"No announcements at this time": "Hiện tại chưa có thông báo nào.",
|
||||
"No announcements yet. Click \"Add Announcement\" to create one.": "Chưa có thông báo nào. Nhấp vào \"Thêm thông báo\" để tạo một thông báo.",
|
||||
"No API Domains yet. Click \"Add API\" to create one.": "Chưa có Tên miền API nào. Nhấp vào \"Thêm API\" để tạo.",
|
||||
"No API key yet": "Chưa có khóa API",
|
||||
"No API keys available. Create your first API key to get started.": "Không có khóa API nào khả dụng. Tạo khóa API đầu tiên của bạn để bắt đầu.",
|
||||
"No API Keys Found": "Không tìm thấy khóa API",
|
||||
"No API routes configured": "Chưa có tuyến API nào được cấu hình",
|
||||
@ -2632,6 +2660,7 @@
|
||||
"One IP or CIDR range per line": "Một IP hoặc dải CIDR mỗi dòng",
|
||||
"One IP per line (empty for no restriction)": "Mỗi IP một dòng (để trống nếu không giới hạn)",
|
||||
"one keyword per line": "Mỗi dòng một từ khóa",
|
||||
"Online": "Trực tuyến",
|
||||
"Online payment is not enabled. Please contact the administrator.": "Thanh toán trực tuyến chưa được kích hoạt. Vui lòng liên hệ quản trị viên.",
|
||||
"Online topup is not enabled. Please use redemption code or contact administrator.": "Tính năng nạp tiền trực tuyến chưa được bật. Vui lòng sử dụng mã quy đổi hoặc liên hệ quản trị viên.",
|
||||
"Only allow specific email domains": "Chỉ cho phép các tên miền email cụ thể",
|
||||
@ -3082,6 +3111,7 @@
|
||||
"Raw Quota": "Hạn mức gốc",
|
||||
"Re-enable on success": "Kích hoạt lại khi thành công",
|
||||
"Re-login": "Đăng nhập lại",
|
||||
"Ready": "Sẵn sàng",
|
||||
"Ready to initialize": "Sẵn sàng khởi tạo",
|
||||
"Ready to simplify": "Sẵn sàng đơn giản hóa",
|
||||
"Real exchange rate between USD and your payment gateway currency": "Tỷ giá hối đoái thực giữa USD và tiền tệ của cổng thanh toán của bạn",
|
||||
@ -3097,6 +3127,7 @@
|
||||
"Recharge Amount": "Số tiền nạp",
|
||||
"Recharge Amount (USD)": "Số tiền nạp (USD)",
|
||||
"Recommended": "Đề xuất",
|
||||
"Recommended actions": "Hành động đề xuất",
|
||||
"Recommended to keep this high to avoid upstream throttling.": "Khuyến nghị giữ mức này cao để tránh điều tiết từ phía thượng nguồn.",
|
||||
"Record IP Address": "Ghi lại địa chỉ IP",
|
||||
"Record quota usage": "Ghi lại mức sử dụng hạn mức",
|
||||
@ -3281,6 +3312,7 @@
|
||||
"Revenue": "Doanh thu",
|
||||
"Review & initialize": "Xem lại và khởi tạo",
|
||||
"Review current version and fetch release notes.": "Xem xét phiên bản hiện tại và lấy ghi chú phát hành.",
|
||||
"Review model rates before scaling traffic": "Xem giá mô hình trước khi mở rộng lưu lượng",
|
||||
"Review your payment details": "Xem lại chi tiết thanh toán của bạn",
|
||||
"Review your purchase details before proceeding.": "Xem lại chi tiết mua hàng trước khi tiếp tục.",
|
||||
"Rewards will be added directly to your balance": "Phần thưởng sẽ được thêm trực tiếp vào số dư của bạn",
|
||||
@ -3291,8 +3323,10 @@
|
||||
"Root": "Gốc",
|
||||
"Rose Garden": "Vườn hoa hồng",
|
||||
"Route": "Tuyến đường",
|
||||
"Route active": "Tuyến đang hoạt động",
|
||||
"Route Description": "Mô tả lộ trình",
|
||||
"Route is required": "Đường dẫn là bắt buộc",
|
||||
"Route, auth, and balance check in one place": "Kiểm tra tuyến, xác thực và số dư ở cùng một nơi",
|
||||
"Routing & Overrides": "Định tuyến & ghi đè",
|
||||
"Routing Strategy": "Chiến lược định tuyến",
|
||||
"Rows per page": "Số hàng trên trang",
|
||||
@ -3401,6 +3435,7 @@
|
||||
"Secret environment variables (JSON)": "Biến môi trường bí mật (JSON)",
|
||||
"Secret Key": "Khóa bí mật",
|
||||
"Secure & Reliable": "An toàn & Đáng tin cậy",
|
||||
"Secured": "Đã bảo vệ",
|
||||
"Security": "Bảo mật",
|
||||
"Security & Limits": "Bảo mật & giới hạn",
|
||||
"Security Check": "Kiểm tra bảo mật",
|
||||
@ -3480,6 +3515,7 @@
|
||||
"Selected when creating a token and used as the default billing group for API calls.": "Được chọn khi tạo token và dùng làm nhóm tính phí mặc định cho các lệnh gọi API.",
|
||||
"Self-Use Mode": "Chế độ tự sử dụng",
|
||||
"Send": "Gửi",
|
||||
"Send a request": "Gửi yêu cầu",
|
||||
"Send code": "Gửi mã",
|
||||
"Send email alerts when a user falls below this quota": "Gửi cảnh báo email khi người dùng xuống dưới hạn mức này",
|
||||
"Sending...": "Đang gửi...",
|
||||
@ -3520,7 +3556,11 @@
|
||||
"Settings": "Cài đặt",
|
||||
"Settings & Preferences": "Cài đặt & Tùy chọn",
|
||||
"Settings updated successfully": "Cài đặt đã được cập nhật thành công",
|
||||
"Setup guide": "Hướng dẫn thiết lập",
|
||||
"Setup guide complete": "Đã hoàn tất hướng dẫn thiết lập",
|
||||
"Setup guide is collapsed. Expand it anytime.": "Hướng dẫn thiết lập đã được thu gọn. Bạn có thể mở lại bất cứ lúc nào.",
|
||||
"Setup Instructions": "Hướng dẫn Thiết lập",
|
||||
"Setup progress: {{completed}}/{{total}}": "Tiến độ thiết lập: {{completed}}/{{total}}",
|
||||
"Setup Two-Factor Authentication": "Thiết lập Xác thực hai yếu tố",
|
||||
"Share": "Tỉ lệ",
|
||||
"Share your link and earn rewards": "Chia sẻ liên kết của bạn và kiếm phần thưởng",
|
||||
@ -3531,6 +3571,7 @@
|
||||
"Show all providers including unbound": "Hiển thị tất cả nhà cung cấp (bao gồm chưa liên kết)",
|
||||
"Show only bound providers": "Chỉ hiển thị nhà cung cấp đã liên kết",
|
||||
"Show prices in currency instead of quota.": "Hiển thị giá bằng tiền tệ thay vì hạn ngạch.",
|
||||
"Show setup guide": "Hiển thị hướng dẫn thiết lập",
|
||||
"Show token usage statistics in the UI": "Hiển thị thống kê sử dụng token trong giao diện người dùng",
|
||||
"Showcase core capabilities with demo credentials and limited access.": "Trình diễn các tính năng cốt lõi với thông tin đăng nhập demo và quyền truy cập hạn chế.",
|
||||
"Showing": "Đang hiển thị",
|
||||
@ -3605,11 +3646,11 @@
|
||||
"Statistical tokens": "Mã thông báo thống kê",
|
||||
"Statistics reset": "Đã đặt lại thống kê",
|
||||
"Status": "Trạng thái",
|
||||
"Status short": "TT",
|
||||
"Status & Sync": "Trạng thái & Đồng bộ",
|
||||
"Status Code": "Mã trạng thái",
|
||||
"Status Code Mapping": "Ánh xạ mã trạng thái",
|
||||
"Status Page Slug": "Đường dẫn phụ trang trạng thái",
|
||||
"Status short": "TT",
|
||||
"Status:": "Trạng thái:",
|
||||
"Stay": "Ở lại",
|
||||
"Stay tuned though!": "Nhưng",
|
||||
@ -3754,6 +3795,7 @@
|
||||
"Test Latency": "Kiểm tra độ trễ",
|
||||
"Test Mode": "Chế độ thử nghiệm",
|
||||
"Test Model": "Kiểm tra Mô hình",
|
||||
"Test models and prompts from the browser": "Kiểm thử mô hình và prompt trong trình duyệt",
|
||||
"Testing all enabled channels started. Please refresh to see results.": "Bắt đầu kiểm tra tất cả các kênh đã kích hoạt. Vui lòng làm mới để xem kết quả.",
|
||||
"Testing...": "Đang kiểm tra...",
|
||||
"Text": "Văn bản",
|
||||
@ -3837,8 +3879,8 @@
|
||||
"This year": "Năm nay",
|
||||
"Three steps to get started": "Ba bước để bắt đầu",
|
||||
"Throughput": "Thông lượng",
|
||||
"Throughput short": "TPS",
|
||||
"Throughput by group": "Thông lượng theo nhóm",
|
||||
"Throughput short": "TPS",
|
||||
"Throughput trend": "Xu hướng thông lượng",
|
||||
"Tier": "Bậc",
|
||||
"Tier conditions": "Tier conditions",
|
||||
@ -4092,6 +4134,7 @@
|
||||
"URL is required": "URL là bắt buộc",
|
||||
"URL to your logo image (optional)": "URL hình ảnh logo của bạn (tùy chọn)",
|
||||
"Usage": "Sử dụng",
|
||||
"Usage at a glance": "Tổng quan mức dùng",
|
||||
"Usage guide": "Hướng dẫn sử dụng",
|
||||
"Usage logs": "Nhật ký sử dụng",
|
||||
"Usage Logs": "Nhật ký sử dụng",
|
||||
@ -4189,6 +4232,7 @@
|
||||
"Verification required to reveal the saved key.": "Yêu cầu xác minh để tiết lộ khóa đã lưu.",
|
||||
"Verify": "Kiểm tra",
|
||||
"Verify and Sign In": "Xác minh và Đăng nhập",
|
||||
"Verify routing with Playground or your client": "Xác minh định tuyến bằng Playground hoặc client của bạn",
|
||||
"Verify Setup": "Xác minh thiết lập",
|
||||
"Verify your database connection": "Xác minh kết nối cơ sở dữ liệu của bạn",
|
||||
"Version Overrides": "Ghi đè phiên bản",
|
||||
@ -4346,6 +4390,7 @@
|
||||
"Your GitHub OAuth Client Secret": "Bí mật ứng dụng OAuth của GitHub của bạn",
|
||||
"Your new backup codes are ready": "Mã dự phòng mới của bạn đã sẵn sàng",
|
||||
"Your Referral Link": "Liên kết giới thiệu của bạn",
|
||||
"Your setup guide is collapsed so usage stays in focus.": "Hướng dẫn thiết lập đã thu gọn để giữ phần sử dụng ở vị trí nổi bật.",
|
||||
"Your system access token for API authentication. Keep it secure and don't share it with others.": "Mã truy cập hệ thống của bạn để xác thực API. Hãy giữ nó an toàn và đừng chia sẻ nó với người khác.",
|
||||
"Your Telegram Bot Token": "Mã thông báo bot Telegram của bạn",
|
||||
"Your Turnstile secret key": "Khóa bí mật Turnstile của bạn",
|
||||
|
||||
53
web/default/src/i18n/locales/zh.json
vendored
53
web/default/src/i18n/locales/zh.json
vendored
@ -98,6 +98,7 @@
|
||||
"7 days ago": "7 天前",
|
||||
"80,443,8080": "80,443,8080",
|
||||
"A billing multiplier. Lower ratios mean lower API call costs.": "计费乘数,倍率越低,API 调用费用越低。",
|
||||
"A focused home for keys, balance, routing, and service health.": "集中展示密钥、余额、路由和服务健康状态。",
|
||||
"About": "关于",
|
||||
"Accept Unpriced Models": "接受未定价模型",
|
||||
"Accepts a JSON array of model identifiers that support the Imagine API.": "接受支持 Imagine API 的模型标识符的 JSON 数组。",
|
||||
@ -145,6 +146,7 @@
|
||||
"Add chat preset": "添加聊天预设",
|
||||
"Add condition": "新增条件",
|
||||
"Add Condition": "添加条件",
|
||||
"Add credits": "添加额度",
|
||||
"Add custom model(s), comma-separated": "添加自定义模型(多个以逗号分隔)",
|
||||
"Add discount tier": "添加折扣等级",
|
||||
"Add each model or tag you want to include.": "添加您想要包含的每个模型或标签。",
|
||||
@ -311,6 +313,7 @@
|
||||
"Announcements": "公告",
|
||||
"Announcements saved successfully": "公告保存成功",
|
||||
"Answer": "答案",
|
||||
"Answers for common access and billing questions": "访问与计费常见问题解答",
|
||||
"Anthropic": "Anthropic",
|
||||
"Any Match (OR)": "任一满足(OR)",
|
||||
"API": "API",
|
||||
@ -411,6 +414,7 @@
|
||||
"Audio Preview": "音乐预览",
|
||||
"Audio ratio": "音频倍率",
|
||||
"Audio Tokens": "语音 Token",
|
||||
"Auth configured": "认证已配置",
|
||||
"Auth Style": "认证方式",
|
||||
"Authentication": "身份验证",
|
||||
"Authenticator code": "身份验证器代码",
|
||||
@ -446,8 +450,6 @@
|
||||
"Available Models": "可用模型",
|
||||
"Available Rewards": "可用奖励",
|
||||
"Average latency": "平均延迟",
|
||||
"Latency": "延迟",
|
||||
"Latency short": "延迟",
|
||||
"Average latency, TTFT, and success rate by group": "各分组的平均延迟、首 Token 延迟和成功率",
|
||||
"Average RPM": "平均 RPM",
|
||||
"Average time-to-first-token (TTFT) by group": "各分组的平均首 Token 延迟(TTFT)",
|
||||
@ -475,6 +477,7 @@
|
||||
"Baidu V2": "百度 V2",
|
||||
"Balance": "余额",
|
||||
"Balance and top-up management": "余额充值管理",
|
||||
"Balance is shown in quota units": "余额以额度单位显示",
|
||||
"Balance queried successfully": "余额查询成功",
|
||||
"Balance updated successfully": "余额更新成功",
|
||||
"Balance updated: {{balance}}": "余额已更新:{{balance}}",
|
||||
@ -549,11 +552,13 @@
|
||||
"Broadcast a global banner to users. Markdown is supported.": "向用户广播全局横幅。支持 Markdown。",
|
||||
"Broadcast short system notices on the dashboard": "在仪表板上广播简短的系统通知",
|
||||
"Browse and compare": "浏览和比较",
|
||||
"Browse available models and pricing": "浏览可用模型和价格",
|
||||
"Browse rankings by category": "按行业浏览排行",
|
||||
"Budget tokens = max tokens × ratio. Accepts a decimal between 0.002 and 1. Recommended to keep aligned with upstream billing.": "预算令牌 = 最大令牌数 × 比例。接受 0.002 到 1 之间的十进制数。建议与上游计费保持一致。",
|
||||
"Budget tokens = max tokens × ratio. Accepts a decimal between 0.1 and 1.": "预算令牌 = 最大令牌数 × 比例。接受 0.1 到 1 之间的十进制数。",
|
||||
"Budget Tokens Ratio": "预算令牌比例",
|
||||
"Budgets": "预算",
|
||||
"Build on your API gateway in minutes": "几分钟内开始使用你的 API 网关",
|
||||
"Built for developers,": "为开发者打造,",
|
||||
"Built-in": "内置",
|
||||
"Built-in Device": "内置设备",
|
||||
@ -846,6 +851,7 @@
|
||||
"Configure xAI Grok model specific settings": "配置 xAI Grok 模型特定设置",
|
||||
"Configure your account behavior preferences": "配置您的账户行为偏好",
|
||||
"Configure your account preferences and integrations": "配置您的账户偏好和集成",
|
||||
"Configured routes and latency checks": "已配置路由和延迟检测",
|
||||
"Confirm": "确认",
|
||||
"Confirm Action": "确认操作",
|
||||
"Confirm Batch Update": "确认批量更新",
|
||||
@ -937,6 +943,7 @@
|
||||
"Copy model name": "复制模型名称",
|
||||
"Copy model names": "复制模型名称列表",
|
||||
"Copy prompt": "复制提示词",
|
||||
"Copy ready-to-run curl": "复制可直接运行的 curl",
|
||||
"Copy redemption code": "复制兑换码",
|
||||
"Copy referral link": "复制推荐链接",
|
||||
"Copy Request Header": "复制请求头",
|
||||
@ -962,9 +969,11 @@
|
||||
"CPU Threshold (%)": "CPU 阈值 (%)",
|
||||
"Create": "新建",
|
||||
"Create a copy of:": "创建副本:",
|
||||
"Create a key for your app or service": "为你的应用或服务创建密钥",
|
||||
"Create a new user group to configure ratio overrides for.": "创建一个新的用户分组来配置比例覆盖。",
|
||||
"Create account": "创建账户",
|
||||
"Create an account": "创建一个账户",
|
||||
"Create an API key to unlock the real request": "创建 API 密钥以解锁真实请求",
|
||||
"Create and review invite or credit codes.": "创建和审查邀请或信用代码。",
|
||||
"Create API Key": "创建 API 密钥",
|
||||
"Create cache": "创建缓存",
|
||||
@ -996,6 +1005,7 @@
|
||||
"Credential generated": "凭据已生成",
|
||||
"Credential refreshed": "凭据已刷新",
|
||||
"Credentials": "凭证",
|
||||
"Credit remaining": "剩余额度",
|
||||
"Creem API key (leave blank unless updating)": "Creem API 密钥(除非更新,否则留空)",
|
||||
"Creem Gateway": "Creem 网关",
|
||||
"Creem Payment": "Creem 支付",
|
||||
@ -1009,6 +1019,7 @@
|
||||
"Current Balance": "当前余额",
|
||||
"Current Billing": "当前计费",
|
||||
"Current Cache Size": "当前缓存大小",
|
||||
"Current domain": "当前域名",
|
||||
"Current email: {{email}}. Enter a new email to change.": "当前邮箱:{{email}}。输入新邮箱以更改。",
|
||||
"Current key": "当前密钥",
|
||||
"Current legacy JSON is invalid, cannot append": "当前旧格式 JSON 不合法,无法追加模板",
|
||||
@ -1211,6 +1222,7 @@
|
||||
"Display name shown to users.": "显示给用户的名称。",
|
||||
"Display Options": "显示选项",
|
||||
"Display Token Statistics": "显示 Token 统计信息",
|
||||
"Displayed in": "显示单位",
|
||||
"Displays the mobile sidebar.": "显示移动侧边栏。",
|
||||
"Do not over-trust this feature. IP may be spoofed. Please use with nginx, CDN and other gateways.": "请勿过度信任此功能,IP 可能被伪造,请配合 nginx 和 cdn 等网关使用",
|
||||
"Do not repeat check-in; only once per day": "请勿重复签到;每天仅一次",
|
||||
@ -1714,6 +1726,7 @@
|
||||
"Finance": "金融",
|
||||
"Fine-tune Midjourney integration and guardrails.": "微调 Midjourney 集成和防护栏。",
|
||||
"Finish Time": "完成时间",
|
||||
"First API request": "首个 API 请求",
|
||||
"First/Last Frame to Video": "首尾生视频",
|
||||
"Fix Abilities": "修复能力",
|
||||
"Fixed abilities: {{success}} succeeded, {{fails}} failed": "修复能力:{{success}} 个成功,{{fails}} 个失败",
|
||||
@ -1800,6 +1813,7 @@
|
||||
"Generation quality preset": "生成质量预设",
|
||||
"Generic cache": "通用缓存",
|
||||
"Get notified when balance falls below this value": "当余额低于此值时接收通知",
|
||||
"Get started": "开始使用",
|
||||
"Get Started": "开始使用",
|
||||
"GitHub": "GitHub",
|
||||
"Give the group a recognizable name and optional description.": "为该分组提供一个可识别的名称和可选的描述。",
|
||||
@ -1856,6 +1870,7 @@
|
||||
"Group-based rate limits": "基于分组的速率限制",
|
||||
"Group:": "分组:",
|
||||
"Group: {{ratio}}x": "分组:{{ratio}}x",
|
||||
"Grouped monitor status from Uptime Kuma": "来自 Uptime Kuma 的分组监控状态",
|
||||
"Groups": "分组",
|
||||
"Groups *": "分组 *",
|
||||
"Groups that users can select when creating API keys.": "用户在创建 API 密钥时可以选择的分组。",
|
||||
@ -1884,6 +1899,7 @@
|
||||
"Hidden — verify to reveal": "隐藏 — 验证以显示",
|
||||
"Hide": "隐藏",
|
||||
"Hide API key": "隐藏 API 密钥",
|
||||
"Hide setup guide": "隐藏设置引导",
|
||||
"High Performance": "高性能",
|
||||
"High-risk operation confirmation": "高危操作确认",
|
||||
"High-risk status code retry confirmation text": "我确认",
|
||||
@ -1990,6 +2006,7 @@
|
||||
"Input tokens": "输入 token",
|
||||
"Input Tokens": "输入 Token",
|
||||
"Inset": "内嵌",
|
||||
"Inspect requests, errors, and billing details": "查看请求、错误和计费详情",
|
||||
"Inspect user prompts": "检查用户提示",
|
||||
"Instance": "实例",
|
||||
"Integrations": "集成",
|
||||
@ -2063,8 +2080,10 @@
|
||||
"JustSong": "JustSong",
|
||||
"K": "K",
|
||||
"Keep enabled if you need to proxy requests for different upstream accounts.": "如果需要为不同上游账户代理请求,请保持启用。",
|
||||
"Keep enough balance before production traffic": "生产流量前保持充足余额",
|
||||
"Keep original value": "保留原值",
|
||||
"Keep original value (skip if target exists)": "保留原值(目标已有值时不覆盖)",
|
||||
"Keep the platform ready": "保持平台就绪",
|
||||
"Keep this above 1 minute to avoid heavy database load": "保持此值大于 1 分钟以避免数据库负载过重",
|
||||
"Keep-alive Ping": "保持连接心跳",
|
||||
"Key": "密钥",
|
||||
@ -2090,7 +2109,11 @@
|
||||
"Last updated:": "上次更新时间:",
|
||||
"Last Used": "最后使用时间",
|
||||
"Last used:": "上次使用时间:",
|
||||
"Latency": "延迟",
|
||||
"Latency check": "延迟检测",
|
||||
"Latency short": "延迟",
|
||||
"Latency trend (last 24h)": "延迟趋势(最近 24 小时)",
|
||||
"Latest platform updates and notices": "最新平台更新和通知",
|
||||
"Lavender Dream": "薰衣草梦",
|
||||
"Layout": "布局",
|
||||
"lead": "领头",
|
||||
@ -2141,6 +2164,7 @@
|
||||
"Load Balancing": "负载均衡",
|
||||
"Load template...": "加载模板...",
|
||||
"Loader": "加载器",
|
||||
"Loading": "加载中",
|
||||
"Loading configuration": "正在加载配置",
|
||||
"Loading content settings...": "正在加载内容设置...",
|
||||
"Loading current models...": "正在加载当前模型...",
|
||||
@ -2314,6 +2338,7 @@
|
||||
"Model ratios reset successfully": "模型比例重置成功",
|
||||
"Model Regex": "模型正则",
|
||||
"Model Regex (one per line)": "模型正则(每行一个)",
|
||||
"Model selected": "已选择模型",
|
||||
"Model Square": "模型广场",
|
||||
"Model Tags": "模型标签",
|
||||
"Model to use for testing": "用于测试的模型",
|
||||
@ -2344,6 +2369,7 @@
|
||||
"Module availability": "模块可用性",
|
||||
"MokaAI": "MokaAI",
|
||||
"Monitor": "监控",
|
||||
"Monitor balance, usage, and request volume": "监控余额、用量和请求量",
|
||||
"Monitoring & Alerts": "监控与警报",
|
||||
"Month": "本月",
|
||||
"Month number": "月份",
|
||||
@ -2406,6 +2432,7 @@
|
||||
"name@example.com": "name@example.com",
|
||||
"Native format": "原生格式",
|
||||
"Need a code?": "需要一个代码吗?",
|
||||
"Needs API key": "需要 API 密钥",
|
||||
"Nested JSON defining per-group rules for adding (+:), removing (-:), or appending usable groups.": "嵌套 JSON,定义按分组添加(+:)、移除(-:)或追加可用分组的规则。",
|
||||
"Nested JSON: source group →": "嵌套 JSON:源分组 →",
|
||||
"Network proxy for this channel (supports socks5 protocol)": "此渠道的网络代理(支持 socks5 协议)",
|
||||
@ -2438,6 +2465,7 @@
|
||||
"No announcements at this time": "目前暂无公告",
|
||||
"No announcements yet. Click \"Add Announcement\" to create one.": "暂无公告。点击“添加公告”来创建一个。",
|
||||
"No API Domains yet. Click \"Add API\" to create one.": "暂无 API 域。点击“添加 API”创建一个。",
|
||||
"No API key yet": "暂无 API 密钥",
|
||||
"No API keys available. Create your first API key to get started.": "没有可用的 API 密钥。创建您的第一个 API 密钥即可开始使用。",
|
||||
"No API Keys Found": "未找到 API 密钥",
|
||||
"No API routes configured": "未配置 API 路由",
|
||||
@ -2632,6 +2660,7 @@
|
||||
"One IP or CIDR range per line": "每行一个 IP 或 CIDR 范围",
|
||||
"One IP per line (empty for no restriction)": "每行一个 IP (留空表示无限制)",
|
||||
"one keyword per line": "每行一个关键词",
|
||||
"Online": "在线",
|
||||
"Online payment is not enabled. Please contact the administrator.": "管理员未开启在线支付功能,请联系管理员配置。",
|
||||
"Online topup is not enabled. Please use redemption code or contact administrator.": "尚未启用在线充值。请使用兑换码或联系管理员。",
|
||||
"Only allow specific email domains": "仅允许特定的电子邮件域名",
|
||||
@ -3082,6 +3111,7 @@
|
||||
"Raw Quota": "原生额度",
|
||||
"Re-enable on success": "成功后重新启用",
|
||||
"Re-login": "重新登录",
|
||||
"Ready": "就绪",
|
||||
"Ready to initialize": "准备初始化",
|
||||
"Ready to simplify": "准备好简化",
|
||||
"Real exchange rate between USD and your payment gateway currency": "美元与您的支付网关货币之间的实际汇率",
|
||||
@ -3097,6 +3127,7 @@
|
||||
"Recharge Amount": "充值金额",
|
||||
"Recharge Amount (USD)": "充值金额 (USD)",
|
||||
"Recommended": "推荐",
|
||||
"Recommended actions": "推荐操作",
|
||||
"Recommended to keep this high to avoid upstream throttling.": "建议保持此值较高,以避免上游限流。",
|
||||
"Record IP Address": "记录 IP 地址",
|
||||
"Record quota usage": "记录配额使用量",
|
||||
@ -3281,6 +3312,7 @@
|
||||
"Revenue": "收入",
|
||||
"Review & initialize": "审核并初始化",
|
||||
"Review current version and fetch release notes.": "查看当前版本并获取发布说明。",
|
||||
"Review model rates before scaling traffic": "扩展流量前查看模型费率",
|
||||
"Review your payment details": "查看您的付款详情",
|
||||
"Review your purchase details before proceeding.": "在继续之前,请审阅您的购买详情。",
|
||||
"Rewards will be added directly to your balance": "奖励将直接添加到您的余额",
|
||||
@ -3291,8 +3323,10 @@
|
||||
"Root": "根",
|
||||
"Rose Garden": "玫瑰花园",
|
||||
"Route": "路由",
|
||||
"Route active": "路由已启用",
|
||||
"Route Description": "路由描述",
|
||||
"Route is required": "路由为必填项",
|
||||
"Route, auth, and balance check in one place": "路由、认证和余额检查集中展示",
|
||||
"Routing & Overrides": "路由与覆盖",
|
||||
"Routing Strategy": "路由策略",
|
||||
"Rows per page": "每页行数",
|
||||
@ -3401,6 +3435,7 @@
|
||||
"Secret environment variables (JSON)": "密钥环境变量 (JSON)",
|
||||
"Secret Key": "密钥",
|
||||
"Secure & Reliable": "安全可靠",
|
||||
"Secured": "已保护",
|
||||
"Security": "安全",
|
||||
"Security & Limits": "安全与限制",
|
||||
"Security Check": "安全验证",
|
||||
@ -3480,6 +3515,7 @@
|
||||
"Selected when creating a token and used as the default billing group for API calls.": "创建令牌时选择,用作 API 调用的默认计费分组。",
|
||||
"Self-Use Mode": "自用模式",
|
||||
"Send": "发送",
|
||||
"Send a request": "发送请求",
|
||||
"Send code": "发送验证码",
|
||||
"Send email alerts when a user falls below this quota": "当用户低于此配额时发送电子邮件警报",
|
||||
"Sending...": "发送中...",
|
||||
@ -3520,7 +3556,11 @@
|
||||
"Settings": "设置",
|
||||
"Settings & Preferences": "设置与偏好",
|
||||
"Settings updated successfully": "设置更新成功",
|
||||
"Setup guide": "设置引导",
|
||||
"Setup guide complete": "设置引导已完成",
|
||||
"Setup guide is collapsed. Expand it anytime.": "设置引导已收起,可随时展开。",
|
||||
"Setup Instructions": "设置说明",
|
||||
"Setup progress: {{completed}}/{{total}}": "设置进度:{{completed}}/{{total}}",
|
||||
"Setup Two-Factor Authentication": "设置双重身份验证",
|
||||
"Share": "占比",
|
||||
"Share your link and earn rewards": "分享您的链接并赚取奖励",
|
||||
@ -3531,6 +3571,7 @@
|
||||
"Show all providers including unbound": "显示所有提供商(包括未绑定)",
|
||||
"Show only bound providers": "仅显示已绑定的提供商",
|
||||
"Show prices in currency instead of quota.": "以货币而非配额显示价格。",
|
||||
"Show setup guide": "显示设置引导",
|
||||
"Show token usage statistics in the UI": "在用户界面中显示令牌使用统计信息",
|
||||
"Showcase core capabilities with demo credentials and limited access.": "使用演示凭据和有限访问权限展示核心功能。",
|
||||
"Showing": "显示第",
|
||||
@ -3605,11 +3646,11 @@
|
||||
"Statistical tokens": "统计 Token 数",
|
||||
"Statistics reset": "统计已重置",
|
||||
"Status": "状态",
|
||||
"Status short": "状态",
|
||||
"Status & Sync": "状态与同步",
|
||||
"Status Code": "状态码",
|
||||
"Status Code Mapping": "状态码映射",
|
||||
"Status Page Slug": "状态页面 Slug",
|
||||
"Status short": "状态",
|
||||
"Status:": "状态:",
|
||||
"Stay": "留下来",
|
||||
"Stay tuned though!": "敬请期待!",
|
||||
@ -3754,6 +3795,7 @@
|
||||
"Test Latency": "测试延迟",
|
||||
"Test Mode": "测试模式",
|
||||
"Test Model": "测试模型",
|
||||
"Test models and prompts from the browser": "在浏览器中测试模型和提示词",
|
||||
"Testing all enabled channels started. Please refresh to see results.": "测试所有启用的通道已开始。请刷新以查看结果。",
|
||||
"Testing...": "测试中...",
|
||||
"Text": "文本",
|
||||
@ -3837,8 +3879,8 @@
|
||||
"This year": "本年",
|
||||
"Three steps to get started": "三步快速上手",
|
||||
"Throughput": "吞吐量",
|
||||
"Throughput short": "吞吐",
|
||||
"Throughput by group": "各分组吞吐量",
|
||||
"Throughput short": "吞吐",
|
||||
"Throughput trend": "吞吐量趋势",
|
||||
"Tier": "档位",
|
||||
"Tier conditions": "档位条件",
|
||||
@ -4092,6 +4134,7 @@
|
||||
"URL is required": "URL 为必填项",
|
||||
"URL to your logo image (optional)": "您的徽标图片 URL(可选)",
|
||||
"Usage": "用量",
|
||||
"Usage at a glance": "用量概览",
|
||||
"Usage guide": "使用教程",
|
||||
"Usage logs": "使用日志",
|
||||
"Usage Logs": "使用日志",
|
||||
@ -4189,6 +4232,7 @@
|
||||
"Verification required to reveal the saved key.": "需要验证才能显示已保存的密钥。",
|
||||
"Verify": "验证",
|
||||
"Verify and Sign In": "验证并登录",
|
||||
"Verify routing with Playground or your client": "使用 Playground 或你的客户端验证路由",
|
||||
"Verify Setup": "验证设置",
|
||||
"Verify your database connection": "验证数据库连接",
|
||||
"Version Overrides": "版本覆盖",
|
||||
@ -4346,6 +4390,7 @@
|
||||
"Your GitHub OAuth Client Secret": "您的 GitHub OAuth 客户端密钥",
|
||||
"Your new backup codes are ready": "您的新备份代码已准备就绪",
|
||||
"Your Referral Link": "您的推荐链接",
|
||||
"Your setup guide is collapsed so usage stays in focus.": "设置引导已收起,让用量信息保持在焦点位置。",
|
||||
"Your system access token for API authentication. Keep it secure and don't share it with others.": "您的系统访问令牌,用于 API 认证。请妥善保管,不要与他人分享。",
|
||||
"Your Telegram Bot Token": "您的 Telegram 机器人令牌",
|
||||
"Your Turnstile secret key": "您的 Turnstile 密钥",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user