Commit 4f24e2de by ccran

feat: add export deploy;

parent 417614a1
#!/bin/bash
# 在 Mac(包括 Apple Silicon)上构建目标架构镜像,上传到 Ubuntu 并导入。
# 用法:bash bin/docker-upload-ubuntu.sh [user@host]
# 示例:SSH_PORT=22 IMAGE_TAG=new-api:export bash bin/docker-upload-ubuntu.sh
# SSH 使用本机 ~/.ssh/config、密钥或交互式密码认证,同次执行复用连接。
set -euo pipefail
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
sed -n '2,5p' "$0"
exit 0
fi
if (( $# > 1 )); then
echo "用法:bash $0 [user@host]" >&2
exit 1
fi
remote_host=${1:-root@8.136.9.68}
ssh_port=${SSH_PORT:-22}
image_tag=${IMAGE_TAG:-new-api:export}
project_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
if [[ "$remote_host" == -* || "$remote_host" == *[[:space:]]* || -z "$remote_host" ]]; then
echo "无效的 SSH 目标:$remote_host" >&2
exit 1
fi
if [[ ! "$ssh_port" =~ ^[0-9]+$ ]]; then
echo "SSH_PORT 必须是端口号。" >&2
exit 1
fi
for command_name in docker ssh scp; do
command -v "$command_name" >/dev/null || {
echo "缺少命令:$command_name" >&2
exit 1
}
done
docker info >/dev/null
docker buildx version >/dev/null
# 使用短路径,避免 macOS 的临时目录导致 SSH 控制套接字路径过长。
# 临时文件放在构建上下文之外,避免将导出的镜像再次打包进镜像。
export_dir=$(mktemp -d /tmp/new-api-export.XXXXXX)
control_path="$export_dir/ssh"
ssh_options=(-o "ControlPath=$control_path" -o ControlMaster=no -o BatchMode=yes)
cleanup() {
ssh "${ssh_options[@]}" -p "$ssh_port" -O exit "$remote_host" >/dev/null 2>&1 || true
rm -rf -- "$export_dir"
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
archive_path="$export_dir/new-api-export.tar"
# 主连接只认证一次,并在耗时的镜像构建期间保持连接。
ssh -p "$ssh_port" -o "ControlPath=$control_path" -o ControlMaster=yes \
-o ControlPersist=no -o ServerAliveInterval=30 -o ServerAliveCountMax=3 \
-fN "$remote_host"
echo "检查 Ubuntu 架构及 Docker 权限:$remote_host"
remote_arch=$(ssh "${ssh_options[@]}" -p "$ssh_port" "$remote_host" 'set -e; docker info >/dev/null; uname -m')
case "$remote_arch" in
x86_64) platform=linux/amd64 ;;
aarch64|arm64) platform=linux/arm64 ;;
*) echo "不支持的远端架构:$remote_arch" >&2; exit 1 ;;
esac
echo "构建 ${image_tag},目标平台:${platform}(跨架构构建可能较慢)"
docker buildx build --platform "$platform" --load \
--tag "$image_tag" --file "$project_dir/Dockerfile" "$project_dir"
echo "导出镜像"
docker save -o "$archive_path" "$image_tag"
echo "上传至 $remote_host:~/new-api-export.tar"
scp "${ssh_options[@]}" -P "$ssh_port" "$archive_path" "$remote_host:new-api-export.tar"
echo "在 Ubuntu 导入镜像"
ssh "${ssh_options[@]}" -p "$ssh_port" "$remote_host" 'docker load -i "$HOME/new-api-export.tar"'
echo "完成:远端已导入 ${image_tag}${platform})。"
echo "远端归档保留在 ~/new-api-export.tar;本地临时归档会自动删除。"
echo "现有容器不会自动重启,请在确认部署配置后重新创建容器。"
...@@ -119,8 +119,11 @@ export async function searchChannels( ...@@ -119,8 +119,11 @@ export async function searchChannels(
/** /**
* Get single channel by ID * Get single channel by ID
*/ */
export async function getChannel(id: number): Promise<GetChannelResponse> { export async function getChannel(
const res = await api.get(`/api/channel/${id}`) id: number,
config?: ApiRequestConfig
): Promise<GetChannelResponse> {
const res = await api.get(`/api/channel/${id}`, config)
return res.data return res.data
} }
......
...@@ -111,6 +111,7 @@ function buildSearchSourceKey(values: { ...@@ -111,6 +111,7 @@ function buildSearchSourceKey(values: {
} }
interface CommonLogsFilterBarProps<TData> { interface CommonLogsFilterBarProps<TData> {
exportAction?: React.ReactNode
table: Table<TData> table: Table<TData>
} }
...@@ -486,7 +487,12 @@ export function CommonLogsFilterBar<TData>( ...@@ -486,7 +487,12 @@ export function CommonLogsFilterBar<TData>(
table={props.table} table={props.table}
compactMobile compactMobile
stats={statsBar} stats={statsBar}
actionStart={sensitiveToggle} actionStart={
<>
{sensitiveToggle}
{props.exportAction}
</>
}
primaryFilters={ primaryFilters={
<> <>
{dateRangeFilter} {dateRangeFilter}
......
/*
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 { Download, Loader2 } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import { Button } from '@/components/ui/button'
import {
buildLogsWorkbook,
collectExportLogs,
MAX_EXPORT_LOGS,
} from '../lib/export'
import type { FetchLogsConfig } from '../types'
export function LogsExportButton(props: {
config: FetchLogsConfig
disabled?: boolean
}) {
const { t } = useTranslation()
const [progress, setProgress] = useState<number | null>(null)
const activeExport = useRef<AbortController | null>(null)
const scopeKey = JSON.stringify(props.config)
useEffect(() => {
setProgress(null)
return () => {
activeExport.current?.abort()
activeExport.current = null
}
}, [scopeKey])
const handleExport = async () => {
if (activeExport.current) return
const controller = new AbortController()
activeExport.current = controller
setProgress(0)
try {
const logs = await collectExportLogs(
props.config,
controller.signal,
setProgress,
t
)
if (logs.length === 0) {
toast.info(t('No Logs Found'))
return
}
const workbook = await buildLogsWorkbook(logs, props.config, t)
const XLSX = await import('xlsx')
controller.signal.throwIfAborted()
XLSX.writeFile(
workbook,
`usage-logs-${props.config.logCategory}-${Date.now()}.xlsx`,
{ compression: true }
)
} catch (error) {
if (!controller.signal.aborted) {
toast.error(
error instanceof Error ? error.message : t('Failed to export logs')
)
}
} finally {
if (activeExport.current === controller) {
activeExport.current = null
setProgress(null)
}
}
}
return (
<>
<Button
variant='outline'
disabled={props.disabled || progress !== null}
aria-busy={progress !== null}
title={t('Export applied filters to Excel (up to {{count}} rows).', {
count: MAX_EXPORT_LOGS,
})}
onClick={handleExport}
>
{progress !== null ? (
<Loader2 className='animate-spin' aria-hidden='true' />
) : (
<Download aria-hidden='true' />
)}
{progress !== null
? t('Exporting {{count}} rows', { count: progress })
: t('Export Excel')}
</Button>
{progress !== null && (
<Button
variant='ghost'
onClick={() => {
activeExport.current?.abort()
activeExport.current = null
setProgress(null)
}}
>
{t('Cancel')}
</Button>
)}
</>
)
}
...@@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com ...@@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com
*/ */
import { useQueryClient, useIsFetching } from '@tanstack/react-query' import { useQueryClient, useIsFetching } from '@tanstack/react-query'
import { useNavigate, getRouteApi } from '@tanstack/react-router' import { useNavigate, getRouteApi } from '@tanstack/react-router'
import { type Table } from '@tanstack/react-table' import type { Table } from '@tanstack/react-table'
import { useState, useEffect, useCallback } from 'react' import { useState, useEffect, useCallback } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
...@@ -39,6 +39,7 @@ type TaskLikeLogCategory = Extract<LogCategory, 'drawing' | 'task'> ...@@ -39,6 +39,7 @@ type TaskLikeLogCategory = Extract<LogCategory, 'drawing' | 'task'>
type TaskLogsFilters = DrawingLogFilters | TaskLogFilters type TaskLogsFilters = DrawingLogFilters | TaskLogFilters
interface TaskLogsFilterBarProps<TData> { interface TaskLogsFilterBarProps<TData> {
exportAction?: React.ReactNode
table: Table<TData> table: Table<TData>
logCategory: TaskLikeLogCategory logCategory: TaskLikeLogCategory
} }
...@@ -201,6 +202,7 @@ export function TaskLogsFilterBar<TData>(props: TaskLogsFilterBarProps<TData>) { ...@@ -201,6 +202,7 @@ export function TaskLogsFilterBar<TData>(props: TaskLogsFilterBarProps<TData>) {
return ( return (
<LogsFilterToolbar <LogsFilterToolbar
actionStart={props.exportAction}
table={props.table} table={props.table}
primaryFilters={ primaryFilters={
<> <>
......
...@@ -41,6 +41,7 @@ import { parseLogOther } from '../lib/format' ...@@ -41,6 +41,7 @@ import { parseLogOther } from '../lib/format'
import { fetchLogsByCategory } from '../lib/utils' import { fetchLogsByCategory } from '../lib/utils'
import type { LogCategory } from '../types' import type { LogCategory } from '../types'
import { CommonLogsFilterBar } from './common-logs-filter-bar' import { CommonLogsFilterBar } from './common-logs-filter-bar'
import { LogsExportButton } from './logs-export-button'
import { TaskLogsFilterBar } from './task-logs-filter-bar' import { TaskLogsFilterBar } from './task-logs-filter-bar'
import { UsageLogsMobileList } from './usage-logs-mobile-card' import { UsageLogsMobileList } from './usage-logs-mobile-card'
import { useLogsViewScope, type LogsViewAccess } from './usage-logs-provider' import { useLogsViewScope, type LogsViewAccess } from './usage-logs-provider'
...@@ -187,6 +188,19 @@ export function UsageLogsTable({ logCategory }: UsageLogsTableProps) { ...@@ -187,6 +188,19 @@ export function UsageLogsTable({ logCategory }: UsageLogsTableProps) {
}) })
const isCommon = logCategory === 'common' const isCommon = logCategory === 'common'
const exportAction = (
<LogsExportButton
config={{
logCategory,
isAdmin,
page: 1,
pageSize: 100,
searchParams,
columnFilters,
}}
disabled={isFetching || !logs.length}
/>
)
return ( return (
<DataTablePage <DataTablePage
...@@ -213,9 +227,13 @@ export function UsageLogsTable({ logCategory }: UsageLogsTableProps) { ...@@ -213,9 +227,13 @@ export function UsageLogsTable({ logCategory }: UsageLogsTableProps) {
} }
toolbar={ toolbar={
isCommon ? ( isCommon ? (
<CommonLogsFilterBar table={table} /> <CommonLogsFilterBar table={table} exportAction={exportAction} />
) : ( ) : (
<TaskLogsFilterBar table={table} logCategory={logCategory} /> <TaskLogsFilterBar
table={table}
logCategory={logCategory}
exportAction={exportAction}
/>
) )
} }
renderRow={(row) => { renderRow={(row) => {
......
/*
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 type { TFunction } from 'i18next'
import type { WorkBook } from 'xlsx'
import { getChannel } from '@/features/channels/api'
import { formatLogQuota, formatTimestampToDate } from '@/lib/format'
import type { UsageLog } from '../data/schema'
import type { FetchLogsConfig, MidjourneyLog, TaskLog } from '../types'
import {
fetchLogsByCategory,
getDefaultTimeRange,
getLogTypeConfig,
} from './utils'
export const MAX_EXPORT_LOGS = 10_000
type ExportTaskLog = TaskLog & { channel_name?: string }
type ExportLog = UsageLog | MidjourneyLog | ExportTaskLog
export async function collectExportLogs(
config: FetchLogsConfig,
signal: AbortSignal,
onProgress: (count: number) => void,
t: TFunction
): Promise<ExportLog[]> {
const defaults = getDefaultTimeRange()
const searchParams = { ...config.searchParams }
if (!(searchParams.startTime ?? searchParams.endTime)) {
searchParams.startTime = defaults.start.getTime()
searchParams.endTime = defaults.end.getTime()
}
// Keep the time window fixed while fetching subsequent pages.
searchParams.endTime = Math.min(
Number(searchParams.endTime) || Date.now(),
Date.now()
)
const logs: ExportLog[] = []
let total = 0
for (let page = 1; page === 1 || logs.length < total; page++) {
signal.throwIfAborted()
const result = await fetchLogsByCategory({
...config,
searchParams,
page,
pageSize: 100,
})
signal.throwIfAborted()
if (!result.success || !result.data) {
throw new Error(result.message || t('Failed to load logs'))
}
const items = result.data.items ?? []
if (page === 1) total = result.data.total
if (
total > MAX_EXPORT_LOGS ||
logs.length + items.length > MAX_EXPORT_LOGS
) {
throw new Error(
t(
'Too many logs to export. Narrow the filters to {{count}} rows or fewer.',
{ count: MAX_EXPORT_LOGS }
)
)
}
if (
result.data.total !== total ||
(items.length === 0 && logs.length < total)
) {
throw new Error(t('Logs changed during export. Please try again.'))
}
logs.push(...items)
onProgress(logs.length)
}
if (config.logCategory === 'task' && config.isAdmin && logs.length > 0) {
const tasks = logs as ExportTaskLog[]
const channelIds = [...new Set(tasks.map((log) => log.channel_id))].filter(
(id) => id > 0
)
const channelNames = new Map<number, string>()
// Bound concurrent lookups and keep deleted/inaccessible channels exportable by ID.
for (let offset = 0; offset < channelIds.length; offset += 5) {
signal.throwIfAborted()
const batch = channelIds.slice(offset, offset + 5)
const results = await Promise.allSettled(
batch.map((id) =>
getChannel(id, {
signal,
skipBusinessError: true,
skipErrorHandler: true,
})
)
)
signal.throwIfAborted()
results.forEach((result, index) => {
if (
result.status === 'fulfilled' &&
result.value.success &&
result.value.data?.name
) {
channelNames.set(batch[index], result.value.data.name)
}
})
}
return tasks.map((log) => ({
...log,
channel_name: channelNames.get(log.channel_id),
}))
}
return logs
}
export async function buildLogsWorkbook(
logs: ExportLog[],
config: Pick<FetchLogsConfig, 'logCategory' | 'isAdmin'>,
t: TFunction
): Promise<WorkBook> {
const XLSX = await import('xlsx')
let headers: string[]
let rows: (string | number | boolean)[][]
if (config.logCategory === 'common') {
headers = [
t('Time'),
t('Type'),
t('Model'),
t('Token'),
t('Group'),
t('Input Tokens'),
t('Output Tokens'),
t('Cost'),
t('Quota'),
t('Duration (seconds)'),
t('Stream'),
t('Request ID'),
t('Upstream Request ID'),
t('IP Address'),
t('Content'),
]
if (config.isAdmin) headers.push(t('Username'), t('Channel'))
rows = (logs as UsageLog[]).map((log) => {
const row = [
formatTimestampToDate(log.created_at),
t(getLogTypeConfig(log.type).label),
log.model_name,
log.token_name,
log.group,
log.prompt_tokens,
log.completion_tokens,
formatLogQuota(log.quota),
log.quota,
log.use_time,
log.is_stream,
log.request_id,
log.upstream_request_id,
log.ip,
log.content,
]
if (config.isAdmin) {
row.push(log.username, log.channel_name || log.channel)
}
return row
})
} else {
headers = [
t('Task ID'),
t('Submit Time'),
t('Finish Time'),
t('Action'),
t('Status'),
t('Progress'),
t('Fail Reason'),
]
if (config.logCategory === 'drawing') {
headers.push(t('Prompt'))
rows = (logs as MidjourneyLog[]).map((log) => [
log.mj_id,
formatTimestampToDate(log.submit_time, 'milliseconds'),
formatTimestampToDate(log.finish_time, 'milliseconds'),
log.action,
log.status,
log.progress,
log.fail_reason || '',
log.prompt,
])
} else {
headers.push(t('Platform'), t('Model'), t('Group'), t('Cost'), t('Quota'))
rows = (logs as TaskLog[]).map((log) => [
log.task_id,
formatTimestampToDate(log.submit_time),
formatTimestampToDate(log.finish_time),
log.action,
log.status,
log.progress || '',
log.fail_reason || '',
log.platform,
log.properties?.origin_model_name || '',
log.group,
formatLogQuota(log.quota),
log.quota,
])
}
if (config.isAdmin) {
if (config.logCategory === 'task') {
headers.push(
t('User ID'),
t('Username'),
t('Channel ID'),
t('Channel Name')
)
} else {
headers.push(t('User ID'), t('Channel'))
}
rows.forEach((row, index) => {
const log = logs[index] as MidjourneyLog | TaskLog
if (config.logCategory === 'task') {
const task = log as ExportTaskLog
row.push(
task.user_id,
task.username || t('Unknown'),
task.channel_id,
task.channel_name || t('Unknown')
)
} else {
row.push(log.user_id, log.channel_id)
}
})
}
}
// Explicit scalar cells keep untrusted text as text, never spreadsheet formulas.
const sheet = XLSX.utils.aoa_to_sheet([headers, ...rows])
sheet['!cols'] = headers.map(() => ({ wch: 22 }))
sheet['!autofilter'] = { ref: sheet['!ref'] || 'A1' }
const workbook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(workbook, sheet, 'Logs')
return workbook
}
...@@ -911,6 +911,7 @@ ...@@ -911,6 +911,7 @@
"Channel key unlocked": "Channel key unlocked", "Channel key unlocked": "Channel key unlocked",
"Channel Management": "Channel Management", "Channel Management": "Channel Management",
"Channel models": "Channel models", "Channel models": "Channel models",
"Channel Name": "Channel Name",
"Channel name is required": "Channel name is required", "Channel name is required": "Channel name is required",
"Channel test completed": "Channel test completed", "Channel test completed": "Channel test completed",
"Channel test concurrency": "Channel test concurrency", "Channel test concurrency": "Channel test concurrency",
...@@ -1725,6 +1726,7 @@ ...@@ -1725,6 +1726,7 @@
"Duplicate source model(s): {{models}}": "Duplicate source model(s): {{models}}", "Duplicate source model(s): {{models}}": "Duplicate source model(s): {{models}}",
"Duration": "Duration", "Duration": "Duration",
"Duration (hours)": "Duration (hours)", "Duration (hours)": "Duration (hours)",
"Duration (seconds)": "Duration (seconds)",
"Duration Settings": "Duration Settings", "Duration Settings": "Duration Settings",
"Duration Unit": "Duration Unit", "Duration Unit": "Duration Unit",
"Duration Value": "Duration Value", "Duration Value": "Duration Value",
...@@ -2065,6 +2067,9 @@ ...@@ -2065,6 +2067,9 @@
"Expires": "Expires", "Expires": "Expires",
"Expires at": "Expires at", "Expires at": "Expires at",
"Expires in": "Expires in", "Expires in": "Expires in",
"Export applied filters to Excel (up to {{count}} rows).": "Export applied filters to Excel (up to {{count}} rows).",
"Export Excel": "Export Excel",
"Exporting {{count}} rows": "Exporting {{count}} rows",
"Expose ratio API": "Expose ratio API", "Expose ratio API": "Expose ratio API",
"Exposes the pricing/models catalog in the top navigation.": "Exposes the pricing/models catalog in the top navigation.", "Exposes the pricing/models catalog in the top navigation.": "Exposes the pricing/models catalog in the top navigation.",
"Expression": "Expression", "Expression": "Expression",
...@@ -2149,6 +2154,7 @@ ...@@ -2149,6 +2154,7 @@
"Failed to enable channels": "Failed to enable channels", "Failed to enable channels": "Failed to enable channels",
"Failed to enable model": "Failed to enable model", "Failed to enable model": "Failed to enable model",
"Failed to enable tag channels": "Failed to enable tag channels", "Failed to enable tag channels": "Failed to enable tag channels",
"Failed to export logs": "Failed to export logs",
"Failed to fetch channel key": "Failed to fetch channel key", "Failed to fetch channel key": "Failed to fetch channel key",
"Failed to fetch checkin status": "Failed to fetch check-in status", "Failed to fetch checkin status": "Failed to fetch check-in status",
"Failed to fetch deployment details": "Failed to fetch deployment details", "Failed to fetch deployment details": "Failed to fetch deployment details",
...@@ -2989,6 +2995,7 @@ ...@@ -2989,6 +2995,7 @@
"Logo": "Logo", "Logo": "Logo",
"Logo URL": "Logo URL", "Logo URL": "Logo URL",
"Logs": "Logs", "Logs": "Logs",
"Logs changed during export. Please try again.": "Logs changed during export. Please try again.",
"Long passwords are unavailable until the password storage upgrade is complete.": "Long passwords are unavailable until the password storage upgrade is complete.", "Long passwords are unavailable until the password storage upgrade is complete.": "Long passwords are unavailable until the password storage upgrade is complete.",
"Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.", "Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.",
"Low balance": "Low balance", "Low balance": "Low balance",
...@@ -5571,6 +5578,7 @@ ...@@ -5571,6 +5578,7 @@
"Too many files. Some were not added.": "Too many files. Some were not added.", "Too many files. Some were not added.": "Too many files. Some were not added.",
"Too many incorrect codes. Start email verification again.": "Too many incorrect codes. Start email verification again.", "Too many incorrect codes. Start email verification again.": "Too many incorrect codes. Start email verification again.",
"Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.", "Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.",
"Too many logs to export. Narrow the filters to {{count}} rows or fewer.": "Too many logs to export. Narrow the filters to {{count}} rows or fewer.",
"Too many requests": "Too many requests", "Too many requests": "Too many requests",
"Tool / function declarations the model may call": "Tool / function declarations the model may call", "Tool / function declarations the model may call": "Tool / function declarations the model may call",
"Tool identifier": "Tool identifier", "Tool identifier": "Tool identifier",
......
...@@ -911,6 +911,7 @@ ...@@ -911,6 +911,7 @@
"Channel key unlocked": "Clé de canal déverrouillée", "Channel key unlocked": "Clé de canal déverrouillée",
"Channel Management": "Gestion des canaux", "Channel Management": "Gestion des canaux",
"Channel models": "Modèles de canaux", "Channel models": "Modèles de canaux",
"Channel Name": "Nom du canal",
"Channel name is required": "Le nom du canal est requis", "Channel name is required": "Le nom du canal est requis",
"Channel test completed": "Test du canal terminé", "Channel test completed": "Test du canal terminé",
"Channel test concurrency": "Parallélisme des tests de canaux", "Channel test concurrency": "Parallélisme des tests de canaux",
...@@ -1725,6 +1726,7 @@ ...@@ -1725,6 +1726,7 @@
"Duplicate source model(s): {{models}}": "Modèle(s) source en double : {{models}}", "Duplicate source model(s): {{models}}": "Modèle(s) source en double : {{models}}",
"Duration": "Durée", "Duration": "Durée",
"Duration (hours)": "Durée (heures)", "Duration (hours)": "Durée (heures)",
"Duration (seconds)": "Durée (secondes)",
"Duration Settings": "Paramètres de durée", "Duration Settings": "Paramètres de durée",
"Duration Unit": "Unité de durée", "Duration Unit": "Unité de durée",
"Duration Value": "Valeur de durée", "Duration Value": "Valeur de durée",
...@@ -2065,6 +2067,9 @@ ...@@ -2065,6 +2067,9 @@
"Expires": "Expire", "Expires": "Expire",
"Expires at": "Expire le", "Expires at": "Expire le",
"Expires in": "Expire dans", "Expires in": "Expire dans",
"Export applied filters to Excel (up to {{count}} rows).": "Exporter les résultats filtrés vers Excel ({{count}} lignes maximum).",
"Export Excel": "Exporter Excel",
"Exporting {{count}} rows": "Export de {{count}} lignes",
"Expose ratio API": "Exposer l'API de ratio", "Expose ratio API": "Exposer l'API de ratio",
"Exposes the pricing/models catalog in the top navigation.": "Expose le catalogue des prix/modèles dans la navigation supérieure.", "Exposes the pricing/models catalog in the top navigation.": "Expose le catalogue des prix/modèles dans la navigation supérieure.",
"Expression": "Expression", "Expression": "Expression",
...@@ -2149,6 +2154,7 @@ ...@@ -2149,6 +2154,7 @@
"Failed to enable channels": "Échec de l'activation des canaux", "Failed to enable channels": "Échec de l'activation des canaux",
"Failed to enable model": "Échec de l'activation du modèle", "Failed to enable model": "Échec de l'activation du modèle",
"Failed to enable tag channels": "Échec de l'activation des canaux de tags", "Failed to enable tag channels": "Échec de l'activation des canaux de tags",
"Failed to export logs": "Échec de l’export des journaux",
"Failed to fetch channel key": "Échec de la récupération de la clé du canal", "Failed to fetch channel key": "Échec de la récupération de la clé du canal",
"Failed to fetch checkin status": "Échec de la récupération du statut d'enregistrement", "Failed to fetch checkin status": "Échec de la récupération du statut d'enregistrement",
"Failed to fetch deployment details": "Impossible de récupérer les détails du déploiement", "Failed to fetch deployment details": "Impossible de récupérer les détails du déploiement",
...@@ -2989,6 +2995,7 @@ ...@@ -2989,6 +2995,7 @@
"Logo": "Logo", "Logo": "Logo",
"Logo URL": "URL du logo", "Logo URL": "URL du logo",
"Logs": "Journaux", "Logs": "Journaux",
"Logs changed during export. Please try again.": "Les journaux ont changé pendant l’export. Réessayez.",
"Long passwords are unavailable until the password storage upgrade is complete.": "Les mots de passe longs seront disponibles après la mise à niveau du stockage des mots de passe.", "Long passwords are unavailable until the password storage upgrade is complete.": "Les mots de passe longs seront disponibles après la mise à niveau du stockage des mots de passe.",
"Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "Cherchez une règle de taux spécial correspondant à ce groupe d’utilisateurs et ce groupe de facturation. Si elle existe, utilisez son taux ; sinon le taux de base du groupe de facturation.", "Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "Cherchez une règle de taux spécial correspondant à ce groupe d’utilisateurs et ce groupe de facturation. Si elle existe, utilisez son taux ; sinon le taux de base du groupe de facturation.",
"Low balance": "Solde faible", "Low balance": "Solde faible",
...@@ -5571,6 +5578,7 @@ ...@@ -5571,6 +5578,7 @@
"Too many files. Some were not added.": "Trop de fichiers. Certains n'ont pas été ajoutés.", "Too many files. Some were not added.": "Trop de fichiers. Certains n'ont pas été ajoutés.",
"Too many incorrect codes. Start email verification again.": "Trop de codes incorrects. Recommencez la vérification de l’adresse e-mail.", "Too many incorrect codes. Start email verification again.": "Trop de codes incorrects. Recommencez la vérification de l’adresse e-mail.",
"Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "Trop de sessions de connexion ont été créées récemment. Attendez la fin de la fenêtre glissante, puis réessayez.", "Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "Trop de sessions de connexion ont été créées récemment. Attendez la fin de la fenêtre glissante, puis réessayez.",
"Too many logs to export. Narrow the filters to {{count}} rows or fewer.": "Trop de journaux. Affinez les filtres pour obtenir au plus {{count}} lignes.",
"Too many requests": "Trop de requêtes", "Too many requests": "Trop de requêtes",
"Tool / function declarations the model may call": "Déclarations d'outils / fonctions que le modèle peut appeler", "Tool / function declarations the model may call": "Déclarations d'outils / fonctions que le modèle peut appeler",
"Tool identifier": "Identifiant d’outil", "Tool identifier": "Identifiant d’outil",
......
...@@ -911,6 +911,7 @@ ...@@ -911,6 +911,7 @@
"Channel key unlocked": "チャネルキーが解除されました", "Channel key unlocked": "チャネルキーが解除されました",
"Channel Management": "チャネル管理", "Channel Management": "チャネル管理",
"Channel models": "チャネルモデル", "Channel models": "チャネルモデル",
"Channel Name": "チャネル名",
"Channel name is required": "チャネル名が必要です", "Channel name is required": "チャネル名が必要です",
"Channel test completed": "チャネルテストが完了しました", "Channel test completed": "チャネルテストが完了しました",
"Channel test concurrency": "チャンネルテストの同時実行数", "Channel test concurrency": "チャンネルテストの同時実行数",
...@@ -1725,6 +1726,7 @@ ...@@ -1725,6 +1726,7 @@
"Duplicate source model(s): {{models}}": "重複したソースモデル: {{models}}", "Duplicate source model(s): {{models}}": "重複したソースモデル: {{models}}",
"Duration": "所要時間", "Duration": "所要時間",
"Duration (hours)": "期間(時間)", "Duration (hours)": "期間(時間)",
"Duration (seconds)": "所要時間(秒)",
"Duration Settings": "有効期間設定", "Duration Settings": "有効期間設定",
"Duration Unit": "期間単位", "Duration Unit": "期間単位",
"Duration Value": "期間値", "Duration Value": "期間値",
...@@ -2065,6 +2067,9 @@ ...@@ -2065,6 +2067,9 @@
"Expires": "有効期限", "Expires": "有効期限",
"Expires at": "有効期限", "Expires at": "有効期限",
"Expires in": "期限まで", "Expires in": "期限まで",
"Export applied filters to Excel (up to {{count}} rows).": "適用済みフィルターの結果を Excel にエクスポート(最大 {{count}} 件)。",
"Export Excel": "Excel にエクスポート",
"Exporting {{count}} rows": "{{count}} 件をエクスポート中",
"Expose ratio API": "倍率APIを公開", "Expose ratio API": "倍率APIを公開",
"Exposes the pricing/models catalog in the top navigation.": "価格/モデルカタログをトップナビゲーションに表示します。", "Exposes the pricing/models catalog in the top navigation.": "価格/モデルカタログをトップナビゲーションに表示します。",
"Expression": "式", "Expression": "式",
...@@ -2149,6 +2154,7 @@ ...@@ -2149,6 +2154,7 @@
"Failed to enable channels": "チャネルの有効化に失敗しました", "Failed to enable channels": "チャネルの有効化に失敗しました",
"Failed to enable model": "モデルの有効化に失敗しました", "Failed to enable model": "モデルの有効化に失敗しました",
"Failed to enable tag channels": "タグチャネルの有効化に失敗しました", "Failed to enable tag channels": "タグチャネルの有効化に失敗しました",
"Failed to export logs": "ログのエクスポートに失敗しました",
"Failed to fetch channel key": "チャネルキーの取得に失敗しました", "Failed to fetch channel key": "チャネルキーの取得に失敗しました",
"Failed to fetch checkin status": "チェックインステータスの取得に失敗しました", "Failed to fetch checkin status": "チェックインステータスの取得に失敗しました",
"Failed to fetch deployment details": "デプロイメント詳細の取得に失敗しました", "Failed to fetch deployment details": "デプロイメント詳細の取得に失敗しました",
...@@ -2989,6 +2995,7 @@ ...@@ -2989,6 +2995,7 @@
"Logo": "ロゴ", "Logo": "ロゴ",
"Logo URL": "ロゴURL", "Logo URL": "ロゴURL",
"Logs": "ログ", "Logs": "ログ",
"Logs changed during export. Please try again.": "エクスポート中にログが変更されました。再試行してください。",
"Long passwords are unavailable until the password storage upgrade is complete.": "パスワード保存方式の更新が完了するまで、長いパスワードは使用できません。", "Long passwords are unavailable until the password storage upgrade is complete.": "パスワード保存方式の更新が完了するまで、長いパスワードは使用できません。",
"Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "このユーザーグループと課金グループに一致する特別倍率ルールを探します。あればその倍率を、なければ料金表の課金グループの基本倍率を使います。", "Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "このユーザーグループと課金グループに一致する特別倍率ルールを探します。あればその倍率を、なければ料金表の課金グループの基本倍率を使います。",
"Low balance": "残高不足", "Low balance": "残高不足",
...@@ -5571,6 +5578,7 @@ ...@@ -5571,6 +5578,7 @@
"Too many files. Some were not added.": "ファイルが多すぎます。一部は追加されませんでした。", "Too many files. Some were not added.": "ファイルが多すぎます。一部は追加されませんでした。",
"Too many incorrect codes. Start email verification again.": "コードの入力ミスが多すぎます。メール認証をやり直してください。", "Too many incorrect codes. Start email verification again.": "コードの入力ミスが多すぎます。メール認証をやり直してください。",
"Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "最近作成されたログインセッションが多すぎます。ローリングウィンドウが経過してから、もう一度お試しください。", "Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "最近作成されたログインセッションが多すぎます。ローリングウィンドウが経過してから、もう一度お試しください。",
"Too many logs to export. Narrow the filters to {{count}} rows or fewer.": "ログが多すぎます。フィルターで {{count}} 件以下に絞ってください。",
"Too many requests": "リクエストが多すぎます", "Too many requests": "リクエストが多すぎます",
"Tool / function declarations the model may call": "モデルが呼び出せるツール / 関数の宣言", "Tool / function declarations the model may call": "モデルが呼び出せるツール / 関数の宣言",
"Tool identifier": "ツールID", "Tool identifier": "ツールID",
......
...@@ -911,6 +911,7 @@ ...@@ -911,6 +911,7 @@
"Channel key unlocked": "Ключ канала разблокирован", "Channel key unlocked": "Ключ канала разблокирован",
"Channel Management": "Управление каналами", "Channel Management": "Управление каналами",
"Channel models": "Модели каналов", "Channel models": "Модели каналов",
"Channel Name": "Название канала",
"Channel name is required": "Имя канала обязательно", "Channel name is required": "Имя канала обязательно",
"Channel test completed": "Тест канала завершён", "Channel test completed": "Тест канала завершён",
"Channel test concurrency": "Параллельность проверки каналов", "Channel test concurrency": "Параллельность проверки каналов",
...@@ -1725,6 +1726,7 @@ ...@@ -1725,6 +1726,7 @@
"Duplicate source model(s): {{models}}": "Повторяющиеся исходные модели: {{models}}", "Duplicate source model(s): {{models}}": "Повторяющиеся исходные модели: {{models}}",
"Duration": "Длительность", "Duration": "Длительность",
"Duration (hours)": "Длительность (часы)", "Duration (hours)": "Длительность (часы)",
"Duration (seconds)": "Длительность (секунды)",
"Duration Settings": "Настройки срока действия", "Duration Settings": "Настройки срока действия",
"Duration Unit": "Единица срока", "Duration Unit": "Единица срока",
"Duration Value": "Значение срока", "Duration Value": "Значение срока",
...@@ -2065,6 +2067,9 @@ ...@@ -2065,6 +2067,9 @@
"Expires": "Истекает", "Expires": "Истекает",
"Expires at": "Истекает", "Expires at": "Истекает",
"Expires in": "До истечения", "Expires in": "До истечения",
"Export applied filters to Excel (up to {{count}} rows).": "Экспорт результатов фильтрации в Excel (до {{count}} строк).",
"Export Excel": "Экспорт в Excel",
"Exporting {{count}} rows": "Экспорт {{count}} строк",
"Expose ratio API": "Интерфейс экспонирования коэффициента", "Expose ratio API": "Интерфейс экспонирования коэффициента",
"Exposes the pricing/models catalog in the top navigation.": "Отображает каталог цен/моделей в верхней навигации.", "Exposes the pricing/models catalog in the top navigation.": "Отображает каталог цен/моделей в верхней навигации.",
"Expression": "Выражение", "Expression": "Выражение",
...@@ -2149,6 +2154,7 @@ ...@@ -2149,6 +2154,7 @@
"Failed to enable channels": "Не удалось включить каналы", "Failed to enable channels": "Не удалось включить каналы",
"Failed to enable model": "Не удалось включить модель", "Failed to enable model": "Не удалось включить модель",
"Failed to enable tag channels": "Не удалось включить каналы тегов", "Failed to enable tag channels": "Не удалось включить каналы тегов",
"Failed to export logs": "Не удалось экспортировать журналы",
"Failed to fetch channel key": "Не удалось получить ключ канала", "Failed to fetch channel key": "Не удалось получить ключ канала",
"Failed to fetch checkin status": "Не удалось получить статус регистрации", "Failed to fetch checkin status": "Не удалось получить статус регистрации",
"Failed to fetch deployment details": "Не удалось получить сведения о развертывании", "Failed to fetch deployment details": "Не удалось получить сведения о развертывании",
...@@ -2989,6 +2995,7 @@ ...@@ -2989,6 +2995,7 @@
"Logo": "Логотип", "Logo": "Логотип",
"Logo URL": "URL логотипа", "Logo URL": "URL логотипа",
"Logs": "Журналы", "Logs": "Журналы",
"Logs changed during export. Please try again.": "Журналы изменились во время экспорта. Повторите попытку.",
"Long passwords are unavailable until the password storage upgrade is complete.": "Длинные пароли будут доступны после обновления системы хранения паролей.", "Long passwords are unavailable until the password storage upgrade is complete.": "Длинные пароли будут доступны после обновления системы хранения паролей.",
"Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "Найдите правило особого коэффициента для этой группы пользователя и тарифной группы. Если оно есть — используется его коэффициент, иначе базовый коэффициент тарифной группы.", "Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "Найдите правило особого коэффициента для этой группы пользователя и тарифной группы. Если оно есть — используется его коэффициент, иначе базовый коэффициент тарифной группы.",
"Low balance": "Низкий баланс", "Low balance": "Низкий баланс",
...@@ -5571,6 +5578,7 @@ ...@@ -5571,6 +5578,7 @@
"Too many files. Some were not added.": "Слишком много файлов. Некоторые не были добавлены.", "Too many files. Some were not added.": "Слишком много файлов. Некоторые не были добавлены.",
"Too many incorrect codes. Start email verification again.": "Слишком много неверных кодов. Начните подтверждение почты заново.", "Too many incorrect codes. Start email verification again.": "Слишком много неверных кодов. Начните подтверждение почты заново.",
"Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "За последнее время создано слишком много сеансов входа. Дождитесь окончания скользящего временного окна и повторите попытку.", "Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "За последнее время создано слишком много сеансов входа. Дождитесь окончания скользящего временного окна и повторите попытку.",
"Too many logs to export. Narrow the filters to {{count}} rows or fewer.": "Слишком много записей. Уточните фильтры до {{count}} строк или менее.",
"Too many requests": "Слишком много запросов", "Too many requests": "Слишком много запросов",
"Tool / function declarations the model may call": "Объявления инструментов и функций, которые модель может вызывать", "Tool / function declarations the model may call": "Объявления инструментов и функций, которые модель может вызывать",
"Tool identifier": "Идентификатор инструмента", "Tool identifier": "Идентификатор инструмента",
......
...@@ -911,6 +911,7 @@ ...@@ -911,6 +911,7 @@
"Channel key unlocked": "Khóa kênh đã được mở khóa", "Channel key unlocked": "Khóa kênh đã được mở khóa",
"Channel Management": "Quản lý kênh", "Channel Management": "Quản lý kênh",
"Channel models": "Mô hình kênh", "Channel models": "Mô hình kênh",
"Channel Name": "Tên kênh",
"Channel name is required": "Tên kênh là bắt buộc", "Channel name is required": "Tên kênh là bắt buộc",
"Channel test completed": "Kiểm tra kênh hoàn tất", "Channel test completed": "Kiểm tra kênh hoàn tất",
"Channel test concurrency": "Mức đồng thời khi kiểm tra kênh", "Channel test concurrency": "Mức đồng thời khi kiểm tra kênh",
...@@ -1725,6 +1726,7 @@ ...@@ -1725,6 +1726,7 @@
"Duplicate source model(s): {{models}}": "Mô hình nguồn trùng lặp: {{models}}", "Duplicate source model(s): {{models}}": "Mô hình nguồn trùng lặp: {{models}}",
"Duration": "Thời lượng", "Duration": "Thời lượng",
"Duration (hours)": "Thời lượng (giờ)", "Duration (hours)": "Thời lượng (giờ)",
"Duration (seconds)": "Thời lượng (giây)",
"Duration Settings": "Cài đặt thời lượng", "Duration Settings": "Cài đặt thời lượng",
"Duration Unit": "Đơn vị thời lượng", "Duration Unit": "Đơn vị thời lượng",
"Duration Value": "Giá trị thời lượng", "Duration Value": "Giá trị thời lượng",
...@@ -2065,6 +2067,9 @@ ...@@ -2065,6 +2067,9 @@
"Expires": "Hết hạn", "Expires": "Hết hạn",
"Expires at": "Hết hạn lúc", "Expires at": "Hết hạn lúc",
"Expires in": "Còn lại", "Expires in": "Còn lại",
"Export applied filters to Excel (up to {{count}} rows).": "Xuất kết quả đã lọc sang Excel (tối đa {{count}} dòng).",
"Export Excel": "Xuất Excel",
"Exporting {{count}} rows": "Đang xuất {{count}} dòng",
"Expose ratio API": "Cung cấp API tỷ lệ", "Expose ratio API": "Cung cấp API tỷ lệ",
"Exposes the pricing/models catalog in the top navigation.": "Hiển thị danh mục giá/mô hình trên thanh điều hướng đầu trang.", "Exposes the pricing/models catalog in the top navigation.": "Hiển thị danh mục giá/mô hình trên thanh điều hướng đầu trang.",
"Expression": "Biểu thức", "Expression": "Biểu thức",
...@@ -2149,6 +2154,7 @@ ...@@ -2149,6 +2154,7 @@
"Failed to enable channels": "Không thể kích hoạt các kênh", "Failed to enable channels": "Không thể kích hoạt các kênh",
"Failed to enable model": "Không thể kích hoạt mô hình", "Failed to enable model": "Không thể kích hoạt mô hình",
"Failed to enable tag channels": "Không thể kích hoạt kênh thẻ", "Failed to enable tag channels": "Không thể kích hoạt kênh thẻ",
"Failed to export logs": "Không thể xuất nhật ký",
"Failed to fetch channel key": "Không thể lấy khóa kênh", "Failed to fetch channel key": "Không thể lấy khóa kênh",
"Failed to fetch checkin status": "Không thể tải trạng thái điểm danh", "Failed to fetch checkin status": "Không thể tải trạng thái điểm danh",
"Failed to fetch deployment details": "Không thể lấy chi tiết triển khai", "Failed to fetch deployment details": "Không thể lấy chi tiết triển khai",
...@@ -2989,6 +2995,7 @@ ...@@ -2989,6 +2995,7 @@
"Logo": "Logo", "Logo": "Logo",
"Logo URL": "URL Logo", "Logo URL": "URL Logo",
"Logs": "Nhật ký", "Logs": "Nhật ký",
"Logs changed during export. Please try again.": "Nhật ký đã thay đổi khi xuất. Vui lòng thử lại.",
"Long passwords are unavailable until the password storage upgrade is complete.": "Mật khẩu dài chỉ khả dụng sau khi hoàn tất nâng cấp hệ thống lưu trữ mật khẩu.", "Long passwords are unavailable until the password storage upgrade is complete.": "Mật khẩu dài chỉ khả dụng sau khi hoàn tất nâng cấp hệ thống lưu trữ mật khẩu.",
"Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "Tìm quy tắc hệ số đặc biệt khớp với nhóm người dùng và nhóm tính phí này. Nếu có thì dùng hệ số của quy tắc, nếu không thì dùng hệ số cơ bản của nhóm tính phí trong bảng định giá.", "Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "Tìm quy tắc hệ số đặc biệt khớp với nhóm người dùng và nhóm tính phí này. Nếu có thì dùng hệ số của quy tắc, nếu không thì dùng hệ số cơ bản của nhóm tính phí trong bảng định giá.",
"Low balance": "Số dư thấp", "Low balance": "Số dư thấp",
...@@ -5571,6 +5578,7 @@ ...@@ -5571,6 +5578,7 @@
"Too many files. Some were not added.": "Quá nhiều tệp. Một số không được thêm.", "Too many files. Some were not added.": "Quá nhiều tệp. Một số không được thêm.",
"Too many incorrect codes. Start email verification again.": "Nhập sai mã quá nhiều lần. Vui lòng bắt đầu xác minh email lại.", "Too many incorrect codes. Start email verification again.": "Nhập sai mã quá nhiều lần. Vui lòng bắt đầu xác minh email lại.",
"Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "Gần đây đã tạo quá nhiều phiên đăng nhập. Vui lòng chờ cửa sổ thời gian trượt kết thúc rồi thử lại.", "Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "Gần đây đã tạo quá nhiều phiên đăng nhập. Vui lòng chờ cửa sổ thời gian trượt kết thúc rồi thử lại.",
"Too many logs to export. Narrow the filters to {{count}} rows or fewer.": "Quá nhiều nhật ký. Hãy lọc còn tối đa {{count}} dòng.",
"Too many requests": "Quá nhiều yêu cầu", "Too many requests": "Quá nhiều yêu cầu",
"Tool / function declarations the model may call": "Khai báo công cụ / hàm mà model có thể gọi", "Tool / function declarations the model may call": "Khai báo công cụ / hàm mà model có thể gọi",
"Tool identifier": "Định danh công cụ", "Tool identifier": "Định danh công cụ",
......
...@@ -911,6 +911,7 @@ ...@@ -911,6 +911,7 @@
"Channel key unlocked": "渠道金鑰已解鎖", "Channel key unlocked": "渠道金鑰已解鎖",
"Channel Management": "渠道管理", "Channel Management": "渠道管理",
"Channel models": "渠道模型", "Channel models": "渠道模型",
"Channel Name": "渠道名稱",
"Channel name is required": "渠道名稱是必填的", "Channel name is required": "渠道名稱是必填的",
"Channel test completed": "渠道測試完成", "Channel test completed": "渠道測試完成",
"Channel test concurrency": "渠道測試並行數", "Channel test concurrency": "渠道測試並行數",
...@@ -1725,6 +1726,7 @@ ...@@ -1725,6 +1726,7 @@
"Duplicate source model(s): {{models}}": "重複的源模型:{{models}}", "Duplicate source model(s): {{models}}": "重複的源模型:{{models}}",
"Duration": "耗時", "Duration": "耗時",
"Duration (hours)": "時長 (小時)", "Duration (hours)": "時長 (小時)",
"Duration (seconds)": "耗時(秒)",
"Duration Settings": "有效期設定", "Duration Settings": "有效期設定",
"Duration Unit": "有效期單位", "Duration Unit": "有效期單位",
"Duration Value": "有效期數值", "Duration Value": "有效期數值",
...@@ -2065,6 +2067,9 @@ ...@@ -2065,6 +2067,9 @@
"Expires": "過期", "Expires": "過期",
"Expires at": "到期時間", "Expires at": "到期時間",
"Expires in": "剩餘到期時間", "Expires in": "剩餘到期時間",
"Export applied filters to Excel (up to {{count}} rows).": "將目前已套用篩選條件的結果匯出為 Excel(最多 {{count}} 筆)。",
"Export Excel": "匯出 Excel",
"Exporting {{count}} rows": "正在匯出 {{count}} 筆",
"Expose ratio API": "暴露倍率接口", "Expose ratio API": "暴露倍率接口",
"Exposes the pricing/models catalog in the top navigation.": "在頂部導航中顯示定價/模型目錄。", "Exposes the pricing/models catalog in the top navigation.": "在頂部導航中顯示定價/模型目錄。",
"Expression": "表達式", "Expression": "表達式",
...@@ -2149,6 +2154,7 @@ ...@@ -2149,6 +2154,7 @@
"Failed to enable channels": "啟用渠道失敗", "Failed to enable channels": "啟用渠道失敗",
"Failed to enable model": "啟用模型失敗", "Failed to enable model": "啟用模型失敗",
"Failed to enable tag channels": "啟用標籤渠道失敗", "Failed to enable tag channels": "啟用標籤渠道失敗",
"Failed to export logs": "匯出日誌失敗",
"Failed to fetch channel key": "獲取渠道金鑰失敗", "Failed to fetch channel key": "獲取渠道金鑰失敗",
"Failed to fetch checkin status": "獲取簽到狀態失敗", "Failed to fetch checkin status": "獲取簽到狀態失敗",
"Failed to fetch deployment details": "獲取部署詳情失敗", "Failed to fetch deployment details": "獲取部署詳情失敗",
...@@ -2989,6 +2995,7 @@ ...@@ -2989,6 +2995,7 @@
"Logo": "徽標", "Logo": "徽標",
"Logo URL": "徽標 URL", "Logo URL": "徽標 URL",
"Logs": "日誌", "Logs": "日誌",
"Logs changed during export. Please try again.": "匯出期間日誌發生變更,請重試。",
"Long passwords are unavailable until the password storage upgrade is complete.": "密碼儲存升級完成前暫不支援長密碼。", "Long passwords are unavailable until the password storage upgrade is complete.": "密碼儲存升級完成前暫不支援長密碼。",
"Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "查找匹配「該用戶分組 + 該收費分組」的特殊倍率規則。有就用規則裡的倍率,沒有就用定價分組表中收費分組的基礎倍率。", "Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "查找匹配「該用戶分組 + 該收費分組」的特殊倍率規則。有就用規則裡的倍率,沒有就用定價分組表中收費分組的基礎倍率。",
"Low balance": "餘額偏低", "Low balance": "餘額偏低",
...@@ -5571,6 +5578,7 @@ ...@@ -5571,6 +5578,7 @@
"Too many files. Some were not added.": "檔案過多。部分未添加。", "Too many files. Some were not added.": "檔案過多。部分未添加。",
"Too many incorrect codes. Start email verification again.": "驗證碼錯誤次數過多,請重新開始信箱驗證。", "Too many incorrect codes. Start email verification again.": "驗證碼錯誤次數過多,請重新開始信箱驗證。",
"Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "近期建立的登入工作階段過多。請等待滾動時間窗口結束後再試。", "Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "近期建立的登入工作階段過多。請等待滾動時間窗口結束後再試。",
"Too many logs to export. Narrow the filters to {{count}} rows or fewer.": "日誌過多,請縮小篩選範圍至 {{count}} 筆以內再匯出。",
"Too many requests": "請求過於頻繁", "Too many requests": "請求過於頻繁",
"Tool / function declarations the model may call": "模型可呼叫的工具 / 函數聲明", "Tool / function declarations the model may call": "模型可呼叫的工具 / 函數聲明",
"Tool identifier": "工具標識", "Tool identifier": "工具標識",
......
...@@ -911,6 +911,7 @@ ...@@ -911,6 +911,7 @@
"Channel key unlocked": "渠道密钥已解锁", "Channel key unlocked": "渠道密钥已解锁",
"Channel Management": "渠道管理", "Channel Management": "渠道管理",
"Channel models": "渠道模型", "Channel models": "渠道模型",
"Channel Name": "渠道名称",
"Channel name is required": "渠道名称是必填的", "Channel name is required": "渠道名称是必填的",
"Channel test completed": "渠道测试完成", "Channel test completed": "渠道测试完成",
"Channel test concurrency": "渠道测试并发数", "Channel test concurrency": "渠道测试并发数",
...@@ -1725,6 +1726,7 @@ ...@@ -1725,6 +1726,7 @@
"Duplicate source model(s): {{models}}": "重复的源模型:{{models}}", "Duplicate source model(s): {{models}}": "重复的源模型:{{models}}",
"Duration": "耗时", "Duration": "耗时",
"Duration (hours)": "时长 (小时)", "Duration (hours)": "时长 (小时)",
"Duration (seconds)": "耗时(秒)",
"Duration Settings": "有效期设置", "Duration Settings": "有效期设置",
"Duration Unit": "有效期单位", "Duration Unit": "有效期单位",
"Duration Value": "有效期数值", "Duration Value": "有效期数值",
...@@ -2065,6 +2067,9 @@ ...@@ -2065,6 +2067,9 @@
"Expires": "过期", "Expires": "过期",
"Expires at": "到期时间", "Expires at": "到期时间",
"Expires in": "剩余到期时间", "Expires in": "剩余到期时间",
"Export applied filters to Excel (up to {{count}} rows).": "将当前已应用筛选条件的结果导出为 Excel(最多 {{count}} 条)。",
"Export Excel": "导出 Excel",
"Exporting {{count}} rows": "正在导出 {{count}} 条",
"Expose ratio API": "暴露倍率接口", "Expose ratio API": "暴露倍率接口",
"Exposes the pricing/models catalog in the top navigation.": "在顶部导航中显示定价/模型目录。", "Exposes the pricing/models catalog in the top navigation.": "在顶部导航中显示定价/模型目录。",
"Expression": "表达式", "Expression": "表达式",
...@@ -2149,6 +2154,7 @@ ...@@ -2149,6 +2154,7 @@
"Failed to enable channels": "启用渠道失败", "Failed to enable channels": "启用渠道失败",
"Failed to enable model": "启用模型失败", "Failed to enable model": "启用模型失败",
"Failed to enable tag channels": "启用标签渠道失败", "Failed to enable tag channels": "启用标签渠道失败",
"Failed to export logs": "导出日志失败",
"Failed to fetch channel key": "获取渠道密钥失败", "Failed to fetch channel key": "获取渠道密钥失败",
"Failed to fetch checkin status": "获取签到状态失败", "Failed to fetch checkin status": "获取签到状态失败",
"Failed to fetch deployment details": "获取部署详情失败", "Failed to fetch deployment details": "获取部署详情失败",
...@@ -2989,6 +2995,7 @@ ...@@ -2989,6 +2995,7 @@
"Logo": "徽标", "Logo": "徽标",
"Logo URL": "徽标 URL", "Logo URL": "徽标 URL",
"Logs": "日志", "Logs": "日志",
"Logs changed during export. Please try again.": "导出期间日志发生变化,请重试。",
"Long passwords are unavailable until the password storage upgrade is complete.": "密码存储升级完成前暂不支持长密码。", "Long passwords are unavailable until the password storage upgrade is complete.": "密码存储升级完成前暂不支持长密码。",
"Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "查找匹配「该用户分组 + 该计费分组」的特殊倍率规则。有就用规则里的倍率,没有就用定价分组表中计费分组的基础倍率。", "Look for a special ratio rule matching this user group and this billing group. If one exists, use its ratio. Otherwise use the billing group base ratio from the pricing table.": "查找匹配「该用户分组 + 该计费分组」的特殊倍率规则。有就用规则里的倍率,没有就用定价分组表中计费分组的基础倍率。",
"Low balance": "余额偏低", "Low balance": "余额偏低",
...@@ -5571,6 +5578,7 @@ ...@@ -5571,6 +5578,7 @@
"Too many files. Some were not added.": "文件过多。部分未添加。", "Too many files. Some were not added.": "文件过多。部分未添加。",
"Too many incorrect codes. Start email verification again.": "验证码错误次数过多,请重新开始邮箱验证。", "Too many incorrect codes. Start email verification again.": "验证码错误次数过多,请重新开始邮箱验证。",
"Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "近期创建的登录会话过多。请等待滚动时间窗口结束后再试。", "Too many login sessions were created recently. Please wait for the rolling window to pass, then try again.": "近期创建的登录会话过多。请等待滚动时间窗口结束后再试。",
"Too many logs to export. Narrow the filters to {{count}} rows or fewer.": "日志过多,请缩小筛选范围至 {{count}} 条以内再导出。",
"Too many requests": "请求过于频繁", "Too many requests": "请求过于频繁",
"Tool / function declarations the model may call": "模型可调用的工具 / 函数声明", "Tool / function declarations the model may call": "模型可调用的工具 / 函数声明",
"Tool identifier": "工具标识", "Tool identifier": "工具标识",
......
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