Commit a77bdb2c by ccran

feat: 新版前端增加批量导入支持;

parent b9106f6f
...@@ -7,7 +7,7 @@ COPY web/classic/package.json ./classic/package.json ...@@ -7,7 +7,7 @@ COPY web/classic/package.json ./classic/package.json
RUN bun install --frozen-lockfile RUN bun install --frozen-lockfile
COPY ./web/default ./default COPY ./web/default ./default
COPY ./VERSION /build/VERSION COPY ./VERSION /build/VERSION
RUN cd default && DISABLE_ESLINT_PLUGIN='true' VITE_REACT_APP_VERSION=$(cat /build/VERSION) bun ../node_modules/@rsbuild/core/bin/rsbuild.js build RUN cd default && DISABLE_ESLINT_PLUGIN='true' VITE_REACT_APP_VERSION=$(cat /build/VERSION) bun run build
FROM docker.m.daocloud.io/oven/bun:1@sha256:0733e50325078969732ebe3b15ce4c4be5082f18c4ac1a0f0ca4839c2e4e42a7 AS builder-classic FROM docker.m.daocloud.io/oven/bun:1@sha256:0733e50325078969732ebe3b15ce4c4be5082f18c4ac1a0f0ca4839c2e4e42a7 AS builder-classic
...@@ -18,7 +18,7 @@ COPY web/classic/package.json ./classic/package.json ...@@ -18,7 +18,7 @@ COPY web/classic/package.json ./classic/package.json
RUN bun install --frozen-lockfile RUN bun install --frozen-lockfile
COPY ./web/classic ./classic COPY ./web/classic ./classic
COPY ./VERSION /build/VERSION COPY ./VERSION /build/VERSION
RUN cd classic && VITE_REACT_APP_VERSION=$(cat /build/VERSION) bun ../node_modules/@rsbuild/core/bin/rsbuild.js build RUN cd classic && VITE_REACT_APP_VERSION=$(cat /build/VERSION) bun run build
FROM docker.m.daocloud.io/golang:1.26.1-alpine@sha256:2389ebfa5b7f43eeafbd6be0c3700cc46690ef842ad962f6c5bd6be49ed82039 AS builder2 FROM docker.m.daocloud.io/golang:1.26.1-alpine@sha256:2389ebfa5b7f43eeafbd6be0c3700cc46690ef842ad962f6c5bd6be49ed82039 AS builder2
ENV GO111MODULE=on CGO_ENABLED=0 ENV GO111MODULE=on CGO_ENABLED=0
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
"@visactor/react-vchart": "~1.8.8", "@visactor/react-vchart": "~1.8.8",
"@visactor/vchart": "~1.8.8", "@visactor/vchart": "~1.8.8",
"@visactor/vchart-semi-theme": "~1.8.8", "@visactor/vchart-semi-theme": "~1.8.8",
"@visactor/vchart-theme-utils": "~1.8.8",
"axios": "catalog:", "axios": "catalog:",
"clsx": "catalog:", "clsx": "catalog:",
"dayjs": "catalog:", "dayjs": "catalog:",
......
...@@ -10,7 +10,7 @@ const semiUiDir = path.resolve( ...@@ -10,7 +10,7 @@ const semiUiDir = path.resolve(
path.dirname(require.resolve('@douyinfe/semi-ui')), path.dirname(require.resolve('@douyinfe/semi-ui')),
'../..', '../..',
) )
const semiUiDateFnsDir = path.resolve(semiUiDir, 'node_modules/date-fns') const semiUiDateFnsDir = path.resolve(semiUiDir, '../../date-fns')
const resolvePackageRoot = (packageName: string, baseRequire = require) => { const resolvePackageRoot = (packageName: string, baseRequire = require) => {
let current = path.dirname(baseRequire.resolve(packageName)) let current = path.dirname(baseRequire.resolve(packageName))
while (current !== path.dirname(current)) { while (current !== path.dirname(current)) {
......
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { ArrowUp01Icon } from '@hugeicons/core-free-icons'
import { HugeiconsIcon } from '@hugeicons/react'
import { type FormEvent, type ReactNode, useId, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Dialog } from '@/components/dialog'
import { Button } from '@/components/ui/button'
import { Checkbox } from '@/components/ui/checkbox'
import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
} from '@/components/ui/field'
import { Input } from '@/components/ui/input'
import { Spinner } from '@/components/ui/spinner'
type ExcelImportDialogProps = {
open: boolean
onOpenChange: (open: boolean) => void
title: string
description: string
children: ReactNode
isPending: boolean
allowOverwrite?: boolean
onImport: (file: File, overwrite: boolean) => Promise<boolean>
}
export function ExcelImportDialog(props: ExcelImportDialogProps) {
const { t } = useTranslation()
const formId = useId()
const fileInputRef = useRef<HTMLInputElement>(null)
const [file, setFile] = useState<File | null>(null)
const [overwrite, setOverwrite] = useState(false)
const reset = () => {
setFile(null)
setOverwrite(false)
if (fileInputRef.current) {
fileInputRef.current.value = ''
}
}
const handleOpenChange = (open: boolean) => {
if (!open && props.isPending) return
if (!open) reset()
props.onOpenChange(open)
}
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault()
if (!file || props.isPending) return
const success = await props.onImport(file, overwrite)
if (!success) return
reset()
props.onOpenChange(false)
}
return (
<Dialog
open={props.open}
onOpenChange={handleOpenChange}
title={props.title}
description={props.description}
showCloseButton={!props.isPending}
bodyClassName='flex flex-col gap-4'
footer={
<>
<Button
type='button'
variant='outline'
onClick={() => handleOpenChange(false)}
disabled={props.isPending}
>
{t('Cancel')}
</Button>
<Button
type='submit'
form={formId}
disabled={!file || props.isPending}
>
{props.isPending ? (
<Spinner data-icon='inline-start' />
) : (
<HugeiconsIcon
icon={ArrowUp01Icon}
data-icon='inline-start'
aria-hidden='true'
/>
)}
{props.isPending ? t('Importing...') : t('Import')}
</Button>
</>
}
>
{props.children}
<form id={formId} onSubmit={handleSubmit}>
<FieldGroup>
<Field>
<FieldLabel htmlFor={`${formId}-file`}>
{t('Excel file')}
</FieldLabel>
<Input
ref={fileInputRef}
id={`${formId}-file`}
type='file'
accept='.xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
disabled={props.isPending}
onChange={(event) =>
setFile(event.currentTarget.files?.[0] ?? null)
}
/>
<FieldDescription>
{file
? t('Selected file: {{name}}', { name: file.name })
: t(
'Select an .xlsx file to import. The maximum file size is 10 MB.'
)}
</FieldDescription>
</Field>
{props.allowOverwrite ? (
<Field orientation='horizontal' data-disabled={props.isPending}>
<Checkbox
id={`${formId}-overwrite`}
checked={overwrite}
onCheckedChange={setOverwrite}
disabled={props.isPending}
/>
<FieldLabel
htmlFor={`${formId}-overwrite`}
className='font-normal'
>
{t('Overwrite existing models')}
</FieldLabel>
</Field>
) : null}
</FieldGroup>
</form>
</Dialog>
)
}
...@@ -70,6 +70,12 @@ import { Badge } from '@/components/ui/badge' ...@@ -70,6 +70,12 @@ import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { Combobox } from '@/components/ui/combobox' import { Combobox } from '@/components/ui/combobox'
import { import {
Field,
FieldDescription,
FieldGroup,
FieldLabel,
} from '@/components/ui/field'
import {
Form, Form,
FormControl, FormControl,
FormDescription, FormDescription,
...@@ -600,6 +606,7 @@ export function ChannelMutateDrawer({ ...@@ -600,6 +606,7 @@ export function ChannelMutateDrawer({
) )
const canRevealChannelKey = currentUser?.role === ROLE.SUPER_ADMIN const canRevealChannelKey = currentUser?.role === ROLE.SUPER_ADMIN
const [fetchModelsDialogOpen, setFetchModelsDialogOpen] = useState(false) const [fetchModelsDialogOpen, setFetchModelsDialogOpen] = useState(false)
const [bulkModels, setBulkModels] = useState('')
const [channelKey, setChannelKey] = useState<string | null>(null) const [channelKey, setChannelKey] = useState<string | null>(null)
const [isChannelKeyLoading, setIsChannelKeyLoading] = useState(false) const [isChannelKeyLoading, setIsChannelKeyLoading] = useState(false)
const [isCodexCredentialRefreshing, setIsCodexCredentialRefreshing] = const [isCodexCredentialRefreshing, setIsCodexCredentialRefreshing] =
...@@ -680,6 +687,7 @@ export function ChannelMutateDrawer({ ...@@ -680,6 +687,7 @@ export function ChannelMutateDrawer({
if (!open) { if (!open) {
setChannelKey(null) setChannelKey(null)
setIsChannelKeyLoading(false) setIsChannelKeyLoading(false)
setBulkModels('')
} else if (channelId) { } else if (channelId) {
setChannelKey(null) setChannelKey(null)
} }
...@@ -1394,6 +1402,30 @@ export function ChannelMutateDrawer({ ...@@ -1394,6 +1402,30 @@ export function ChannelMutateDrawer({
[currentModelsArray, form] [currentModelsArray, form]
) )
const handleAddBulkModels = useCallback(() => {
const parsedModels = [
...new Set(
bulkModels
.split(/[\s,,;;]+/)
.map((model) => model.trim())
.filter(Boolean)
),
]
const currentModelSet = new Set(currentModelsArray)
const newModels = parsedModels.filter(
(model) => !currentModelSet.has(model)
)
if (newModels.length === 0) {
toast.info(t('No new models to add'))
return
}
updateModels(newModels, true)
setBulkModels('')
toast.success(t('Added {{count}} model(s)', { count: newModels.length }))
}, [bulkModels, currentModelsArray, t, updateModels])
// Handle fetching models from upstream // Handle fetching models from upstream
const handleFetchModels = useCallback(async () => { const handleFetchModels = useCallback(async () => {
const type = form.getValues('type') const type = form.getValues('type')
...@@ -3242,6 +3274,40 @@ export function ChannelMutateDrawer({ ...@@ -3242,6 +3274,40 @@ export function ChannelMutateDrawer({
copyChipOnClick copyChipOnClick
/> />
</FormControl> </FormControl>
<FieldGroup className='mt-1'>
<Field>
<FieldLabel htmlFor='channel-bulk-models'>
{t('Add models in bulk')}
</FieldLabel>
<Textarea
id='channel-bulk-models'
value={bulkModels}
onChange={(event) =>
setBulkModels(event.target.value)
}
placeholder={t(
'Paste model names here'
)}
rows={3}
/>
<FieldDescription>
{t(
'Paste model names separated by commas, spaces, semicolons, or line breaks.'
)}
</FieldDescription>
<div className='flex justify-end'>
<Button
type='button'
variant='secondary'
size='sm'
onClick={handleAddBulkModels}
disabled={!bulkModels.trim()}
>
{t('Add Models')}
</Button>
</div>
</Field>
</FieldGroup>
{modelMappingGuardrail.exposedTargetModels {modelMappingGuardrail.exposedTargetModels
.length > 0 && ( .length > 0 && (
<Alert className='border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-500/40 dark:bg-amber-500/10 dark:text-amber-50'> <Alert className='border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-500/40 dark:bg-amber-500/10 dark:text-amber-50'>
......
...@@ -36,6 +36,7 @@ import type { ...@@ -36,6 +36,7 @@ import type {
SyncOverwritePayload, SyncOverwritePayload,
DeploymentSettingsResponse, DeploymentSettingsResponse,
ListDeploymentsResponse, ListDeploymentsResponse,
ImportModelsResponse,
} from './types' } from './types'
// ============================================================================ // ============================================================================
...@@ -111,6 +112,27 @@ export async function deleteModel( ...@@ -111,6 +112,27 @@ export async function deleteModel(
return res.data return res.data
} }
/**
* Import model metadata from an Excel workbook
*/
export async function importModels(
file: File,
overwrite: boolean
): Promise<ImportModelsResponse> {
const formData = new FormData()
formData.append('file', file)
const res = await api.post<ImportModelsResponse>(
'/api/models/import',
formData,
{
params: overwrite ? { overwrite: true } : undefined,
skipBusinessError: true,
}
)
return res.data
}
// ============================================================================ // ============================================================================
// Vendor Management // Vendor Management
// ============================================================================ // ============================================================================
......
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { InformationCircleIcon } from '@hugeicons/core-free-icons'
import { HugeiconsIcon } from '@hugeicons/react'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { ExcelImportDialog } from '@/components/excel-import-dialog'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { importModels } from '../../api'
import { modelsQueryKeys, vendorsQueryKeys } from '../../lib'
type ImportModelsDialogProps = {
open: boolean
onOpenChange: (open: boolean) => void
}
export function ImportModelsDialog(props: ImportModelsDialogProps) {
const { t } = useTranslation()
const queryClient = useQueryClient()
const { isPending, mutateAsync } = useMutation({
mutationFn: (variables: { file: File; overwrite: boolean }) =>
importModels(variables.file, variables.overwrite),
})
const handleImport = useCallback(
async (file: File, overwrite: boolean) => {
try {
const response = await mutateAsync({ file, overwrite })
if (!response.success) {
toast.error(response.message || t('Model import failed'))
return false
}
const result = response.data
toast.success(
t(
'Model import completed: {{created}} created, {{updated}} updated, {{skipped}} skipped, {{failed}} failed.',
{
created: result?.created ?? 0,
updated: result?.updated ?? 0,
skipped: result?.skipped ?? 0,
failed: result?.failed ?? 0,
}
)
)
await Promise.all([
queryClient.invalidateQueries({ queryKey: modelsQueryKeys.lists() }),
queryClient.invalidateQueries({ queryKey: vendorsQueryKeys.lists() }),
])
return true
} catch {
return false
}
},
[mutateAsync, queryClient, t]
)
return (
<ExcelImportDialog
open={props.open}
onOpenChange={props.onOpenChange}
title={t('Import models')}
description={t('Import model metadata from an Excel workbook.')}
isPending={isPending}
allowOverwrite
onImport={handleImport}
>
<Alert>
<HugeiconsIcon icon={InformationCircleIcon} aria-hidden='true' />
<AlertTitle>{t('Model import requirements')}</AlertTitle>
<AlertDescription>
{t(
'The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.'
)}
</AlertDescription>
</Alert>
</ExcelImportDialog>
)
}
...@@ -17,6 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. ...@@ -17,6 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com For commercial licensing, please contact support@quantumnous.com
*/ */
import { DescriptionDialog } from './dialogs/description-dialog' import { DescriptionDialog } from './dialogs/description-dialog'
import { ImportModelsDialog } from './dialogs/import-models-dialog'
import { MissingModelsDialog } from './dialogs/missing-models-dialog' import { MissingModelsDialog } from './dialogs/missing-models-dialog'
import { PrefillGroupManagement } from './dialogs/prefill-group-management' import { PrefillGroupManagement } from './dialogs/prefill-group-management'
import { SyncWizardDialog } from './dialogs/sync-wizard-dialog' import { SyncWizardDialog } from './dialogs/sync-wizard-dialog'
...@@ -57,6 +58,11 @@ export function ModelsDialogs() { ...@@ -57,6 +58,11 @@ export function ModelsDialogs() {
onOpenChange={(v) => !v && setOpen(null)} onOpenChange={(v) => !v && setOpen(null)}
/> />
<ImportModelsDialog
open={open === 'import-models'}
onOpenChange={(v) => !v && setOpen(null)}
/>
{/* Sync Wizard Dialog */} {/* Sync Wizard Dialog */}
<SyncWizardDialog <SyncWizardDialog
open={open === 'sync-wizard'} open={open === 'sync-wizard'}
......
...@@ -16,6 +16,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. ...@@ -16,6 +16,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com For commercial licensing, please contact support@quantumnous.com
*/ */
import { ArrowUp01Icon } from '@hugeicons/core-free-icons'
import { HugeiconsIcon } from '@hugeicons/react'
import { import {
Plus, Plus,
MoreHorizontal, MoreHorizontal,
...@@ -55,6 +57,10 @@ export function ModelsPrimaryButtons() { ...@@ -55,6 +57,10 @@ export function ModelsPrimaryButtons() {
setOpen('sync-wizard') setOpen('sync-wizard')
} }
const handleImport = () => {
setOpen('import-models')
}
const handlePrefillGroups = () => { const handlePrefillGroups = () => {
setOpen('prefill-groups') setOpen('prefill-groups')
} }
...@@ -91,6 +97,13 @@ export function ModelsPrimaryButtons() { ...@@ -91,6 +97,13 @@ export function ModelsPrimaryButtons() {
</DropdownMenuShortcut> </DropdownMenuShortcut>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem onClick={handleImport}>
{t('Import models')}
<DropdownMenuShortcut>
<HugeiconsIcon icon={ArrowUp01Icon} aria-hidden='true' />
</DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItem onClick={handlePrefillGroups}> <DropdownMenuItem onClick={handlePrefillGroups}>
......
...@@ -38,6 +38,7 @@ type DialogType = ...@@ -38,6 +38,7 @@ type DialogType =
| 'create-vendor' | 'create-vendor'
| 'update-vendor' | 'update-vendor'
| 'missing-models' | 'missing-models'
| 'import-models'
| 'sync-wizard' | 'sync-wizard'
| 'upstream-conflict' | 'upstream-conflict'
| 'prefill-groups' | 'prefill-groups'
......
...@@ -220,6 +220,23 @@ export interface PrefillGroupsResponse { ...@@ -220,6 +220,23 @@ export interface PrefillGroupsResponse {
data?: PrefillGroup[] data?: PrefillGroup[]
} }
export interface ImportModelsResponse {
success: boolean
message?: string
data?: {
created: number
updated: number
skipped: number
failed: number
rows: Array<{
row: number
model_name?: string
status: string
message?: string
}>
}
}
// ============================================================================ // ============================================================================
// Form Data Types // Form Data Types
// ============================================================================ // ============================================================================
......
...@@ -21,6 +21,7 @@ import { api } from '@/lib/api' ...@@ -21,6 +21,7 @@ import { api } from '@/lib/api'
import type { import type {
ConfirmPaymentComplianceResponse, ConfirmPaymentComplianceResponse,
FetchUpstreamRatiosRequest, FetchUpstreamRatiosRequest,
ImportModelPricingResponse,
LogCleanupTask, LogCleanupTask,
SystemOptionsResponse, SystemOptionsResponse,
SystemTaskListResponse, SystemTaskListResponse,
...@@ -91,6 +92,20 @@ export async function resetModelRatios() { ...@@ -91,6 +92,20 @@ export async function resetModelRatios() {
return res.data return res.data
} }
export async function importModelPricing(
file: File
): Promise<ImportModelPricingResponse> {
const formData = new FormData()
formData.append('file', file)
const res = await api.post<ImportModelPricingResponse>(
'/api/option/import_model_pricing',
formData,
{ skipBusinessError: true }
)
return res.data
}
export async function getUpstreamChannels() { export async function getUpstreamChannels() {
const res = await api.get<UpstreamChannelsResponse>( const res = await api.get<UpstreamChannelsResponse>(
'/api/ratio_sync/channels' '/api/ratio_sync/channels'
......
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { Alert02Icon, InformationCircleIcon } from '@hugeicons/core-free-icons'
import { HugeiconsIcon } from '@hugeicons/react'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { ExcelImportDialog } from '@/components/excel-import-dialog'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { importModelPricing } from '../api'
type ModelPricingImportDialogProps = {
open: boolean
onOpenChange: (open: boolean) => void
}
export function ModelPricingImportDialog(props: ModelPricingImportDialogProps) {
const { t } = useTranslation()
const queryClient = useQueryClient()
const { isPending, mutateAsync } = useMutation({
mutationFn: importModelPricing,
})
const handleImport = useCallback(
async (file: File) => {
try {
const response = await mutateAsync(file)
if (!response.success) {
toast.error(response.message || t('Model pricing import failed'))
return false
}
const result = response.data
toast.success(
t(
'Model pricing import completed: {{updated}} updated, {{perToken}} per-token, {{perRequest}} per-request, {{failed}} failed.',
{
updated: result?.updated ?? 0,
perToken: result?.per_token ?? 0,
perRequest: result?.per_request ?? 0,
failed: result?.failed ?? 0,
}
)
)
await queryClient.invalidateQueries({ queryKey: ['system-options'] })
return true
} catch {
return false
}
},
[mutateAsync, queryClient, t]
)
return (
<ExcelImportDialog
open={props.open}
onOpenChange={props.onOpenChange}
title={t('Import model pricing')}
description={t('Import model prices from an Excel workbook.')}
isPending={isPending}
onImport={handleImport}
>
<div className='flex flex-col gap-3'>
<Alert>
<HugeiconsIcon icon={InformationCircleIcon} aria-hidden='true' />
<AlertTitle>{t('Pricing import requirements')}</AlertTitle>
<AlertDescription>
{t(
'The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.'
)}
</AlertDescription>
</Alert>
<Alert>
<HugeiconsIcon icon={Alert02Icon} aria-hidden='true' />
<AlertTitle>{t('Existing pricing will be replaced')}</AlertTitle>
<AlertDescription>
{t(
'Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.'
)}
</AlertDescription>
</Alert>
</div>
</ExcelImportDialog>
)
}
...@@ -16,9 +16,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. ...@@ -16,9 +16,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com For commercial licensing, please contact support@quantumnous.com
*/ */
import { ArrowUp01Icon } from '@hugeicons/core-free-icons'
import { HugeiconsIcon } from '@hugeicons/react'
import { Code2, Eye, RotateCcw, Save } from 'lucide-react' import { Code2, Eye, RotateCcw, Save } from 'lucide-react'
import { memo, useCallback, useRef, useState } from 'react' import { memo, useCallback, useRef, useState } from 'react'
import { type UseFormReturn } from 'react-hook-form' import type { UseFormReturn } from 'react-hook-form'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { JsonCodeEditor } from '@/components/json-code-editor' import { JsonCodeEditor } from '@/components/json-code-editor'
...@@ -39,6 +41,7 @@ import { ...@@ -39,6 +41,7 @@ import {
SettingsSwitchContent, SettingsSwitchContent,
SettingsSwitchItem, SettingsSwitchItem,
} from '../components/settings-form-layout' } from '../components/settings-form-layout'
import { ModelPricingImportDialog } from './model-pricing-import-dialog'
import { import {
ModelRatioVisualEditor, ModelRatioVisualEditor,
type ModelRatioVisualEditorHandle, type ModelRatioVisualEditorHandle,
...@@ -167,6 +170,7 @@ export const ModelRatioForm = memo(function ModelRatioForm({ ...@@ -167,6 +170,7 @@ export const ModelRatioForm = memo(function ModelRatioForm({
}: ModelRatioFormProps) { }: ModelRatioFormProps) {
const { t } = useTranslation() const { t } = useTranslation()
const [editMode, setEditMode] = useState<'visual' | 'json'>('visual') const [editMode, setEditMode] = useState<'visual' | 'json'>('visual')
const [importOpen, setImportOpen] = useState(false)
const visualEditorRef = useRef<ModelRatioVisualEditorHandle>(null) const visualEditorRef = useRef<ModelRatioVisualEditorHandle>(null)
const handleFieldChange = useCallback( const handleFieldChange = useCallback(
...@@ -197,6 +201,19 @@ export const ModelRatioForm = memo(function ModelRatioForm({ ...@@ -197,6 +201,19 @@ export const ModelRatioForm = memo(function ModelRatioForm({
<div className='flex flex-wrap justify-end gap-2'> <div className='flex flex-wrap justify-end gap-2'>
<Button <Button
type='button' type='button'
variant='outline'
size='sm'
onClick={() => setImportOpen(true)}
>
<HugeiconsIcon
icon={ArrowUp01Icon}
data-icon='inline-start'
aria-hidden='true'
/>
{t('Excel import')}
</Button>
<Button
type='button'
variant='destructive' variant='destructive'
size='sm' size='sm'
onClick={onReset} onClick={onReset}
...@@ -331,6 +348,10 @@ export const ModelRatioForm = memo(function ModelRatioForm({ ...@@ -331,6 +348,10 @@ export const ModelRatioForm = memo(function ModelRatioForm({
</SettingsForm> </SettingsForm>
)} )}
</Form> </Form>
<ModelPricingImportDialog
open={importOpen}
onOpenChange={setImportOpen}
/>
</div> </div>
) )
}) })
...@@ -39,6 +39,24 @@ export type UpdateOptionResponse = { ...@@ -39,6 +39,24 @@ export type UpdateOptionResponse = {
message: string message: string
} }
export type ImportModelPricingResponse = {
success: boolean
message?: string
data?: {
updated: number
per_token: number
per_request: number
failed: number
rows: Array<{
row: number
model_name?: string
mode?: string
status: string
message?: string
}>
}
}
export type ConfirmPaymentComplianceResponse = { export type ConfirmPaymentComplianceResponse = {
success: boolean success: boolean
message: string message: string
......
...@@ -191,6 +191,7 @@ ...@@ -191,6 +191,7 @@
"Add Model": "Add Model", "Add Model": "Add Model",
"Add model pricing": "Add model pricing", "Add model pricing": "Add model pricing",
"Add Models": "Add Models", "Add Models": "Add Models",
"Add models in bulk": "Add models in bulk",
"Add new amount": "Add new amount", "Add new amount": "Add new amount",
"Add new redemption code(s) by providing necessary info.": "Add new redemption code(s) by providing necessary info.", "Add new redemption code(s) by providing necessary info.": "Add new redemption code(s) by providing necessary info.",
"Add OAuth Provider": "Add OAuth Provider", "Add OAuth Provider": "Add OAuth Provider",
...@@ -1719,6 +1720,8 @@ ...@@ -1719,6 +1720,8 @@
"Example:": "Example:", "Example:": "Example:",
"example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com", "example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com",
"example.com&#10;company.com": "example.com&#10;company.com", "example.com&#10;company.com": "example.com&#10;company.com",
"Excel file": "Excel file",
"Excel import": "Excel import",
"Excellent": "Excellent", "Excellent": "Excellent",
"Exchange rate is required": "Exchange rate is required", "Exchange rate is required": "Exchange rate is required",
"Exchange rate must be greater than 0": "Exchange rate must be greater than 0", "Exchange rate must be greater than 0": "Exchange rate must be greater than 0",
...@@ -1727,6 +1730,7 @@ ...@@ -1727,6 +1730,7 @@
"Exhausted": "Exhausted", "Exhausted": "Exhausted",
"Existing account will be reused": "Existing account will be reused", "Existing account will be reused": "Existing account will be reused",
"Existing Models ({{count}})": "Existing Models ({{count}})", "Existing Models ({{count}})": "Existing Models ({{count}})",
"Existing pricing will be replaced": "Existing pricing will be replaced",
"Exists": "Exists", "Exists": "Exists",
"Expand": "Expand", "Expand": "Expand",
"Expand All": "Expand All", "Expand All": "Expand All",
...@@ -2256,8 +2260,15 @@ ...@@ -2256,8 +2260,15 @@
"Image to Video": "Image to Video", "Image to Video": "Image to Video",
"Image Tokens": "Image Tokens", "Image Tokens": "Image Tokens",
"Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.", "Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.",
"Import": "Import",
"Import model metadata from an Excel workbook.": "Import model metadata from an Excel workbook.",
"Import model prices from an Excel workbook.": "Import model prices from an Excel workbook.",
"Import model pricing": "Import model pricing",
"Import models": "Import models",
"Import to CC Switch": "Import to CC Switch", "Import to CC Switch": "Import to CC Switch",
"Important": "Important", "Important": "Important",
"Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.": "Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.",
"Importing...": "Importing...",
"In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.", "In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.",
"In Progress": "In Progress", "In Progress": "In Progress",
"In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.", "In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.",
...@@ -2625,6 +2636,9 @@ ...@@ -2625,6 +2636,9 @@
"Model enabled successfully": "Model enabled successfully", "Model enabled successfully": "Model enabled successfully",
"Model fixed pricing": "Model fixed pricing", "Model fixed pricing": "Model fixed pricing",
"Model Group": "Model Group", "Model Group": "Model Group",
"Model import completed: {{created}} created, {{updated}} updated, {{skipped}} skipped, {{failed}} failed.": "Model import completed: {{created}} created, {{updated}} updated, {{skipped}} skipped, {{failed}} failed.",
"Model import failed": "Model import failed",
"Model import requirements": "Model import requirements",
"Model Limits": "Model Limits", "Model Limits": "Model Limits",
"Model Mapping": "Model Mapping", "Model Mapping": "Model Mapping",
"Model Mapping (JSON)": "Model Mapping (JSON)", "Model Mapping (JSON)": "Model Mapping (JSON)",
...@@ -2647,6 +2661,8 @@ ...@@ -2647,6 +2661,8 @@
"Model prices": "Model prices", "Model prices": "Model prices",
"Model prices reset successfully": "Model prices reset successfully", "Model prices reset successfully": "Model prices reset successfully",
"Model Pricing": "Model Pricing", "Model Pricing": "Model Pricing",
"Model pricing import completed: {{updated}} updated, {{perToken}} per-token, {{perRequest}} per-request, {{failed}} failed.": "Model pricing import completed: {{updated}} updated, {{perToken}} per-token, {{perRequest}} per-request, {{failed}} failed.",
"Model pricing import failed": "Model pricing import failed",
"Model pull failed: {{msg}}": "Model pull failed: {{msg}}", "Model pull failed: {{msg}}": "Model pull failed: {{msg}}",
"Model ratio": "Model ratio", "Model ratio": "Model ratio",
"Model ratios": "Model ratios", "Model ratios": "Model ratios",
...@@ -3152,6 +3168,7 @@ ...@@ -3152,6 +3168,7 @@
"overrides for matching model prefix.": "overrides for matching model prefix.", "overrides for matching model prefix.": "overrides for matching model prefix.",
"Overrode user quota from {{from}} to {{to}}": "Overrode user quota from {{from}} to {{to}}", "Overrode user quota from {{from}} to {{to}}": "Overrode user quota from {{from}} to {{to}}",
"Overview": "Overview", "Overview": "Overview",
"Overwrite existing models": "Overwrite existing models",
"Overwritten": "Overwritten", "Overwritten": "Overwritten",
"Page": "Page", "Page": "Page",
"Page {{current}} of {{total}}": "Page {{current}} of {{total}}", "Page {{current}} of {{total}}": "Page {{current}} of {{total}}",
...@@ -3217,6 +3234,8 @@ ...@@ -3217,6 +3234,8 @@
"Passwords do not match": "Passwords do not match", "Passwords do not match": "Passwords do not match",
"Passwords don't match.": "Passwords don't match.", "Passwords don't match.": "Passwords don't match.",
"Paste Connection Info": "Paste Connection Info", "Paste Connection Info": "Paste Connection Info",
"Paste model names here": "Paste model names here",
"Paste model names separated by commas, spaces, semicolons, or line breaks.": "Paste model names separated by commas, spaces, semicolons, or line breaks.",
"Path": "Path", "Path": "Path",
"Path not set": "Path not set", "Path not set": "Path not set",
"Path Regex (one per line)": "Path Regex (one per line)", "Path Regex (one per line)": "Path Regex (one per line)",
...@@ -3436,6 +3455,7 @@ ...@@ -3436,6 +3455,7 @@
"Pricing Configuration": "Pricing Configuration", "Pricing Configuration": "Pricing Configuration",
"Pricing group example": "Pricing group example", "Pricing group example": "Pricing group example",
"Pricing groups": "Pricing groups", "Pricing groups": "Pricing groups",
"Pricing import requirements": "Pricing import requirements",
"Pricing mode": "Pricing mode", "Pricing mode": "Pricing mode",
"Pricing Ratios": "Pricing Ratios", "Pricing Ratios": "Pricing Ratios",
"Pricing Type": "Pricing Type", "Pricing Type": "Pricing Type",
...@@ -3778,6 +3798,7 @@ ...@@ -3778,6 +3798,7 @@
"Reset usage window": "Reset usage window", "Reset usage window": "Reset usage window",
"Resets in:": "Resets in:", "Resets in:": "Resets in:",
"Resetting...": "Resetting...", "Resetting...": "Resetting...",
"Resize column": "Resize column",
"Resolve Conflicts": "Resolve Conflicts", "Resolve Conflicts": "Resolve Conflicts",
"Resource Configuration": "Resource Configuration", "Resource Configuration": "Resource Configuration",
"Resources": "Resources", "Resources": "Resources",
...@@ -3966,6 +3987,7 @@ ...@@ -3966,6 +3987,7 @@
"Select all (filtered)": "Select all (filtered)", "Select all (filtered)": "Select all (filtered)",
"Select all models": "Select all models", "Select all models": "Select all models",
"Select All Visible": "Select All Visible", "Select All Visible": "Select All Visible",
"Select an .xlsx file to import. The maximum file size is 10 MB.": "Select an .xlsx file to import. The maximum file size is 10 MB.",
"Select an operation mode and enter the amount": "Select an operation mode and enter the amount", "Select an operation mode and enter the amount": "Select an operation mode and enter the amount",
"Select announcement type": "Select announcement type", "Select announcement type": "Select announcement type",
"Select at least one field to overwrite.": "Select at least one field to overwrite.", "Select at least one field to overwrite.": "Select at least one field to overwrite.",
...@@ -4029,6 +4051,7 @@ ...@@ -4029,6 +4051,7 @@
"Selected {{count}}": "Selected {{count}}", "Selected {{count}}": "Selected {{count}}",
"selected channel(s). Leave empty to remove tag.": "selected channel(s). Leave empty to remove tag.", "selected channel(s). Leave empty to remove tag.": "selected channel(s). Leave empty to remove tag.",
"Selected conflicts were overwritten successfully.": "Selected conflicts were overwritten successfully.", "Selected conflicts were overwritten successfully.": "Selected conflicts were overwritten successfully.",
"Selected file: {{name}}": "Selected file: {{name}}",
"Selected nodes": "Selected nodes", "Selected nodes": "Selected nodes",
"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.", "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", "Self-Use Mode": "Self-Use Mode",
...@@ -4401,6 +4424,8 @@ ...@@ -4401,6 +4424,8 @@
"The entered text does not match the required text.": "The entered text does not match the required text.", "The entered text does not match the required text.": "The entered text does not match the required text.",
"The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.", "The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.",
"The exact model identifier as used in API requests.": "The exact model identifier as used in API requests.", "The exact model identifier as used in API requests.": "The exact model identifier as used in API requests.",
"The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.": "The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.",
"The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.": "The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.",
"The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.", "The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.",
"The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:", "The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:",
"The mapped upstream model(s)": "The mapped upstream model(s)", "The mapped upstream model(s)": "The mapped upstream model(s)",
......
...@@ -191,6 +191,7 @@ ...@@ -191,6 +191,7 @@
"Add Model": "Ajouter un modèle", "Add Model": "Ajouter un modèle",
"Add model pricing": "Ajouter une tarification de modèle", "Add model pricing": "Ajouter une tarification de modèle",
"Add Models": "Ajouter des modèles", "Add Models": "Ajouter des modèles",
"Add models in bulk": "Ajouter des modèles en lot",
"Add new amount": "Ajouter un nouveau montant", "Add new amount": "Ajouter un nouveau montant",
"Add new redemption code(s) by providing necessary info.": "Ajouter de nouveaux codes de réduction en fournissant les informations nécessaires.", "Add new redemption code(s) by providing necessary info.": "Ajouter de nouveaux codes de réduction en fournissant les informations nécessaires.",
"Add OAuth Provider": "Ajouter un fournisseur OAuth", "Add OAuth Provider": "Ajouter un fournisseur OAuth",
...@@ -1719,6 +1720,8 @@ ...@@ -1719,6 +1720,8 @@
"Example:": "Exemple :", "Example:": "Exemple :",
"example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com", "example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com",
"example.com&#10;company.com": "example.com&#10;company.com", "example.com&#10;company.com": "example.com&#10;company.com",
"Excel file": "Fichier Excel",
"Excel import": "Importation Excel",
"Excellent": "Excellent", "Excellent": "Excellent",
"Exchange rate is required": "Le taux de change est requis", "Exchange rate is required": "Le taux de change est requis",
"Exchange rate must be greater than 0": "Le taux de change doit être supérieur à 0", "Exchange rate must be greater than 0": "Le taux de change doit être supérieur à 0",
...@@ -1727,6 +1730,7 @@ ...@@ -1727,6 +1730,7 @@
"Exhausted": "Épuisé", "Exhausted": "Épuisé",
"Existing account will be reused": "Le compte existant sera réutilisé", "Existing account will be reused": "Le compte existant sera réutilisé",
"Existing Models ({{count}})": "Modèles existants ({{count}})", "Existing Models ({{count}})": "Modèles existants ({{count}})",
"Existing pricing will be replaced": "Les tarifs existants seront remplacés",
"Exists": "Existe", "Exists": "Existe",
"Expand": "Développer", "Expand": "Développer",
"Expand All": "Tout développer", "Expand All": "Tout développer",
...@@ -2256,8 +2260,15 @@ ...@@ -2256,8 +2260,15 @@
"Image to Video": "Image vers vidéo", "Image to Video": "Image vers vidéo",
"Image Tokens": "Tokens image", "Image Tokens": "Tokens image",
"Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "Imaginez que le tableau tarifaire contient trois groupes : default (taux 1,0), premium (taux 0,5) et vip (taux 0,8). Les utilisateurs du groupe vip bénéficient d’avantages au niveau du compte, et premium est un pool de canaux moins cher que les utilisateurs peuvent choisir pour leurs jetons.", "Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "Imaginez que le tableau tarifaire contient trois groupes : default (taux 1,0), premium (taux 0,5) et vip (taux 0,8). Les utilisateurs du groupe vip bénéficient d’avantages au niveau du compte, et premium est un pool de canaux moins cher que les utilisateurs peuvent choisir pour leurs jetons.",
"Import": "Importer",
"Import model metadata from an Excel workbook.": "Importer les métadonnées des modèles depuis un classeur Excel.",
"Import model prices from an Excel workbook.": "Importer les tarifs des modèles depuis un classeur Excel.",
"Import model pricing": "Importer les tarifs des modèles",
"Import models": "Importer des modèles",
"Import to CC Switch": "Importer vers CC Switch", "Import to CC Switch": "Importer vers CC Switch",
"Important": "Important", "Important": "Important",
"Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.": "L’importation remplace le mode de facturation des modèles correspondants : la facturation au token efface les prix fixes, tandis que la facturation par requête efface les ratios.",
"Importing...": "Importation...",
"In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "En JSON, le groupe d’utilisateurs est la clé externe et le groupe de facturation la clé interne. L’exemple ci-dessous signifie : les utilisateurs vip paient 0,8 sous standard et 0,3 sous premium.", "In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "En JSON, le groupe d’utilisateurs est la clé externe et le groupe de facturation la clé interne. L’exemple ci-dessous signifie : les utilisateurs vip paient 0,8 sous standard et 0,3 sous premium.",
"In Progress": "En cours", "In Progress": "En cours",
"In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "Dans l’éditeur visuel, ces règles apparaissent comme « Visible en plus » et « Masqué ». En JSON, +: (ou aucun préfixe) ajoute un groupe et -: en retire un.", "In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "Dans l’éditeur visuel, ces règles apparaissent comme « Visible en plus » et « Masqué ». En JSON, +: (ou aucun préfixe) ajoute un groupe et -: en retire un.",
...@@ -2625,6 +2636,9 @@ ...@@ -2625,6 +2636,9 @@
"Model enabled successfully": "Modèle activé avec succès", "Model enabled successfully": "Modèle activé avec succès",
"Model fixed pricing": "Tarification fixe du modèle", "Model fixed pricing": "Tarification fixe du modèle",
"Model Group": "Groupe de modèles", "Model Group": "Groupe de modèles",
"Model import completed: {{created}} created, {{updated}} updated, {{skipped}} skipped, {{failed}} failed.": "Importation terminée : {{created}} créés, {{updated}} mis à jour, {{skipped}} ignorés, {{failed}} en échec.",
"Model import failed": "Échec de l’importation des modèles",
"Model import requirements": "Exigences d’importation des modèles",
"Model Limits": "Limites du modèle", "Model Limits": "Limites du modèle",
"Model Mapping": "Mappage de modèle", "Model Mapping": "Mappage de modèle",
"Model Mapping (JSON)": "Mappage de modèle (JSON)", "Model Mapping (JSON)": "Mappage de modèle (JSON)",
...@@ -2647,6 +2661,8 @@ ...@@ -2647,6 +2661,8 @@
"Model prices": "Prix des modèles", "Model prices": "Prix des modèles",
"Model prices reset successfully": "Prix des modèles réinitialisés avec succès", "Model prices reset successfully": "Prix des modèles réinitialisés avec succès",
"Model Pricing": "Tarification des modèles", "Model Pricing": "Tarification des modèles",
"Model pricing import completed: {{updated}} updated, {{perToken}} per-token, {{perRequest}} per-request, {{failed}} failed.": "Importation des tarifs terminée : {{updated}} mis à jour, {{perToken}} au token, {{perRequest}} par requête, {{failed}} en échec.",
"Model pricing import failed": "Échec de l’importation des tarifs des modèles",
"Model pull failed: {{msg}}": "Échec du téléchargement du modèle : {{msg}}", "Model pull failed: {{msg}}": "Échec du téléchargement du modèle : {{msg}}",
"Model ratio": "Ratio modèle", "Model ratio": "Ratio modèle",
"Model ratios": "Ratios de modèle", "Model ratios": "Ratios de modèle",
...@@ -3152,6 +3168,7 @@ ...@@ -3152,6 +3168,7 @@
"overrides for matching model prefix.": "remplace le tarif si le modèle a ce préfixe.", "overrides for matching model prefix.": "remplace le tarif si le modèle a ce préfixe.",
"Overrode user quota from {{from}} to {{to}}": "Quota de l'utilisateur remplacé de {{from}} à {{to}}", "Overrode user quota from {{from}} to {{to}}": "Quota de l'utilisateur remplacé de {{from}} à {{to}}",
"Overview": "Vue d'ensemble", "Overview": "Vue d'ensemble",
"Overwrite existing models": "Remplacer les modèles existants",
"Overwritten": "Écrasé", "Overwritten": "Écrasé",
"Page": "Page", "Page": "Page",
"Page {{current}} of {{total}}": "Page {{current}} sur {{total}}", "Page {{current}} of {{total}}": "Page {{current}} sur {{total}}",
...@@ -3217,6 +3234,8 @@ ...@@ -3217,6 +3234,8 @@
"Passwords do not match": "Les mots de passe ne correspondent pas", "Passwords do not match": "Les mots de passe ne correspondent pas",
"Passwords don't match.": "Les mots de passe ne correspondent pas.", "Passwords don't match.": "Les mots de passe ne correspondent pas.",
"Paste Connection Info": "Coller les infos de connexion", "Paste Connection Info": "Coller les infos de connexion",
"Paste model names here": "Collez les noms des modèles ici",
"Paste model names separated by commas, spaces, semicolons, or line breaks.": "Séparez les noms des modèles par des virgules, espaces, points-virgules ou retours à la ligne.",
"Path": "Chemin", "Path": "Chemin",
"Path not set": "Chemin non défini", "Path not set": "Chemin non défini",
"Path Regex (one per line)": "Regex du chemin (un par ligne)", "Path Regex (one per line)": "Regex du chemin (un par ligne)",
...@@ -3436,6 +3455,7 @@ ...@@ -3436,6 +3455,7 @@
"Pricing Configuration": "Configuration de la tarification", "Pricing Configuration": "Configuration de la tarification",
"Pricing group example": "Exemple de groupe tarifaire", "Pricing group example": "Exemple de groupe tarifaire",
"Pricing groups": "Groupes tarifaires", "Pricing groups": "Groupes tarifaires",
"Pricing import requirements": "Exigences d’importation des tarifs",
"Pricing mode": "Mode de tarification", "Pricing mode": "Mode de tarification",
"Pricing Ratios": "Ratios de tarification", "Pricing Ratios": "Ratios de tarification",
"Pricing Type": "Type de tarification", "Pricing Type": "Type de tarification",
...@@ -3778,6 +3798,7 @@ ...@@ -3778,6 +3798,7 @@
"Reset usage window": "Réinitialiser la fenêtre d’utilisation", "Reset usage window": "Réinitialiser la fenêtre d’utilisation",
"Resets in:": "Réinitialise dans :", "Resets in:": "Réinitialise dans :",
"Resetting...": "Réinitialisation...", "Resetting...": "Réinitialisation...",
"Resize column": "Redimensionner la colonne",
"Resolve Conflicts": "Résoudre les conflits", "Resolve Conflicts": "Résoudre les conflits",
"Resource Configuration": "Configuration des ressources", "Resource Configuration": "Configuration des ressources",
"Resources": "Ressources", "Resources": "Ressources",
...@@ -3966,6 +3987,7 @@ ...@@ -3966,6 +3987,7 @@
"Select all (filtered)": "Tout sélectionner (filtré)", "Select all (filtered)": "Tout sélectionner (filtré)",
"Select all models": "Sélectionner tous les modèles", "Select all models": "Sélectionner tous les modèles",
"Select All Visible": "Sélectionner tout ce qui est visible", "Select All Visible": "Sélectionner tout ce qui est visible",
"Select an .xlsx file to import. The maximum file size is 10 MB.": "Sélectionnez un fichier .xlsx à importer, de 10 Mo maximum.",
"Select an operation mode and enter the amount": "Sélectionnez un mode d'opération et entrez le montant", "Select an operation mode and enter the amount": "Sélectionnez un mode d'opération et entrez le montant",
"Select announcement type": "Sélectionner le type d'annonce", "Select announcement type": "Sélectionner le type d'annonce",
"Select at least one field to overwrite.": "Sélectionnez au moins un champ à écraser.", "Select at least one field to overwrite.": "Sélectionnez au moins un champ à écraser.",
...@@ -4029,6 +4051,7 @@ ...@@ -4029,6 +4051,7 @@
"Selected {{count}}": "{{count}} sélectionné(s)", "Selected {{count}}": "{{count}} sélectionné(s)",
"selected channel(s). Leave empty to remove tag.": "canal(aux) sélectionné(s). Laisser vide pour supprimer l'étiquette.", "selected channel(s). Leave empty to remove tag.": "canal(aux) sélectionné(s). Laisser vide pour supprimer l'étiquette.",
"Selected conflicts were overwritten successfully.": "Les conflits sélectionnés ont été écrasés avec succès.", "Selected conflicts were overwritten successfully.": "Les conflits sélectionnés ont été écrasés avec succès.",
"Selected file: {{name}}": "Fichier sélectionné : {{name}}",
"Selected nodes": "Nœuds sélectionnés", "Selected nodes": "Nœuds sélectionnés",
"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.", "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", "Self-Use Mode": "Mode d'utilisation personnelle",
...@@ -4401,6 +4424,8 @@ ...@@ -4401,6 +4424,8 @@
"The entered text does not match the required text.": "Le texte saisi ne correspond pas au texte requis.", "The entered text does not match the required text.": "Le texte saisi ne correspond pas au texte requis.",
"The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "L’environnement (test ou production) est déterminé par la clé collée ici : utilisez la clé de test pendant l’intégration, puis remplacez-la par la clé de production lors de la mise en ligne.", "The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "L’environnement (test ou production) est déterminé par la clé collée ici : utilisez la clé de test pendant l’intégration, puis remplacez-la par la clé de production lors de la mise en ligne.",
"The exact model identifier as used in API requests.": "L'identifiant exact du modèle tel qu'utilisé dans les requêtes API.", "The exact model identifier as used in API requests.": "L'identifiant exact du modèle tel qu'utilisé dans les requêtes API.",
"The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.": "La première ligne doit contenir les en-têtes et model_name est obligatoire. Pour la facturation par requête, renseignez fixed_price. Sinon, input_price est obligatoire ; colonnes facultatives : completion_price, cache_price, create_cache_price, image_price, audio_input_price et audio_output_price.",
"The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.": "La première ligne doit contenir les en-têtes et model_name est obligatoire. Colonnes facultatives : description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.",
"The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "Les modèles suivants présentent des conflits de type de facturation (prix fixe vs facturation au ratio). Confirmez pour procéder aux changements.", "The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "Les modèles suivants présentent des conflits de type de facturation (prix fixe vs facturation au ratio). Confirmez pour procéder aux changements.",
"The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "Les modèles suivants dans la redirection du modèle n'ont pas été ajoutés à la liste \"Modèles\" et peuvent échouer lors de l'invocation en raison de modèles disponibles manquants :", "The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "Les modèles suivants dans la redirection du modèle n'ont pas été ajoutés à la liste \"Modèles\" et peuvent échouer lors de l'invocation en raison de modèles disponibles manquants :",
"The mapped upstream model(s)": "Le(s) modèle(s) amont mappé(s)", "The mapped upstream model(s)": "Le(s) modèle(s) amont mappé(s)",
......
...@@ -191,6 +191,7 @@ ...@@ -191,6 +191,7 @@
"Add Model": "モデルを追加", "Add Model": "モデルを追加",
"Add model pricing": "モデル料金を追加", "Add model pricing": "モデル料金を追加",
"Add Models": "モデルを追加", "Add Models": "モデルを追加",
"Add models in bulk": "モデルを一括追加",
"Add new amount": "新しい金額を追加", "Add new amount": "新しい金額を追加",
"Add new redemption code(s) by providing necessary info.": "必要な情報を提供して新しい引き換えコードを追加します。", "Add new redemption code(s) by providing necessary info.": "必要な情報を提供して新しい引き換えコードを追加します。",
"Add OAuth Provider": "OAuthプロバイダーを追加", "Add OAuth Provider": "OAuthプロバイダーを追加",
...@@ -1719,6 +1720,8 @@ ...@@ -1719,6 +1720,8 @@
"Example:": "例:", "Example:": "例:",
"example.com&#10;blocked-site.com": "example.com &#10;blocked-site.com", "example.com&#10;blocked-site.com": "example.com &#10;blocked-site.com",
"example.com&#10;company.com": "example.com &#10;company.com", "example.com&#10;company.com": "example.com &#10;company.com",
"Excel file": "Excel ファイル",
"Excel import": "Excel インポート",
"Excellent": "優秀", "Excellent": "優秀",
"Exchange rate is required": "為替レートは必須です", "Exchange rate is required": "為替レートは必須です",
"Exchange rate must be greater than 0": "為替レートは 0 より大きくする必要があります", "Exchange rate must be greater than 0": "為替レートは 0 より大きくする必要があります",
...@@ -1727,6 +1730,7 @@ ...@@ -1727,6 +1730,7 @@
"Exhausted": "使い切り", "Exhausted": "使い切り",
"Existing account will be reused": "既存のアカウントが再利用されます", "Existing account will be reused": "既存のアカウントが再利用されます",
"Existing Models ({{count}})": "既存のモデル ({{count}})", "Existing Models ({{count}})": "既存のモデル ({{count}})",
"Existing pricing will be replaced": "既存の料金設定は上書きされます",
"Exists": "存在", "Exists": "存在",
"Expand": "展開", "Expand": "展開",
"Expand All": "すべて展開", "Expand All": "すべて展開",
...@@ -2256,8 +2260,15 @@ ...@@ -2256,8 +2260,15 @@
"Image to Video": "画像から動画", "Image to Video": "画像から動画",
"Image Tokens": "画像トークン", "Image Tokens": "画像トークン",
"Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "料金表に3つのグループがあるとします:default(倍率 1.0)、premium(倍率 0.5)、vip(倍率 0.8)。アカウントが vip グループのユーザーはユーザーレベルの特典を受けられ、premium はユーザーがトークン用に選べる安いチャネルプールです。", "Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "料金表に3つのグループがあるとします:default(倍率 1.0)、premium(倍率 0.5)、vip(倍率 0.8)。アカウントが vip グループのユーザーはユーザーレベルの特典を受けられ、premium はユーザーがトークン用に選べる安いチャネルプールです。",
"Import": "インポート",
"Import model metadata from an Excel workbook.": "Excel ワークブックからモデルのメタデータをインポートします。",
"Import model prices from an Excel workbook.": "Excel ワークブックからモデル料金をインポートします。",
"Import model pricing": "モデル料金をインポート",
"Import models": "モデルをインポート",
"Import to CC Switch": "CC Switch にインポート", "Import to CC Switch": "CC Switch にインポート",
"Important": "重要", "Important": "重要",
"Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.": "インポートすると該当モデルの課金方式が上書きされます。トークン単位課金では固定価格が削除され、リクエスト単位課金では倍率設定が削除されます。",
"Importing...": "インポート中...",
"In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "JSONでは外側のキーがユーザーグループ、内側のキーが課金グループです。以下の例は、vip ユーザーが standard として課金されると 0.8、premium として課金されると 0.3 を意味します。", "In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "JSONでは外側のキーがユーザーグループ、内側のキーが課金グループです。以下の例は、vip ユーザーが standard として課金されると 0.8、premium として課金されると 0.3 を意味します。",
"In Progress": "処理中", "In Progress": "処理中",
"In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "ビジュアルエディタでは「追加表示」と「非表示」として表示されます。JSONでは +:(または接頭辞なし)でグループを追加し、-: で削除します。", "In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "ビジュアルエディタでは「追加表示」と「非表示」として表示されます。JSONでは +:(または接頭辞なし)でグループを追加し、-: で削除します。",
...@@ -2625,6 +2636,9 @@ ...@@ -2625,6 +2636,9 @@
"Model enabled successfully": "モデルが正常に有効化されました", "Model enabled successfully": "モデルが正常に有効化されました",
"Model fixed pricing": "モデルの固定価格設定", "Model fixed pricing": "モデルの固定価格設定",
"Model Group": "モデルグループ", "Model Group": "モデルグループ",
"Model import completed: {{created}} created, {{updated}} updated, {{skipped}} skipped, {{failed}} failed.": "モデルのインポートが完了しました:新規 {{created}} 件、更新 {{updated}} 件、スキップ {{skipped}} 件、失敗 {{failed}} 件。",
"Model import failed": "モデルのインポートに失敗しました",
"Model import requirements": "モデルインポートの要件",
"Model Limits": "モデル制限", "Model Limits": "モデル制限",
"Model Mapping": "モデルマッピング", "Model Mapping": "モデルマッピング",
"Model Mapping (JSON)": "モデルマッピング (JSON)", "Model Mapping (JSON)": "モデルマッピング (JSON)",
...@@ -2647,6 +2661,8 @@ ...@@ -2647,6 +2661,8 @@
"Model prices": "モデル価格", "Model prices": "モデル価格",
"Model prices reset successfully": "モデル価格が正常にリセットされました", "Model prices reset successfully": "モデル価格が正常にリセットされました",
"Model Pricing": "モデル料金", "Model Pricing": "モデル料金",
"Model pricing import completed: {{updated}} updated, {{perToken}} per-token, {{perRequest}} per-request, {{failed}} failed.": "モデル料金のインポートが完了しました:更新 {{updated}} 件、トークン単位 {{perToken}} 件、リクエスト単位 {{perRequest}} 件、失敗 {{failed}} 件。",
"Model pricing import failed": "モデル料金のインポートに失敗しました",
"Model pull failed: {{msg}}": "モデルのプルに失敗しました: __ PH_0 __", "Model pull failed: {{msg}}": "モデルのプルに失敗しました: __ PH_0 __",
"Model ratio": "モデル倍率", "Model ratio": "モデル倍率",
"Model ratios": "モデル比率", "Model ratios": "モデル比率",
...@@ -3152,6 +3168,7 @@ ...@@ -3152,6 +3168,7 @@
"overrides for matching model prefix.": "は一致するモデル接頭辞に上書きします。", "overrides for matching model prefix.": "は一致するモデル接頭辞に上書きします。",
"Overrode user quota from {{from}} to {{to}}": "ユーザーのクォータを {{from}} から {{to}} に上書きしました", "Overrode user quota from {{from}} to {{to}}": "ユーザーのクォータを {{from}} から {{to}} に上書きしました",
"Overview": "概要", "Overview": "概要",
"Overwrite existing models": "既存のモデルを上書き",
"Overwritten": "上書き済み", "Overwritten": "上書き済み",
"Page": "ページ", "Page": "ページ",
"Page {{current}} of {{total}}": "{{total}} ページ中 {{current}} ページ目", "Page {{current}} of {{total}}": "{{total}} ページ中 {{current}} ページ目",
...@@ -3217,6 +3234,8 @@ ...@@ -3217,6 +3234,8 @@
"Passwords do not match": "パスワードが一致しません", "Passwords do not match": "パスワードが一致しません",
"Passwords don't match.": "パスワードが一致しません。", "Passwords don't match.": "パスワードが一致しません。",
"Paste Connection Info": "接続情報を貼り付け", "Paste Connection Info": "接続情報を貼り付け",
"Paste model names here": "モデル名をここに貼り付け",
"Paste model names separated by commas, spaces, semicolons, or line breaks.": "モデル名をカンマ、スペース、セミコロン、または改行で区切って貼り付けてください。",
"Path": "パス", "Path": "パス",
"Path not set": "パス未設定", "Path not set": "パス未設定",
"Path Regex (one per line)": "パス正規表現(1行に1つ)", "Path Regex (one per line)": "パス正規表現(1行に1つ)",
...@@ -3436,6 +3455,7 @@ ...@@ -3436,6 +3455,7 @@
"Pricing Configuration": "価格設定", "Pricing Configuration": "価格設定",
"Pricing group example": "料金グループの例", "Pricing group example": "料金グループの例",
"Pricing groups": "料金グループ", "Pricing groups": "料金グループ",
"Pricing import requirements": "料金インポートの要件",
"Pricing mode": "価格モード", "Pricing mode": "価格モード",
"Pricing Ratios": "価格比率", "Pricing Ratios": "価格比率",
"Pricing Type": "価格タイプ", "Pricing Type": "価格タイプ",
...@@ -3778,6 +3798,7 @@ ...@@ -3778,6 +3798,7 @@
"Reset usage window": "使用量ウィンドウをリセット", "Reset usage window": "使用量ウィンドウをリセット",
"Resets in:": "リセットまで:", "Resets in:": "リセットまで:",
"Resetting...": "リセット中...", "Resetting...": "リセット中...",
"Resize column": "列幅を変更",
"Resolve Conflicts": "競合を解決", "Resolve Conflicts": "競合を解決",
"Resource Configuration": "リソース設定", "Resource Configuration": "リソース設定",
"Resources": "リソース", "Resources": "リソース",
...@@ -3966,6 +3987,7 @@ ...@@ -3966,6 +3987,7 @@
"Select all (filtered)": "フィルタ結果をすべて選択(S)", "Select all (filtered)": "フィルタ結果をすべて選択(S)",
"Select all models": "すべてのモデルを選択", "Select all models": "すべてのモデルを選択",
"Select All Visible": "表示中のすべてを選択", "Select All Visible": "表示中のすべてを選択",
"Select an .xlsx file to import. The maximum file size is 10 MB.": "インポートする .xlsx ファイルを選択してください。最大ファイルサイズは 10 MB です。",
"Select an operation mode and enter the amount": "操作モードを選択し、金額を入力してください", "Select an operation mode and enter the amount": "操作モードを選択し、金額を入力してください",
"Select announcement type": "アナウンスメントタイプを選択", "Select announcement type": "アナウンスメントタイプを選択",
"Select at least one field to overwrite.": "上書きするフィールドを少なくとも 1 つ選択してください。", "Select at least one field to overwrite.": "上書きするフィールドを少なくとも 1 つ選択してください。",
...@@ -4029,6 +4051,7 @@ ...@@ -4029,6 +4051,7 @@
"Selected {{count}}": "{{count}} 件選択済み", "Selected {{count}}": "{{count}} 件選択済み",
"selected channel(s). Leave empty to remove tag.": "選択されたチャネル。タグを削除するには空のままにしてください。", "selected channel(s). Leave empty to remove tag.": "選択されたチャネル。タグを削除するには空のままにしてください。",
"Selected conflicts were overwritten successfully.": "選択した競合が正常に上書きされました。", "Selected conflicts were overwritten successfully.": "選択した競合が正常に上書きされました。",
"Selected file: {{name}}": "選択したファイル:{{name}}",
"Selected nodes": "選択したノード", "Selected nodes": "選択したノード",
"Selected when creating a token and used as the default billing group for API calls.": "トークン作成時に選択され、API 呼び出しのデフォルト課金グループとして使われます。", "Selected when creating a token and used as the default billing group for API calls.": "トークン作成時に選択され、API 呼び出しのデフォルト課金グループとして使われます。",
"Self-Use Mode": "セルフユースモード", "Self-Use Mode": "セルフユースモード",
...@@ -4401,6 +4424,8 @@ ...@@ -4401,6 +4424,8 @@
"The entered text does not match the required text.": "入力したテキストが必要なテキストと一致しません。", "The entered text does not match the required text.": "入力したテキストが必要なテキストと一致しません。",
"The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "環境(テスト/本番)はここに貼り付けるキーで決まります。統合中はテストキーを使用し、本番公開時に本番キーへ切り替えてください。", "The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "環境(テスト/本番)はここに貼り付けるキーで決まります。統合中はテストキーを使用し、本番公開時に本番キーへ切り替えてください。",
"The exact model identifier as used in API requests.": "APIリクエストで使用される正確なモデル識別子。", "The exact model identifier as used in API requests.": "APIリクエストで使用される正確なモデル識別子。",
"The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.": "1 行目はヘッダーで、model_name は必須です。リクエスト単位課金では fixed_price を入力してください。それ以外では input_price が必須です。任意列:completion_price、cache_price、create_cache_price、image_price、audio_input_price、audio_output_price。",
"The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.": "1 行目はヘッダーで、model_name は必須です。任意列:description、icon、tags、vendor_name、endpoints、status、sync_official、name_rule。",
"The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "以下のモデルには請求タイプ(固定価格 vs 比率請求)の競合があります。変更を続行するには確認してください。", "The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "以下のモデルには請求タイプ(固定価格 vs 比率請求)の競合があります。変更を続行するには確認してください。",
"The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "モデルリダイレクト内の以下のモデルは\"モデル\"リストに追加されていないため、利用可能なモデルが不足して呼び出しが失敗する可能性があります:", "The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "モデルリダイレクト内の以下のモデルは\"モデル\"リストに追加されていないため、利用可能なモデルが不足して呼び出しが失敗する可能性があります:",
"The mapped upstream model(s)": "マッピングされたアップストリームモデル", "The mapped upstream model(s)": "マッピングされたアップストリームモデル",
......
...@@ -191,6 +191,7 @@ ...@@ -191,6 +191,7 @@
"Add Model": "Добавить модель", "Add Model": "Добавить модель",
"Add model pricing": "Добавить тариф модели", "Add model pricing": "Добавить тариф модели",
"Add Models": "Добавить модели", "Add Models": "Добавить модели",
"Add models in bulk": "Добавить модели пакетом",
"Add new amount": "Добавить новую сумму", "Add new amount": "Добавить новую сумму",
"Add new redemption code(s) by providing necessary info.": "Добавьте новый(-ые) код(-ы) активации, предоставив необходимую информацию.", "Add new redemption code(s) by providing necessary info.": "Добавьте новый(-ые) код(-ы) активации, предоставив необходимую информацию.",
"Add OAuth Provider": "Добавить поставщика OAuth", "Add OAuth Provider": "Добавить поставщика OAuth",
...@@ -1719,6 +1720,8 @@ ...@@ -1719,6 +1720,8 @@
"Example:": "Пример:", "Example:": "Пример:",
"example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com", "example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com",
"example.com&#10;company.com": "example.com&#10;company.com", "example.com&#10;company.com": "example.com&#10;company.com",
"Excel file": "Файл Excel",
"Excel import": "Импорт из Excel",
"Excellent": "Отлично", "Excellent": "Отлично",
"Exchange rate is required": "Требуется курс обмена", "Exchange rate is required": "Требуется курс обмена",
"Exchange rate must be greater than 0": "Курс обмена должен быть больше 0", "Exchange rate must be greater than 0": "Курс обмена должен быть больше 0",
...@@ -1727,6 +1730,7 @@ ...@@ -1727,6 +1730,7 @@
"Exhausted": "Исчерпано", "Exhausted": "Исчерпано",
"Existing account will be reused": "Существующая учётная запись будет использована повторно", "Existing account will be reused": "Существующая учётная запись будет использована повторно",
"Existing Models ({{count}})": "Существующие модели ({{count}})", "Existing Models ({{count}})": "Существующие модели ({{count}})",
"Existing pricing will be replaced": "Существующие тарифы будут заменены",
"Exists": "Существует", "Exists": "Существует",
"Expand": "Развернуть", "Expand": "Развернуть",
"Expand All": "Развернуть все", "Expand All": "Развернуть все",
...@@ -2256,8 +2260,15 @@ ...@@ -2256,8 +2260,15 @@
"Image to Video": "Изображение в видео", "Image to Video": "Изображение в видео",
"Image Tokens": "Токены изображений", "Image Tokens": "Токены изображений",
"Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "Представьте, что в таблице тарифов три группы: default (коэффициент 1,0), premium (коэффициент 0,5) и vip (коэффициент 0,8). Пользователи из группы vip получают привилегии на уровне аккаунта, а premium — более дешёвый пул каналов, который пользователи могут выбирать для своих токенов.", "Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "Представьте, что в таблице тарифов три группы: default (коэффициент 1,0), premium (коэффициент 0,5) и vip (коэффициент 0,8). Пользователи из группы vip получают привилегии на уровне аккаунта, а premium — более дешёвый пул каналов, который пользователи могут выбирать для своих токенов.",
"Import": "Импортировать",
"Import model metadata from an Excel workbook.": "Импорт метаданных моделей из книги Excel.",
"Import model prices from an Excel workbook.": "Импорт тарифов моделей из книги Excel.",
"Import model pricing": "Импорт тарифов моделей",
"Import models": "Импорт моделей",
"Import to CC Switch": "Импорт в CC Switch", "Import to CC Switch": "Импорт в CC Switch",
"Important": "Важно", "Important": "Важно",
"Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.": "При импорте режим тарификации совпадающих моделей заменяется: тарификация по токенам удаляет фиксированные цены, а тарификация за запрос — настройки коэффициентов.",
"Importing...": "Импорт...",
"In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "В JSON внешний ключ — группа пользователя, внутренний — тарифная группа. Пример ниже означает: пользователи vip платят 0,8 по standard и 0,3 по premium.", "In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "В JSON внешний ключ — группа пользователя, внутренний — тарифная группа. Пример ниже означает: пользователи vip платят 0,8 по standard и 0,3 по premium.",
"In Progress": "Выполняется", "In Progress": "Выполняется",
"In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "В визуальном редакторе это отображается как «Дополнительно видимая» и «Скрыта». В JSON префикс +: (или его отсутствие) добавляет группу, а -: удаляет её.", "In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "В визуальном редакторе это отображается как «Дополнительно видимая» и «Скрыта». В JSON префикс +: (или его отсутствие) добавляет группу, а -: удаляет её.",
...@@ -2625,6 +2636,9 @@ ...@@ -2625,6 +2636,9 @@
"Model enabled successfully": "Модель успешно включена", "Model enabled successfully": "Модель успешно включена",
"Model fixed pricing": "Фиксированная цена модели", "Model fixed pricing": "Фиксированная цена модели",
"Model Group": "Группа моделей", "Model Group": "Группа моделей",
"Model import completed: {{created}} created, {{updated}} updated, {{skipped}} skipped, {{failed}} failed.": "Импорт моделей завершён: создано {{created}}, обновлено {{updated}}, пропущено {{skipped}}, ошибок {{failed}}.",
"Model import failed": "Не удалось импортировать модели",
"Model import requirements": "Требования к импорту моделей",
"Model Limits": "Лимиты модели", "Model Limits": "Лимиты модели",
"Model Mapping": "Сопоставление моделей", "Model Mapping": "Сопоставление моделей",
"Model Mapping (JSON)": "Сопоставление моделей (JSON)", "Model Mapping (JSON)": "Сопоставление моделей (JSON)",
...@@ -2647,6 +2661,8 @@ ...@@ -2647,6 +2661,8 @@
"Model prices": "Цены моделей", "Model prices": "Цены моделей",
"Model prices reset successfully": "Цены моделей успешно сброшены", "Model prices reset successfully": "Цены моделей успешно сброшены",
"Model Pricing": "Тарификация моделей", "Model Pricing": "Тарификация моделей",
"Model pricing import completed: {{updated}} updated, {{perToken}} per-token, {{perRequest}} per-request, {{failed}} failed.": "Импорт тарифов моделей завершён: обновлено {{updated}}, по токенам {{perToken}}, за запрос {{perRequest}}, ошибок {{failed}}.",
"Model pricing import failed": "Не удалось импортировать тарифы моделей",
"Model pull failed: {{msg}}": "Ошибка тяги модели: {{msg}}", "Model pull failed: {{msg}}": "Ошибка тяги модели: {{msg}}",
"Model ratio": "Коэффициент модели", "Model ratio": "Коэффициент модели",
"Model ratios": "Коэффициенты модели", "Model ratios": "Коэффициенты модели",
...@@ -3152,6 +3168,7 @@ ...@@ -3152,6 +3168,7 @@
"overrides for matching model prefix.": "переопределяет цену по совпавшему префиксу модели.", "overrides for matching model prefix.": "переопределяет цену по совпавшему префиксу модели.",
"Overrode user quota from {{from}} to {{to}}": "Квота пользователя изменена с {{from}} на {{to}}", "Overrode user quota from {{from}} to {{to}}": "Квота пользователя изменена с {{from}} на {{to}}",
"Overview": "Обзор", "Overview": "Обзор",
"Overwrite existing models": "Перезаписать существующие модели",
"Overwritten": "Перезаписано", "Overwritten": "Перезаписано",
"Page": "Страница", "Page": "Страница",
"Page {{current}} of {{total}}": "Страница {{current}} из {{total}}", "Page {{current}} of {{total}}": "Страница {{current}} из {{total}}",
...@@ -3217,6 +3234,8 @@ ...@@ -3217,6 +3234,8 @@
"Passwords do not match": "Пароли не совпадают", "Passwords do not match": "Пароли не совпадают",
"Passwords don't match.": "Пароли не совпадают.", "Passwords don't match.": "Пароли не совпадают.",
"Paste Connection Info": "Вставить данные подключения", "Paste Connection Info": "Вставить данные подключения",
"Paste model names here": "Вставьте названия моделей",
"Paste model names separated by commas, spaces, semicolons, or line breaks.": "Разделяйте названия моделей запятыми, пробелами, точками с запятой или переносами строк.",
"Path": "Путь", "Path": "Путь",
"Path not set": "Путь не задан", "Path not set": "Путь не задан",
"Path Regex (one per line)": "Регулярное выражение пути (по одному на строку)", "Path Regex (one per line)": "Регулярное выражение пути (по одному на строку)",
...@@ -3436,6 +3455,7 @@ ...@@ -3436,6 +3455,7 @@
"Pricing Configuration": "Конфигурация ценообразования", "Pricing Configuration": "Конфигурация ценообразования",
"Pricing group example": "Пример группы тарификации", "Pricing group example": "Пример группы тарификации",
"Pricing groups": "Группы тарификации", "Pricing groups": "Группы тарификации",
"Pricing import requirements": "Требования к импорту тарифов",
"Pricing mode": "Режим ценообразования", "Pricing mode": "Режим ценообразования",
"Pricing Ratios": "Коэффициенты ценообразования", "Pricing Ratios": "Коэффициенты ценообразования",
"Pricing Type": "Тип ценообразования", "Pricing Type": "Тип ценообразования",
...@@ -3778,6 +3798,7 @@ ...@@ -3778,6 +3798,7 @@
"Reset usage window": "Сбросить окно использования", "Reset usage window": "Сбросить окно использования",
"Resets in:": "Сброс через:", "Resets in:": "Сброс через:",
"Resetting...": "Сброс...", "Resetting...": "Сброс...",
"Resize column": "Изменить ширину столбца",
"Resolve Conflicts": "Разрешить конфликты", "Resolve Conflicts": "Разрешить конфликты",
"Resource Configuration": "Конфигурация ресурсов", "Resource Configuration": "Конфигурация ресурсов",
"Resources": "Ресурсы", "Resources": "Ресурсы",
...@@ -3966,6 +3987,7 @@ ...@@ -3966,6 +3987,7 @@
"Select all (filtered)": "& Выбрать все отфильтрованные", "Select all (filtered)": "& Выбрать все отфильтрованные",
"Select all models": "Выбрать все модели", "Select all models": "Выбрать все модели",
"Select All Visible": "Выбрать все видимые", "Select All Visible": "Выбрать все видимые",
"Select an .xlsx file to import. The maximum file size is 10 MB.": "Выберите файл .xlsx для импорта размером не более 10 МБ.",
"Select an operation mode and enter the amount": "Выберите режим операции и введите сумму", "Select an operation mode and enter the amount": "Выберите режим операции и введите сумму",
"Select announcement type": "Выбрать тип объявления", "Select announcement type": "Выбрать тип объявления",
"Select at least one field to overwrite.": "Выберите хотя бы одно поле для перезаписи.", "Select at least one field to overwrite.": "Выберите хотя бы одно поле для перезаписи.",
...@@ -4029,6 +4051,7 @@ ...@@ -4029,6 +4051,7 @@
"Selected {{count}}": "Выбрано: {{count}}", "Selected {{count}}": "Выбрано: {{count}}",
"selected channel(s). Leave empty to remove tag.": "выбранный канал(ы). Оставьте пустым, чтобы удалить тег.", "selected channel(s). Leave empty to remove tag.": "выбранный канал(ы). Оставьте пустым, чтобы удалить тег.",
"Selected conflicts were overwritten successfully.": "Выбранные конфликты успешно перезаписаны.", "Selected conflicts were overwritten successfully.": "Выбранные конфликты успешно перезаписаны.",
"Selected file: {{name}}": "Выбранный файл: {{name}}",
"Selected nodes": "Выбранные узлы", "Selected nodes": "Выбранные узлы",
"Selected when creating a token and used as the default billing group for API calls.": "Выбирается при создании токена и используется как группа тарификации по умолчанию для вызовов API.", "Selected when creating a token and used as the default billing group for API calls.": "Выбирается при создании токена и используется как группа тарификации по умолчанию для вызовов API.",
"Self-Use Mode": "Режим самоиспользования", "Self-Use Mode": "Режим самоиспользования",
...@@ -4401,6 +4424,8 @@ ...@@ -4401,6 +4424,8 @@
"The entered text does not match the required text.": "Введенный текст не совпадает с требуемым.", "The entered text does not match the required text.": "Введенный текст не совпадает с требуемым.",
"The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "Окружение (тестовое или рабочее) определяется ключом, который вы вставляете здесь: используйте тестовый ключ при интеграции, затем замените его на рабочий при запуске.", "The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "Окружение (тестовое или рабочее) определяется ключом, который вы вставляете здесь: используйте тестовый ключ при интеграции, затем замените его на рабочий при запуске.",
"The exact model identifier as used in API requests.": "Точный идентификатор модели, используемый в запросах API.", "The exact model identifier as used in API requests.": "Точный идентификатор модели, используемый в запросах API.",
"The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.": "Первая строка должна содержать заголовки, поле model_name обязательно. Для тарификации за запрос укажите fixed_price. В остальных случаях требуется input_price; необязательные столбцы: completion_price, cache_price, create_cache_price, image_price, audio_input_price и audio_output_price.",
"The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.": "Первая строка должна содержать заголовки, поле model_name обязательно. Необязательные столбцы: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.",
"The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "Следующие модели имеют конфликты типов тарификации (фиксированная цена против тарификации по соотношению). Подтвердите, чтобы продолжить изменения.", "The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "Следующие модели имеют конфликты типов тарификации (фиксированная цена против тарификации по соотношению). Подтвердите, чтобы продолжить изменения.",
"The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "Следующие модели в перенаправлении модели не были добавлены в список \"Модели\" и могут не работать при вызове из-за отсутствия доступных моделей:", "The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "Следующие модели в перенаправлении модели не были добавлены в список \"Модели\" и могут не работать при вызове из-за отсутствия доступных моделей:",
"The mapped upstream model(s)": "Сопоставленные upstream модель(и)", "The mapped upstream model(s)": "Сопоставленные upstream модель(и)",
......
...@@ -191,6 +191,7 @@ ...@@ -191,6 +191,7 @@
"Add Model": "Thêm Mô hình", "Add Model": "Thêm Mô hình",
"Add model pricing": "Thêm giá mô hình", "Add model pricing": "Thêm giá mô hình",
"Add Models": "Thêm mô hình", "Add Models": "Thêm mô hình",
"Add models in bulk": "Thêm mô hình hàng loạt",
"Add new amount": "Thêm số tiền mới", "Add new amount": "Thêm số tiền mới",
"Add new redemption code(s) by providing necessary info.": "Thêm mã đổi thưởng mới bằng cách cung cấp thông tin cần thiết.", "Add new redemption code(s) by providing necessary info.": "Thêm mã đổi thưởng mới bằng cách cung cấp thông tin cần thiết.",
"Add OAuth Provider": "Thêm nhà cung cấp OAuth", "Add OAuth Provider": "Thêm nhà cung cấp OAuth",
...@@ -1719,6 +1720,8 @@ ...@@ -1719,6 +1720,8 @@
"Example:": "Ví dụ:", "Example:": "Ví dụ:",
"example.com&#10;blocked-site.com": "example.com\nblocked-site.com", "example.com&#10;blocked-site.com": "example.com\nblocked-site.com",
"example.com&#10;company.com": "example.com\ncompany.com", "example.com&#10;company.com": "example.com\ncompany.com",
"Excel file": "Tệp Excel",
"Excel import": "Nhập từ Excel",
"Excellent": "Tuyệt vời", "Excellent": "Tuyệt vời",
"Exchange rate is required": "Cần có tỷ giá", "Exchange rate is required": "Cần có tỷ giá",
"Exchange rate must be greater than 0": "Tỷ giá phải lớn hơn 0", "Exchange rate must be greater than 0": "Tỷ giá phải lớn hơn 0",
...@@ -1727,6 +1730,7 @@ ...@@ -1727,6 +1730,7 @@
"Exhausted": "Đã cạn kiệt", "Exhausted": "Đã cạn kiệt",
"Existing account will be reused": "Tài khoản hiện có sẽ được sử dụng lại", "Existing account will be reused": "Tài khoản hiện có sẽ được sử dụng lại",
"Existing Models ({{count}})": "Các mô hình hiện có ({{count}})", "Existing Models ({{count}})": "Các mô hình hiện có ({{count}})",
"Existing pricing will be replaced": "Giá hiện tại sẽ bị thay thế",
"Exists": "Tồn tại", "Exists": "Tồn tại",
"Expand": "Mở rộng", "Expand": "Mở rộng",
"Expand All": "Mở rộng tất cả", "Expand All": "Mở rộng tất cả",
...@@ -2256,8 +2260,15 @@ ...@@ -2256,8 +2260,15 @@
"Image to Video": "Ảnh sang video", "Image to Video": "Ảnh sang video",
"Image Tokens": "Token hình ảnh", "Image Tokens": "Token hình ảnh",
"Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "Giả sử bảng định giá có ba nhóm: default (hệ số 1.0), premium (hệ số 0.5) và vip (hệ số 0.8). Người dùng có tài khoản thuộc nhóm vip nhận ưu đãi cấp người dùng, còn premium là một nhóm kênh rẻ hơn mà người dùng có thể chọn cho token của mình.", "Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "Giả sử bảng định giá có ba nhóm: default (hệ số 1.0), premium (hệ số 0.5) và vip (hệ số 0.8). Người dùng có tài khoản thuộc nhóm vip nhận ưu đãi cấp người dùng, còn premium là một nhóm kênh rẻ hơn mà người dùng có thể chọn cho token của mình.",
"Import": "Nhập",
"Import model metadata from an Excel workbook.": "Nhập metadata mô hình từ sổ làm việc Excel.",
"Import model prices from an Excel workbook.": "Nhập giá mô hình từ sổ làm việc Excel.",
"Import model pricing": "Nhập giá mô hình",
"Import models": "Nhập mô hình",
"Import to CC Switch": "Nhập vào CC Switch", "Import to CC Switch": "Nhập vào CC Switch",
"Important": "Quan trọng", "Important": "Quan trọng",
"Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.": "Việc nhập sẽ thay thế chế độ tính phí của các mô hình trùng khớp: tính phí theo token sẽ xóa giá cố định, còn tính phí theo lượt sẽ xóa cấu hình hệ số.",
"Importing...": "Đang nhập...",
"In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "Trong JSON, khóa ngoài là nhóm người dùng, khóa trong là nhóm tính phí. Ví dụ dưới đây nghĩa là: người dùng vip trả 0.8 khi tính phí theo standard và 0.3 khi theo premium.", "In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "Trong JSON, khóa ngoài là nhóm người dùng, khóa trong là nhóm tính phí. Ví dụ dưới đây nghĩa là: người dùng vip trả 0.8 khi tính phí theo standard và 0.3 khi theo premium.",
"In Progress": "Đang xử lý", "In Progress": "Đang xử lý",
"In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "Trong trình chỉnh sửa trực quan, các quy tắc này hiển thị là «Hiển thị thêm» và «Ẩn». Trong JSON, +: (hoặc không có tiền tố) thêm nhóm và -: xóa nhóm.", "In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "Trong trình chỉnh sửa trực quan, các quy tắc này hiển thị là «Hiển thị thêm» và «Ẩn». Trong JSON, +: (hoặc không có tiền tố) thêm nhóm và -: xóa nhóm.",
...@@ -2625,6 +2636,9 @@ ...@@ -2625,6 +2636,9 @@
"Model enabled successfully": "Model đã được kích hoạt thành công", "Model enabled successfully": "Model đã được kích hoạt thành công",
"Model fixed pricing": "Fixed-price model", "Model fixed pricing": "Fixed-price model",
"Model Group": "Nhóm Mô hình", "Model Group": "Nhóm Mô hình",
"Model import completed: {{created}} created, {{updated}} updated, {{skipped}} skipped, {{failed}} failed.": "Đã nhập mô hình: tạo {{created}}, cập nhật {{updated}}, bỏ qua {{skipped}}, lỗi {{failed}}.",
"Model import failed": "Nhập mô hình thất bại",
"Model import requirements": "Yêu cầu nhập mô hình",
"Model Limits": "Giới hạn Mô hình", "Model Limits": "Giới hạn Mô hình",
"Model Mapping": "Ánh xạ mô hình", "Model Mapping": "Ánh xạ mô hình",
"Model Mapping (JSON)": "Ánh xạ mô hình (JSON)", "Model Mapping (JSON)": "Ánh xạ mô hình (JSON)",
...@@ -2647,6 +2661,8 @@ ...@@ -2647,6 +2661,8 @@
"Model prices": "Giá mô hình", "Model prices": "Giá mô hình",
"Model prices reset successfully": "Đã đặt lại giá mô hình thành công", "Model prices reset successfully": "Đã đặt lại giá mô hình thành công",
"Model Pricing": "Định giá mô hình", "Model Pricing": "Định giá mô hình",
"Model pricing import completed: {{updated}} updated, {{perToken}} per-token, {{perRequest}} per-request, {{failed}} failed.": "Đã nhập giá mô hình: cập nhật {{updated}}, theo token {{perToken}}, theo lượt {{perRequest}}, lỗi {{failed}}.",
"Model pricing import failed": "Nhập giá mô hình thất bại",
"Model pull failed: {{msg}}": "Tải mô hình thất bại: {{msg}}", "Model pull failed: {{msg}}": "Tải mô hình thất bại: {{msg}}",
"Model ratio": "Tỷ lệ mô hình", "Model ratio": "Tỷ lệ mô hình",
"Model ratios": "Tỷ lệ mô hình", "Model ratios": "Tỷ lệ mô hình",
...@@ -3152,6 +3168,7 @@ ...@@ -3152,6 +3168,7 @@
"overrides for matching model prefix.": "ghi đè theo tiền tố model tương ứng.", "overrides for matching model prefix.": "ghi đè theo tiền tố model tương ứng.",
"Overrode user quota from {{from}} to {{to}}": "Đã ghi đè hạn mức người dùng từ {{from}} thành {{to}}", "Overrode user quota from {{from}} to {{to}}": "Đã ghi đè hạn mức người dùng từ {{from}} thành {{to}}",
"Overview": "Tổng quan", "Overview": "Tổng quan",
"Overwrite existing models": "Ghi đè các mô hình hiện có",
"Overwritten": "Đã ghi đè", "Overwritten": "Đã ghi đè",
"Page": "Trang", "Page": "Trang",
"Page {{current}} of {{total}}": "Trang {{current}} / {{total}}", "Page {{current}} of {{total}}": "Trang {{current}} / {{total}}",
...@@ -3217,6 +3234,8 @@ ...@@ -3217,6 +3234,8 @@
"Passwords do not match": "Mật khẩu không khớp", "Passwords do not match": "Mật khẩu không khớp",
"Passwords don't match.": "Mật khẩu không khớp.", "Passwords don't match.": "Mật khẩu không khớp.",
"Paste Connection Info": "Dán thông tin kết nối", "Paste Connection Info": "Dán thông tin kết nối",
"Paste model names here": "Dán tên mô hình tại đây",
"Paste model names separated by commas, spaces, semicolons, or line breaks.": "Phân tách tên mô hình bằng dấu phẩy, dấu cách, dấu chấm phẩy hoặc xuống dòng.",
"Path": "Đường dẫn", "Path": "Đường dẫn",
"Path not set": "Chưa đặt đường dẫn", "Path not set": "Chưa đặt đường dẫn",
"Path Regex (one per line)": "Regex đường dẫn (mỗi dòng một mục)", "Path Regex (one per line)": "Regex đường dẫn (mỗi dòng một mục)",
...@@ -3436,6 +3455,7 @@ ...@@ -3436,6 +3455,7 @@
"Pricing Configuration": "Price configuration", "Pricing Configuration": "Price configuration",
"Pricing group example": "Ví dụ nhóm định giá", "Pricing group example": "Ví dụ nhóm định giá",
"Pricing groups": "Nhóm định giá", "Pricing groups": "Nhóm định giá",
"Pricing import requirements": "Yêu cầu nhập giá",
"Pricing mode": "Chế độ định giá", "Pricing mode": "Chế độ định giá",
"Pricing Ratios": "Tỷ lệ định giá", "Pricing Ratios": "Tỷ lệ định giá",
"Pricing Type": "Price type", "Pricing Type": "Price type",
...@@ -3778,6 +3798,7 @@ ...@@ -3778,6 +3798,7 @@
"Reset usage window": "Đặt lại cửa sổ mức dùng", "Reset usage window": "Đặt lại cửa sổ mức dùng",
"Resets in:": "Đặt lại sau:", "Resets in:": "Đặt lại sau:",
"Resetting...": "Đang đặt lại...", "Resetting...": "Đang đặt lại...",
"Resize column": "Đổi kích thước cột",
"Resolve Conflicts": "Giải quyết Xung đột", "Resolve Conflicts": "Giải quyết Xung đột",
"Resource Configuration": "Cấu hình tài nguyên", "Resource Configuration": "Cấu hình tài nguyên",
"Resources": "Tài nguyên", "Resources": "Tài nguyên",
...@@ -3966,6 +3987,7 @@ ...@@ -3966,6 +3987,7 @@
"Select all (filtered)": "Chọn tất cả (đã lọc)", "Select all (filtered)": "Chọn tất cả (đã lọc)",
"Select all models": "Chọn tất cả mô hình", "Select all models": "Chọn tất cả mô hình",
"Select All Visible": "Chọn tất cả hiển thị", "Select All Visible": "Chọn tất cả hiển thị",
"Select an .xlsx file to import. The maximum file size is 10 MB.": "Chọn tệp .xlsx để nhập, kích thước tối đa 10 MB.",
"Select an operation mode and enter the amount": "Chọn chế độ thao tác và nhập số tiền", "Select an operation mode and enter the amount": "Chọn chế độ thao tác và nhập số tiền",
"Select announcement type": "Select notification type", "Select announcement type": "Select notification type",
"Select at least one field to overwrite.": "Chọn ít nhất một trường để ghi đè.", "Select at least one field to overwrite.": "Chọn ít nhất một trường để ghi đè.",
...@@ -4029,6 +4051,7 @@ ...@@ -4029,6 +4051,7 @@
"Selected {{count}}": "Đã chọn {{count}}", "Selected {{count}}": "Đã chọn {{count}}",
"selected channel(s). Leave empty to remove tag.": "Kênh đã chọn. Để trống để xóa thẻ.", "selected channel(s). Leave empty to remove tag.": "Kênh đã chọn. Để trống để xóa thẻ.",
"Selected conflicts were overwritten successfully.": "Các xung đột được chọn đã được ghi đè thành công.", "Selected conflicts were overwritten successfully.": "Các xung đột được chọn đã được ghi đè thành công.",
"Selected file: {{name}}": "Tệp đã chọn: {{name}}",
"Selected nodes": "Nút đã chọn", "Selected nodes": "Nút đã chọn",
"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.", "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", "Self-Use Mode": "Chế độ tự sử dụng",
...@@ -4401,6 +4424,8 @@ ...@@ -4401,6 +4424,8 @@
"The entered text does not match the required text.": "Văn bản đã nhập không khớp với văn bản yêu cầu.", "The entered text does not match the required text.": "Văn bản đã nhập không khớp với văn bản yêu cầu.",
"The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "Môi trường (test hay production) được quyết định bởi khóa bạn dán tại đây — dùng khóa Test khi tích hợp, sau đó đổi sang khóa Production khi chạy chính thức.", "The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "Môi trường (test hay production) được quyết định bởi khóa bạn dán tại đây — dùng khóa Test khi tích hợp, sau đó đổi sang khóa Production khi chạy chính thức.",
"The exact model identifier as used in API requests.": "Mã định danh mô hình chính xác như được sử dụng trong các yêu cầu API.", "The exact model identifier as used in API requests.": "Mã định danh mô hình chính xác như được sử dụng trong các yêu cầu API.",
"The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.": "Hàng đầu tiên phải chứa tiêu đề và bắt buộc có model_name. Với tính phí theo lượt, hãy nhập fixed_price. Nếu không, bắt buộc có input_price; các cột tùy chọn gồm completion_price, cache_price, create_cache_price, image_price, audio_input_price và audio_output_price.",
"The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.": "Hàng đầu tiên phải chứa tiêu đề và bắt buộc có model_name. Các cột tùy chọn: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.",
"The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "Các mô hình sau có xung đột loại thanh toán (giá cố định so với thanh toán theo tỷ lệ). Xác nhận để tiếp tục với các thay đổi.", "The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "Các mô hình sau có xung đột loại thanh toán (giá cố định so với thanh toán theo tỷ lệ). Xác nhận để tiếp tục với các thay đổi.",
"The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "Các mô hình sau trong chuyển hướng mô hình chưa được thêm vào danh sách \"Mô hình\" và có thể gọi thất bại do thiếu các mô hình có sẵn:", "The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "Các mô hình sau trong chuyển hướng mô hình chưa được thêm vào danh sách \"Mô hình\" và có thể gọi thất bại do thiếu các mô hình có sẵn:",
"The mapped upstream model(s)": "Mô hình(s) thượng nguồn được ánh xạ", "The mapped upstream model(s)": "Mô hình(s) thượng nguồn được ánh xạ",
......
...@@ -191,6 +191,7 @@ ...@@ -191,6 +191,7 @@
"Add Model": "新增模型", "Add Model": "新增模型",
"Add model pricing": "新增模型定價", "Add model pricing": "新增模型定價",
"Add Models": "新增模型", "Add Models": "新增模型",
"Add models in bulk": "批次新增模型",
"Add new amount": "新增金額", "Add new amount": "新增金額",
"Add new redemption code(s) by providing necessary info.": "透過提供必要資訊新增兌換碼。", "Add new redemption code(s) by providing necessary info.": "透過提供必要資訊新增兌換碼。",
"Add OAuth Provider": "新增 OAuth 供應商", "Add OAuth Provider": "新增 OAuth 供應商",
...@@ -1502,7 +1503,7 @@ ...@@ -1502,7 +1503,7 @@
"Each tier supports 0~2 conditions (over len, p, c); the last tier is the catch-all without conditions. Use len (full input length, including cache hits) for tier conditions to avoid mis-routing when cache hits reduce p.": "每個檔位支援 0~2 個條件(針對 len、p、c),最後一檔為兜底檔無需條件。建議條件使用 len(完整輸入長度,含緩存命中),避免緩存命中降低 p 導致檔位誤判。", "Each tier supports 0~2 conditions (over len, p, c); the last tier is the catch-all without conditions. Use len (full input length, including cache hits) for tier conditions to avoid mis-routing when cache hits reduce p.": "每個檔位支援 0~2 個條件(針對 len、p、c),最後一檔為兜底檔無需條件。建議條件使用 len(完整輸入長度,含緩存命中),避免緩存命中降低 p 導致檔位誤判。",
"Each tier supports up to 2 conditions; the last tier is the catch-all without conditions. Use full input length for tier conditions to avoid mis-routing when cache hits reduce billable input tokens.": "每個檔位最多支援 2 個條件;最後一個檔位是不帶條件的兜底檔。建議使用完整輸入長度作為檔位條件,避免緩存命中減少收費輸入 token 後誤判檔位。", "Each tier supports up to 2 conditions; the last tier is the catch-all without conditions. Use full input length for tier conditions to avoid mis-routing when cache hits reduce billable input tokens.": "每個檔位最多支援 2 個條件;最後一個檔位是不帶條件的兜底檔。建議使用完整輸入長度作為檔位條件,避免緩存命中減少收費輸入 token 後誤判檔位。",
"Each tier supports up to 2 conditions. The last tier without conditions is the fallback.": "每個階梯最多支援 2 個條件。最後一個無條件階梯作為兜底。", "Each tier supports up to 2 conditions. The last tier without conditions is the fallback.": "每個階梯最多支援 2 個條件。最後一個無條件階梯作為兜底。",
"Earn rewards when your referrals add funds. Transfer accumulated rewards to your balance anytime.": "當您的推薦人儲值時即可獲得獎勵。隨時將累計獎勵轉移到您的餘額。", "Earn rewards when users join through your referral link. Transfer accumulated rewards to your balance anytime.": "使用者透過您的推薦連結註冊後,您即可獲得獎勵。可隨時將累計獎勵轉入餘額。",
"Edit": "編輯", "Edit": "編輯",
"Edit {{title}}": "編輯{{title}}", "Edit {{title}}": "編輯{{title}}",
"Edit all channels with tag:": "編輯所有帶有標籤的渠道:", "Edit all channels with tag:": "編輯所有帶有標籤的渠道:",
...@@ -1719,6 +1720,8 @@ ...@@ -1719,6 +1720,8 @@
"Example:": "示例:", "Example:": "示例:",
"example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com", "example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com",
"example.com&#10;company.com": "example.com&#10;company.com", "example.com&#10;company.com": "example.com&#10;company.com",
"Excel file": "Excel 檔案",
"Excel import": "Excel 匯入",
"Excellent": "優秀", "Excellent": "優秀",
"Exchange rate is required": "匯率為必填項", "Exchange rate is required": "匯率為必填項",
"Exchange rate must be greater than 0": "匯率必須大於 0", "Exchange rate must be greater than 0": "匯率必須大於 0",
...@@ -1727,6 +1730,7 @@ ...@@ -1727,6 +1730,7 @@
"Exhausted": "已耗盡", "Exhausted": "已耗盡",
"Existing account will be reused": "將使用現有用戶", "Existing account will be reused": "將使用現有用戶",
"Existing Models ({{count}})": "現有模型 ({{count}})", "Existing Models ({{count}})": "現有模型 ({{count}})",
"Existing pricing will be replaced": "現有定價將被覆寫",
"Exists": "存在", "Exists": "存在",
"Expand": "展開", "Expand": "展開",
"Expand All": "全部展開", "Expand All": "全部展開",
...@@ -2256,8 +2260,15 @@ ...@@ -2256,8 +2260,15 @@
"Image to Video": "圖生影片", "Image to Video": "圖生影片",
"Image Tokens": "圖像 Token", "Image Tokens": "圖像 Token",
"Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "假設定價分組表裡有三個分組:default(倍率 1.0)、premium(倍率 0.5)、vip(倍率 0.8)。賬號在 vip 分組的用戶享受用戶級待遇,premium 則是一個更便宜的渠道池,用戶建令牌時可以選它。", "Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "假設定價分組表裡有三個分組:default(倍率 1.0)、premium(倍率 0.5)、vip(倍率 0.8)。賬號在 vip 分組的用戶享受用戶級待遇,premium 則是一個更便宜的渠道池,用戶建令牌時可以選它。",
"Import": "匯入",
"Import model metadata from an Excel workbook.": "從 Excel 活頁簿匯入模型中繼資料。",
"Import model prices from an Excel workbook.": "從 Excel 活頁簿匯入模型定價。",
"Import model pricing": "匯入模型定價",
"Import models": "匯入模型",
"Import to CC Switch": "填入 CC Switch", "Import to CC Switch": "填入 CC Switch",
"Important": "重要", "Important": "重要",
"Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.": "匯入會覆寫相符模型的計費方式:按量計費會清除固定價格,按次計費會清除倍率設定。",
"Importing...": "正在匯入...",
"In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "在 JSON 中,外層鍵是用戶分組,內層鍵是收費分組。下面的示例表示:vip 用戶按 standard 收費時用 0.8,按 premium 收費時用 0.3。", "In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "在 JSON 中,外層鍵是用戶分組,內層鍵是收費分組。下面的示例表示:vip 用戶按 standard 收費時用 0.8,按 premium 收費時用 0.3。",
"In Progress": "進行中", "In Progress": "進行中",
"In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "在可視化編輯器中顯示為「額外可見」和「屏蔽」。在 JSON 中,+:(或無前綴)表示新增分組,-: 表示移除分組。", "In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "在可視化編輯器中顯示為「額外可見」和「屏蔽」。在 JSON 中,+:(或無前綴)表示新增分組,-: 表示移除分組。",
...@@ -2625,6 +2636,9 @@ ...@@ -2625,6 +2636,9 @@
"Model enabled successfully": "模型啟用成功", "Model enabled successfully": "模型啟用成功",
"Model fixed pricing": "模型固定定價", "Model fixed pricing": "模型固定定價",
"Model Group": "模型分組", "Model Group": "模型分組",
"Model import completed: {{created}} created, {{updated}} updated, {{skipped}} skipped, {{failed}} failed.": "模型匯入完成:新增 {{created}} 個、更新 {{updated}} 個、略過 {{skipped}} 個、失敗 {{failed}} 個。",
"Model import failed": "模型匯入失敗",
"Model import requirements": "模型匯入要求",
"Model Limits": "模型限制", "Model Limits": "模型限制",
"Model Mapping": "模型映射", "Model Mapping": "模型映射",
"Model Mapping (JSON)": "模型映射 (JSON)", "Model Mapping (JSON)": "模型映射 (JSON)",
...@@ -2647,6 +2661,8 @@ ...@@ -2647,6 +2661,8 @@
"Model prices": "模型價格", "Model prices": "模型價格",
"Model prices reset successfully": "模型價格重置成功", "Model prices reset successfully": "模型價格重置成功",
"Model Pricing": "模型定價", "Model Pricing": "模型定價",
"Model pricing import completed: {{updated}} updated, {{perToken}} per-token, {{perRequest}} per-request, {{failed}} failed.": "模型定價匯入完成:更新 {{updated}} 個、按量 {{perToken}} 個、按次 {{perRequest}} 個、失敗 {{failed}} 個。",
"Model pricing import failed": "模型定價匯入失敗",
"Model pull failed: {{msg}}": "模型拉取失敗:{{msg}}", "Model pull failed: {{msg}}": "模型拉取失敗:{{msg}}",
"Model ratio": "模型倍率", "Model ratio": "模型倍率",
"Model ratios": "模型比例", "Model ratios": "模型比例",
...@@ -3152,6 +3168,7 @@ ...@@ -3152,6 +3168,7 @@
"overrides for matching model prefix.": "為匹配模型前綴的覆蓋價。", "overrides for matching model prefix.": "為匹配模型前綴的覆蓋價。",
"Overrode user quota from {{from}} to {{to}}": "覆蓋用戶額度,從 {{from}} 改為 {{to}}", "Overrode user quota from {{from}} to {{to}}": "覆蓋用戶額度,從 {{from}} 改為 {{to}}",
"Overview": "概覽", "Overview": "概覽",
"Overwrite existing models": "覆寫現有模型",
"Overwritten": "已覆蓋", "Overwritten": "已覆蓋",
"Page": "頁面", "Page": "頁面",
"Page {{current}} of {{total}}": "第 {{current}} 頁,共 {{total}} 頁", "Page {{current}} of {{total}}": "第 {{current}} 頁,共 {{total}} 頁",
...@@ -3217,6 +3234,8 @@ ...@@ -3217,6 +3234,8 @@
"Passwords do not match": "密碼不匹配", "Passwords do not match": "密碼不匹配",
"Passwords don't match.": "兩次輸入的密碼不一致。", "Passwords don't match.": "兩次輸入的密碼不一致。",
"Paste Connection Info": "貼上連線資訊", "Paste Connection Info": "貼上連線資訊",
"Paste model names here": "在此貼上模型名稱",
"Paste model names separated by commas, spaces, semicolons, or line breaks.": "使用逗號、空格、分號或換行分隔模型名稱。",
"Path": "路徑", "Path": "路徑",
"Path not set": "未設定路徑", "Path not set": "未設定路徑",
"Path Regex (one per line)": "路徑正則(每行一個)", "Path Regex (one per line)": "路徑正則(每行一個)",
...@@ -3436,6 +3455,7 @@ ...@@ -3436,6 +3455,7 @@
"Pricing Configuration": "定價設定", "Pricing Configuration": "定價設定",
"Pricing group example": "定價分組示例", "Pricing group example": "定價分組示例",
"Pricing groups": "定價分組", "Pricing groups": "定價分組",
"Pricing import requirements": "定價匯入要求",
"Pricing mode": "定價模式", "Pricing mode": "定價模式",
"Pricing Ratios": "定價比例", "Pricing Ratios": "定價比例",
"Pricing Type": "定價類型", "Pricing Type": "定價類型",
...@@ -3778,6 +3798,7 @@ ...@@ -3778,6 +3798,7 @@
"Reset usage window": "重置用量窗口", "Reset usage window": "重置用量窗口",
"Resets in:": "將於以下時間重置:", "Resets in:": "將於以下時間重置:",
"Resetting...": "重置中...", "Resetting...": "重置中...",
"Resize column": "調整欄寬",
"Resolve Conflicts": "解決衝突", "Resolve Conflicts": "解決衝突",
"Resource Configuration": "資源設定", "Resource Configuration": "資源設定",
"Resources": "資源", "Resources": "資源",
...@@ -3966,6 +3987,7 @@ ...@@ -3966,6 +3987,7 @@
"Select all (filtered)": "全選(篩選結果)", "Select all (filtered)": "全選(篩選結果)",
"Select all models": "選擇所有模型", "Select all models": "選擇所有模型",
"Select All Visible": "全選目前", "Select All Visible": "全選目前",
"Select an .xlsx file to import. The maximum file size is 10 MB.": "請選擇要匯入的 .xlsx 檔案,檔案大小不能超過 10 MB。",
"Select an operation mode and enter the amount": "選擇操作模式並輸入金額", "Select an operation mode and enter the amount": "選擇操作模式並輸入金額",
"Select announcement type": "選擇公告類型", "Select announcement type": "選擇公告類型",
"Select at least one field to overwrite.": "請選擇至少一個要覆蓋的欄位。", "Select at least one field to overwrite.": "請選擇至少一個要覆蓋的欄位。",
...@@ -4029,6 +4051,7 @@ ...@@ -4029,6 +4051,7 @@
"Selected {{count}}": "已選 {{count}} 個", "Selected {{count}}": "已選 {{count}} 個",
"selected channel(s). Leave empty to remove tag.": "選定的渠道。留空以移除標籤。", "selected channel(s). Leave empty to remove tag.": "選定的渠道。留空以移除標籤。",
"Selected conflicts were overwritten successfully.": "選中的衝突已成功覆蓋。", "Selected conflicts were overwritten successfully.": "選中的衝突已成功覆蓋。",
"Selected file: {{name}}": "已選擇檔案:{{name}}",
"Selected nodes": "已選節點", "Selected nodes": "已選節點",
"Selected when creating a token and used as the default billing group for API calls.": "建立令牌時選擇,用作 API 呼叫的預設收費分組。", "Selected when creating a token and used as the default billing group for API calls.": "建立令牌時選擇,用作 API 呼叫的預設收費分組。",
"Self-Use Mode": "自用模式", "Self-Use Mode": "自用模式",
...@@ -4401,6 +4424,8 @@ ...@@ -4401,6 +4424,8 @@
"The entered text does not match the required text.": "輸入文字與要求文字不匹配。", "The entered text does not match the required text.": "輸入文字與要求文字不匹配。",
"The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "環境(測試或生產)由你在此貼上的金鑰決定。整合期間使用測試金鑰,上線時再切換為生產金鑰。", "The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "環境(測試或生產)由你在此貼上的金鑰決定。整合期間使用測試金鑰,上線時再切換為生產金鑰。",
"The exact model identifier as used in API requests.": "API 請求中使用的確切模型標識符。", "The exact model identifier as used in API requests.": "API 請求中使用的確切模型標識符。",
"The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.": "第一列必須是表頭,且 model_name 為必填欄位。按次計費請填寫 fixed_price;否則必須填寫 input_price。可選欄位:completion_price、cache_price、create_cache_price、image_price、audio_input_price、audio_output_price。",
"The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.": "第一列必須是表頭,且 model_name 為必填欄位。可選欄位:description、icon、tags、vendor_name、endpoints、status、sync_official、name_rule。",
"The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "以下模型存在收費類型衝突(固定價格 vs 比例收費)。確認以繼續更改。", "The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "以下模型存在收費類型衝突(固定價格 vs 比例收費)。確認以繼續更改。",
"The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "模型重新導向裡的下列模型尚未新增到「模型」列表,呼叫時會因為缺少可用模型而失敗:", "The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "模型重新導向裡的下列模型尚未新增到「模型」列表,呼叫時會因為缺少可用模型而失敗:",
"The mapped upstream model(s)": "映射的上游模型", "The mapped upstream model(s)": "映射的上游模型",
......
...@@ -191,6 +191,7 @@ ...@@ -191,6 +191,7 @@
"Add Model": "添加模型", "Add Model": "添加模型",
"Add model pricing": "添加模型定价", "Add model pricing": "添加模型定价",
"Add Models": "新增模型", "Add Models": "新增模型",
"Add models in bulk": "批量添加模型",
"Add new amount": "添加新金额", "Add new amount": "添加新金额",
"Add new redemption code(s) by providing necessary info.": "通过提供必要信息添加新的兑换码。", "Add new redemption code(s) by providing necessary info.": "通过提供必要信息添加新的兑换码。",
"Add OAuth Provider": "添加 OAuth 提供商", "Add OAuth Provider": "添加 OAuth 提供商",
...@@ -1719,6 +1720,8 @@ ...@@ -1719,6 +1720,8 @@
"Example:": "示例:", "Example:": "示例:",
"example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com", "example.com&#10;blocked-site.com": "example.com&#10;blocked-site.com",
"example.com&#10;company.com": "example.com&#10;company.com", "example.com&#10;company.com": "example.com&#10;company.com",
"Excel file": "Excel 文件",
"Excel import": "Excel 导入",
"Excellent": "优秀", "Excellent": "优秀",
"Exchange rate is required": "汇率为必填项", "Exchange rate is required": "汇率为必填项",
"Exchange rate must be greater than 0": "汇率必须大于 0", "Exchange rate must be greater than 0": "汇率必须大于 0",
...@@ -1727,6 +1730,7 @@ ...@@ -1727,6 +1730,7 @@
"Exhausted": "已耗尽", "Exhausted": "已耗尽",
"Existing account will be reused": "将使用现有账户", "Existing account will be reused": "将使用现有账户",
"Existing Models ({{count}})": "现有模型 ({{count}})", "Existing Models ({{count}})": "现有模型 ({{count}})",
"Existing pricing will be replaced": "现有定价将被覆盖",
"Exists": "存在", "Exists": "存在",
"Expand": "展开", "Expand": "展开",
"Expand All": "全部展开", "Expand All": "全部展开",
...@@ -2256,8 +2260,15 @@ ...@@ -2256,8 +2260,15 @@
"Image to Video": "图生视频", "Image to Video": "图生视频",
"Image Tokens": "图像 Token", "Image Tokens": "图像 Token",
"Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "假设定价分组表里有三个分组:default(倍率 1.0)、premium(倍率 0.5)、vip(倍率 0.8)。账号在 vip 分组的用户享受用户级待遇,premium 则是一个更便宜的渠道池,用户建令牌时可以选它。", "Imagine the pricing table has three groups: default (ratio 1.0), premium (ratio 0.5), and vip (ratio 0.8). Users whose account is in the vip group get user-level perks, and premium is a cheaper channel pool that users can pick for their tokens.": "假设定价分组表里有三个分组:default(倍率 1.0)、premium(倍率 0.5)、vip(倍率 0.8)。账号在 vip 分组的用户享受用户级待遇,premium 则是一个更便宜的渠道池,用户建令牌时可以选它。",
"Import": "导入",
"Import model metadata from an Excel workbook.": "从 Excel 工作簿导入模型元数据。",
"Import model prices from an Excel workbook.": "从 Excel 工作簿导入模型定价。",
"Import model pricing": "导入模型定价",
"Import models": "导入模型",
"Import to CC Switch": "填入 CC Switch", "Import to CC Switch": "填入 CC Switch",
"Important": "重要", "Important": "重要",
"Importing replaces the billing mode for matching models: per-token billing clears fixed prices, while per-request billing clears ratio settings.": "导入会覆盖匹配模型的计费方式:按量计费会清除固定价格,按次计费会清除倍率设置。",
"Importing...": "正在导入...",
"In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "在 JSON 中,外层键是用户分组,内层键是计费分组。下面的示例表示:vip 用户按 standard 计费时用 0.8,按 premium 计费时用 0.3。", "In JSON, the user group is the outer key and the billing group is the inner key. The example below means: vip users pay 0.8 when billed as standard, and 0.3 when billed as premium.": "在 JSON 中,外层键是用户分组,内层键是计费分组。下面的示例表示:vip 用户按 standard 计费时用 0.8,按 premium 计费时用 0.3。",
"In Progress": "进行中", "In Progress": "进行中",
"In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "在可视化编辑器中显示为「额外可见」和「屏蔽」。在 JSON 中,+:(或无前缀)表示添加分组,-: 表示移除分组。", "In the visual editor these appear as Extra visible and Hidden. In JSON, +: (or no prefix) adds a group and -: removes one.": "在可视化编辑器中显示为「额外可见」和「屏蔽」。在 JSON 中,+:(或无前缀)表示添加分组,-: 表示移除分组。",
...@@ -2625,6 +2636,9 @@ ...@@ -2625,6 +2636,9 @@
"Model enabled successfully": "模型启用成功", "Model enabled successfully": "模型启用成功",
"Model fixed pricing": "模型固定定价", "Model fixed pricing": "模型固定定价",
"Model Group": "模型分组", "Model Group": "模型分组",
"Model import completed: {{created}} created, {{updated}} updated, {{skipped}} skipped, {{failed}} failed.": "模型导入完成:新增 {{created}} 个,更新 {{updated}} 个,跳过 {{skipped}} 个,失败 {{failed}} 个。",
"Model import failed": "模型导入失败",
"Model import requirements": "模型导入要求",
"Model Limits": "模型限制", "Model Limits": "模型限制",
"Model Mapping": "模型映射", "Model Mapping": "模型映射",
"Model Mapping (JSON)": "模型映射 (JSON)", "Model Mapping (JSON)": "模型映射 (JSON)",
...@@ -2647,6 +2661,8 @@ ...@@ -2647,6 +2661,8 @@
"Model prices": "模型价格", "Model prices": "模型价格",
"Model prices reset successfully": "模型价格重置成功", "Model prices reset successfully": "模型价格重置成功",
"Model Pricing": "模型定价", "Model Pricing": "模型定价",
"Model pricing import completed: {{updated}} updated, {{perToken}} per-token, {{perRequest}} per-request, {{failed}} failed.": "模型定价导入完成:更新 {{updated}} 个,按量 {{perToken}} 个,按次 {{perRequest}} 个,失败 {{failed}} 个。",
"Model pricing import failed": "模型定价导入失败",
"Model pull failed: {{msg}}": "模型拉取失败:{{msg}}", "Model pull failed: {{msg}}": "模型拉取失败:{{msg}}",
"Model ratio": "模型倍率", "Model ratio": "模型倍率",
"Model ratios": "模型比例", "Model ratios": "模型比例",
...@@ -3152,6 +3168,7 @@ ...@@ -3152,6 +3168,7 @@
"overrides for matching model prefix.": "为匹配模型前缀的覆盖价。", "overrides for matching model prefix.": "为匹配模型前缀的覆盖价。",
"Overrode user quota from {{from}} to {{to}}": "覆盖用户额度,从 {{from}} 改为 {{to}}", "Overrode user quota from {{from}} to {{to}}": "覆盖用户额度,从 {{from}} 改为 {{to}}",
"Overview": "概览", "Overview": "概览",
"Overwrite existing models": "覆盖已存在的模型",
"Overwritten": "已覆盖", "Overwritten": "已覆盖",
"Page": "页面", "Page": "页面",
"Page {{current}} of {{total}}": "第 {{current}} 页,共 {{total}} 页", "Page {{current}} of {{total}}": "第 {{current}} 页,共 {{total}} 页",
...@@ -3217,6 +3234,8 @@ ...@@ -3217,6 +3234,8 @@
"Passwords do not match": "密码不匹配", "Passwords do not match": "密码不匹配",
"Passwords don't match.": "两次输入的密码不一致。", "Passwords don't match.": "两次输入的密码不一致。",
"Paste Connection Info": "粘贴连接信息", "Paste Connection Info": "粘贴连接信息",
"Paste model names here": "在此粘贴模型名称",
"Paste model names separated by commas, spaces, semicolons, or line breaks.": "使用逗号、空格、分号或换行分隔模型名称。",
"Path": "路径", "Path": "路径",
"Path not set": "未设置路径", "Path not set": "未设置路径",
"Path Regex (one per line)": "路径正则(每行一个)", "Path Regex (one per line)": "路径正则(每行一个)",
...@@ -3436,6 +3455,7 @@ ...@@ -3436,6 +3455,7 @@
"Pricing Configuration": "定价配置", "Pricing Configuration": "定价配置",
"Pricing group example": "定价分组示例", "Pricing group example": "定价分组示例",
"Pricing groups": "定价分组", "Pricing groups": "定价分组",
"Pricing import requirements": "定价导入要求",
"Pricing mode": "定价模式", "Pricing mode": "定价模式",
"Pricing Ratios": "定价比例", "Pricing Ratios": "定价比例",
"Pricing Type": "定价类型", "Pricing Type": "定价类型",
...@@ -3778,6 +3798,7 @@ ...@@ -3778,6 +3798,7 @@
"Reset usage window": "重置用量窗口", "Reset usage window": "重置用量窗口",
"Resets in:": "将于以下时间重置:", "Resets in:": "将于以下时间重置:",
"Resetting...": "重置中...", "Resetting...": "重置中...",
"Resize column": "调整列宽",
"Resolve Conflicts": "解决冲突", "Resolve Conflicts": "解决冲突",
"Resource Configuration": "资源配置", "Resource Configuration": "资源配置",
"Resources": "资源", "Resources": "资源",
...@@ -3966,6 +3987,7 @@ ...@@ -3966,6 +3987,7 @@
"Select all (filtered)": "全选(筛选结果)", "Select all (filtered)": "全选(筛选结果)",
"Select all models": "选择所有模型", "Select all models": "选择所有模型",
"Select All Visible": "全选当前", "Select All Visible": "全选当前",
"Select an .xlsx file to import. The maximum file size is 10 MB.": "请选择要导入的 .xlsx 文件,文件大小不能超过 10 MB。",
"Select an operation mode and enter the amount": "选择操作模式并输入金额", "Select an operation mode and enter the amount": "选择操作模式并输入金额",
"Select announcement type": "选择公告类型", "Select announcement type": "选择公告类型",
"Select at least one field to overwrite.": "请选择至少一个要覆盖的字段。", "Select at least one field to overwrite.": "请选择至少一个要覆盖的字段。",
...@@ -4029,6 +4051,7 @@ ...@@ -4029,6 +4051,7 @@
"Selected {{count}}": "已选 {{count}} 个", "Selected {{count}}": "已选 {{count}} 个",
"selected channel(s). Leave empty to remove tag.": "选定的渠道。留空以移除标签。", "selected channel(s). Leave empty to remove tag.": "选定的渠道。留空以移除标签。",
"Selected conflicts were overwritten successfully.": "选中的冲突已成功覆盖。", "Selected conflicts were overwritten successfully.": "选中的冲突已成功覆盖。",
"Selected file: {{name}}": "已选择文件:{{name}}",
"Selected nodes": "已选节点", "Selected nodes": "已选节点",
"Selected when creating a token and used as the default billing group for API calls.": "创建令牌时选择,用作 API 调用的默认计费分组。", "Selected when creating a token and used as the default billing group for API calls.": "创建令牌时选择,用作 API 调用的默认计费分组。",
"Self-Use Mode": "自用模式", "Self-Use Mode": "自用模式",
...@@ -4401,6 +4424,8 @@ ...@@ -4401,6 +4424,8 @@
"The entered text does not match the required text.": "输入文本与要求文本不匹配。", "The entered text does not match the required text.": "输入文本与要求文本不匹配。",
"The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "环境(测试或生产)由你在此粘贴的密钥决定。集成期间使用测试密钥,上线时再切换为生产密钥。", "The environment (test vs production) is decided by the key you paste here — use the Test key while integrating, then swap to the Production key when going live.": "环境(测试或生产)由你在此粘贴的密钥决定。集成期间使用测试密钥,上线时再切换为生产密钥。",
"The exact model identifier as used in API requests.": "API 请求中使用的确切模型标识符。", "The exact model identifier as used in API requests.": "API 请求中使用的确切模型标识符。",
"The first row must contain headers, and model_name is required. For per-request billing, provide fixed_price. Otherwise input_price is required; optional columns are completion_price, cache_price, create_cache_price, image_price, audio_input_price, and audio_output_price.": "第一行必须是表头,且 model_name 为必填列。按次计费请填写 fixed_price;否则必须填写 input_price。可选列:completion_price、cache_price、create_cache_price、image_price、audio_input_price、audio_output_price。",
"The first row must contain headers, and model_name is required. Optional columns: description, icon, tags, vendor_name, endpoints, status, sync_official, name_rule.": "第一行必须是表头,且 model_name 为必填列。可选列:description、icon、tags、vendor_name、endpoints、status、sync_official、name_rule。",
"The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "以下模型存在计费类型冲突(固定价格 vs 比例计费)。确认以继续更改。", "The following models have billing type conflicts (fixed price vs ratio billing). Confirm to proceed with the changes.": "以下模型存在计费类型冲突(固定价格 vs 比例计费)。确认以继续更改。",
"The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "模型重定向里的下列模型尚未添加到\"模型\"列表,调用时会因为缺少可用模型而失败:", "The following models in the model redirect have not been added to the \"Models\" list and may fail during invocation due to missing available models:": "模型重定向里的下列模型尚未添加到\"模型\"列表,调用时会因为缺少可用模型而失败:",
"The mapped upstream model(s)": "映射的上游模型", "The mapped upstream model(s)": "映射的上游模型",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment