From 040e8c1da82af1b7cce819c9fe720877eca65556 Mon Sep 17 00:00:00 2001 From: CaIon Date: Thu, 9 Apr 2026 22:44:53 +0800 Subject: [PATCH 01/46] feat: replace quota input with amount-first UI and atomic quota adjustment - Refactor token, redemption, and user quota inputs to prioritize monetary amount entry, with raw quota input collapsed by default - Add atomic quota adjustment modal for users with add/subtract/override modes, bypassing batch update queue for immediate DB consistency - Make user quota fields readonly in edit form; all modifications go through the dedicated adjust-quota modal via POST /api/user/manage - Add DecreaseUserQuota `db` parameter for direct DB writes, matching IncreaseUserQuota behavior - Support negative quota display in amount conversion helpers - Add i18n keys for all new UI strings across all locales --- .gitignore | 2 + controller/user.go | 46 ++- i18n/keys.go | 1 + i18n/locales/en.yaml | 1 + i18n/locales/zh-CN.yaml | 1 + i18n/locales/zh-TW.yaml | 1 + model/user.go | 7 +- service/funding_source.go | 4 +- service/quota.go | 2 +- service/task_billing.go | 2 +- .../modals/EditRedemptionModal.jsx | 99 +++++-- .../table/tokens/modals/EditTokenModal.jsx | 103 +++++-- .../table/users/modals/EditUserModal.jsx | 267 ++++++++++++------ web/src/helpers/quota.js | 36 ++- web/src/i18n/locales/en.json | 10 + web/src/i18n/locales/fr.json | 10 + web/src/i18n/locales/ja.json | 10 + web/src/i18n/locales/ru.json | 10 + web/src/i18n/locales/vi.json | 10 + web/src/i18n/locales/zh-CN.json | 10 + web/src/i18n/locales/zh-TW.json | 10 + 21 files changed, 493 insertions(+), 149 deletions(-) diff --git a/.gitignore b/.gitignore index 54fa8311..c17652a2 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ data/ .gomodcache/ .gocache-temp .gopath + +token_estimator_test.go \ No newline at end of file diff --git a/controller/user.go b/controller/user.go index 8229d0d2..8e26320f 100644 --- a/controller/user.go +++ b/controller/user.go @@ -572,9 +572,6 @@ func UpdateUser(c *gin.Context) { common.ApiError(c, err) return } - if originUser.Quota != updatedUser.Quota { - model.RecordLog(originUser.Id, model.LogTypeManage, fmt.Sprintf("管理员将用户额度从 %s修改为 %s", logger.LogQuota(originUser.Quota), logger.LogQuota(updatedUser.Quota))) - } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", @@ -841,6 +838,8 @@ func CreateUser(c *gin.Context) { type ManageRequest struct { Id int `json:"id"` Action string `json:"action"` + Value int `json:"value"` + Mode string `json:"mode"` } // ManageUser Only admin user can do this @@ -907,6 +906,47 @@ func ManageUser(c *gin.Context) { return } user.Role = common.RoleCommonUser + case "add_quota": + switch req.Mode { + case "add": + if req.Value <= 0 { + common.ApiErrorI18n(c, i18n.MsgUserQuotaChangeZero) + return + } + if err := model.IncreaseUserQuota(user.Id, req.Value, true); err != nil { + common.ApiError(c, err) + return + } + model.RecordLog(user.Id, model.LogTypeManage, + fmt.Sprintf("管理员增加用户额度 %s", logger.LogQuota(req.Value))) + case "subtract": + if req.Value <= 0 { + common.ApiErrorI18n(c, i18n.MsgUserQuotaChangeZero) + return + } + if err := model.DecreaseUserQuota(user.Id, req.Value, true); err != nil { + common.ApiError(c, err) + return + } + model.RecordLog(user.Id, model.LogTypeManage, + fmt.Sprintf("管理员减少用户额度 %s", logger.LogQuota(req.Value))) + case "override": + oldQuota := user.Quota + if err := model.DB.Model(&model.User{}).Where("id = ?", user.Id).Update("quota", req.Value).Error; err != nil { + common.ApiError(c, err) + return + } + model.RecordLog(user.Id, model.LogTypeManage, + fmt.Sprintf("管理员覆盖用户额度从 %s 为 %s", logger.LogQuota(oldQuota), logger.LogQuota(req.Value))) + default: + common.ApiErrorI18n(c, i18n.MsgInvalidParams) + return + } + c.JSON(http.StatusOK, gin.H{ + "success": true, + "message": "", + }) + return } if err := user.Update(false); err != nil { diff --git a/i18n/keys.go b/i18n/keys.go index 8118dff9..67ddbb9a 100644 --- a/i18n/keys.go +++ b/i18n/keys.go @@ -101,6 +101,7 @@ const ( MsgUserTelegramIdEmpty = "user.telegram_id_empty" MsgUserTelegramNotBound = "user.telegram_not_bound" MsgUserLinuxDOIdEmpty = "user.linux_do_id_empty" + MsgUserQuotaChangeZero = "user.quota_change_zero" ) // Quota related messages diff --git a/i18n/locales/en.yaml b/i18n/locales/en.yaml index 75a8bc6e..3cb9ac2c 100644 --- a/i18n/locales/en.yaml +++ b/i18n/locales/en.yaml @@ -91,6 +91,7 @@ user.wechat_id_empty: "WeChat ID is empty!" user.telegram_id_empty: "Telegram ID is empty!" user.telegram_not_bound: "This Telegram account is not bound" user.linux_do_id_empty: "Linux DO ID is empty!" +user.quota_change_zero: "Quota change amount cannot be zero" # Quota messages quota.negative: "Quota cannot be negative!" diff --git a/i18n/locales/zh-CN.yaml b/i18n/locales/zh-CN.yaml index 1f3b5a7b..20f8a77b 100644 --- a/i18n/locales/zh-CN.yaml +++ b/i18n/locales/zh-CN.yaml @@ -92,6 +92,7 @@ user.wechat_id_empty: "WeChat id 为空!" user.telegram_id_empty: "Telegram id 为空!" user.telegram_not_bound: "该 Telegram 账户未绑定" user.linux_do_id_empty: "Linux DO id 为空!" +user.quota_change_zero: "额度变更量不能为0" # Quota messages quota.negative: "额度不能为负数!" diff --git a/i18n/locales/zh-TW.yaml b/i18n/locales/zh-TW.yaml index 1231c0e2..887322c2 100644 --- a/i18n/locales/zh-TW.yaml +++ b/i18n/locales/zh-TW.yaml @@ -92,6 +92,7 @@ user.wechat_id_empty: "WeChat id 為空!" user.telegram_id_empty: "Telegram id 為空!" user.telegram_not_bound: "該 Telegram 帳號未綁定" user.linux_do_id_empty: "Linux DO id 為空!" +user.quota_change_zero: "額度變更量不能為0" # Quota messages quota.negative: "額度不能為負數!" diff --git a/model/user.go b/model/user.go index 1210b543..2bd1a8c3 100644 --- a/model/user.go +++ b/model/user.go @@ -523,7 +523,6 @@ func (user *User) Edit(updatePassword bool) error { "username": newUser.Username, "display_name": newUser.DisplayName, "group": newUser.Group, - "quota": newUser.Quota, "remark": newUser.Remark, } if updatePassword { @@ -896,7 +895,7 @@ func increaseUserQuota(id int, quota int) (err error) { return err } -func DecreaseUserQuota(id int, quota int) (err error) { +func DecreaseUserQuota(id int, quota int, db bool) (err error) { if quota < 0 { return errors.New("quota 不能为负数!") } @@ -906,7 +905,7 @@ func DecreaseUserQuota(id int, quota int) (err error) { common.SysLog("failed to decrease user quota: " + err.Error()) } }) - if common.BatchUpdateEnabled { + if !db && common.BatchUpdateEnabled { addNewRecord(BatchUpdateTypeUserQuota, id, -quota) return nil } @@ -928,7 +927,7 @@ func DeltaUpdateUserQuota(id int, delta int) (err error) { if delta > 0 { return IncreaseUserQuota(id, delta, false) } else { - return DecreaseUserQuota(id, -delta) + return DecreaseUserQuota(id, -delta, false) } } diff --git a/service/funding_source.go b/service/funding_source.go index 98f5e874..fc629c83 100644 --- a/service/funding_source.go +++ b/service/funding_source.go @@ -37,7 +37,7 @@ func (w *WalletFunding) PreConsume(amount int) error { if amount <= 0 { return nil } - if err := model.DecreaseUserQuota(w.userId, amount); err != nil { + if err := model.DecreaseUserQuota(w.userId, amount, false); err != nil { return err } w.consumed = amount @@ -49,7 +49,7 @@ func (w *WalletFunding) Settle(delta int) error { return nil } if delta > 0 { - return model.DecreaseUserQuota(w.userId, delta) + return model.DecreaseUserQuota(w.userId, delta, false) } return model.IncreaseUserQuota(w.userId, -delta, false) } diff --git a/service/quota.go b/service/quota.go index 9dc84ab4..4150c444 100644 --- a/service/quota.go +++ b/service/quota.go @@ -381,7 +381,7 @@ func PostConsumeQuota(relayInfo *relaycommon.RelayInfo, quota int, preConsumedQu } else { // Wallet if quota > 0 { - err = model.DecreaseUserQuota(relayInfo.UserId, quota) + err = model.DecreaseUserQuota(relayInfo.UserId, quota, false) } else { err = model.IncreaseUserQuota(relayInfo.UserId, -quota, false) } diff --git a/service/task_billing.go b/service/task_billing.go index e5c406dd..6cf7a965 100644 --- a/service/task_billing.go +++ b/service/task_billing.go @@ -90,7 +90,7 @@ func taskAdjustFunding(task *model.Task, delta int) error { return model.PostConsumeUserSubscriptionDelta(task.PrivateData.SubscriptionId, int64(delta)) } if delta > 0 { - return model.DecreaseUserQuota(task.UserId, delta) + return model.DecreaseUserQuota(task.UserId, delta, false) } return model.IncreaseUserQuota(task.UserId, -delta, false) } diff --git a/web/src/components/table/redemptions/modals/EditRedemptionModal.jsx b/web/src/components/table/redemptions/modals/EditRedemptionModal.jsx index bcde7260..e8352a63 100644 --- a/web/src/components/table/redemptions/modals/EditRedemptionModal.jsx +++ b/web/src/components/table/redemptions/modals/EditRedemptionModal.jsx @@ -25,8 +25,12 @@ import { showError, showSuccess, renderQuota, - renderQuotaWithPrompt, + getCurrencyConfig, } from '../../../../helpers'; +import { + quotaToDisplayAmount, + displayAmountToQuota, +} from '../../../../helpers/quota'; import { useIsMobile } from '../../../../hooks/common/useIsMobile'; import { Button, @@ -41,6 +45,7 @@ import { Avatar, Row, Col, + InputNumber, } from '@douyinfe/semi-ui'; import { IconCreditCard, @@ -57,10 +62,12 @@ const EditRedemptionModal = (props) => { const [loading, setLoading] = useState(isEdit); const isMobile = useIsMobile(); const formApiRef = useRef(null); + const [showQuotaInput, setShowQuotaInput] = useState(false); const getInitValues = () => ({ name: '', quota: 100000, + amount: Number(quotaToDisplayAmount(100000).toFixed(6)), count: 1, expired_time: null, }); @@ -79,6 +86,7 @@ const EditRedemptionModal = (props) => { } else { data.expired_time = new Date(data.expired_time * 1000); } + data.amount = Number(quotaToDisplayAmount(data.quota || 0).toFixed(6)); formApiRef.current?.setValues({ ...getInitValues(), ...data }); } else { showError(message); @@ -104,7 +112,12 @@ const EditRedemptionModal = (props) => { setLoading(true); let localInputs = { ...values }; localInputs.count = parseInt(localInputs.count) || 0; - localInputs.quota = parseInt(localInputs.quota) || 0; + localInputs.quota = displayAmountToQuota(localInputs.amount); + if (localInputs.quota <= 0) { + showError(t('请输入金额')); + setLoading(false); + return; + } localInputs.name = name; if (!localInputs.expired_time) { localInputs.expired_time = 0; @@ -285,37 +298,63 @@ const EditRedemptionModal = (props) => { - - + { - const num = parseInt(v, 10); - return num > 0 - ? Promise.resolve() - : Promise.reject(t('额度必须大于0')); - }, - }, - ]} - extraText={renderQuotaWithPrompt( - Number(values.quota) || 0, - )} - data={[ - { value: 500000, label: '1$' }, - { value: 5000000, label: '10$' }, - { value: 25000000, label: '50$' }, - { value: 50000000, label: '100$' }, - { value: 250000000, label: '500$' }, - { value: 500000000, label: '1000$' }, - ]} + onChange={(val) => { + const amount = val === '' || val == null ? 0 : val; + formApiRef.current?.setValue('amount', amount); + formApiRef.current?.setValue( + 'quota', + displayAmountToQuota(amount), + ); + }} showClear /> +
setShowQuotaInput((v) => !v)} + > + {showQuotaInput + ? `▾ ${t('收起原生额度输入')}` + : `▸ ${t('使用原生额度输入')}`} +
+
+ { + const num = parseInt(v, 10); + return num > 0 + ? Promise.resolve() + : Promise.reject(t('额度必须大于0')); + }, + }, + ]} + onChange={(val) => { + const quota = val === '' || val == null ? 0 : val; + formApiRef.current?.setValue('quota', quota); + formApiRef.current?.setValue( + 'amount', + Number(quotaToDisplayAmount(quota).toFixed(6)), + ); + }} + style={{ width: '100%' }} + showClear + /> +
{!isEdit && ( diff --git a/web/src/components/table/tokens/modals/EditTokenModal.jsx b/web/src/components/table/tokens/modals/EditTokenModal.jsx index 93664580..9112658b 100644 --- a/web/src/components/table/tokens/modals/EditTokenModal.jsx +++ b/web/src/components/table/tokens/modals/EditTokenModal.jsx @@ -24,10 +24,14 @@ import { showSuccess, timestamp2string, renderGroupOption, - renderQuotaWithPrompt, + getCurrencyConfig, getModelCategories, selectFilter, } from '../../../../helpers'; +import { + quotaToDisplayAmount, + displayAmountToQuota, +} from '../../../../helpers/quota'; import { useIsMobile } from '../../../../hooks/common/useIsMobile'; import { Button, @@ -41,6 +45,7 @@ import { Form, Col, Row, + InputNumber, } from '@douyinfe/semi-ui'; import { IconCreditCard, @@ -62,11 +67,13 @@ const EditTokenModal = (props) => { const formApiRef = useRef(null); const [models, setModels] = useState([]); const [groups, setGroups] = useState([]); + const [showQuotaInput, setShowQuotaInput] = useState(false); const isEdit = props.editingToken.id !== undefined; const getInitValues = () => ({ name: '', remain_quota: 0, + remain_amount: 0, expired_time: -1, unlimited_quota: true, model_limits_enabled: false, @@ -162,6 +169,9 @@ const EditTokenModal = (props) => { } else { data.model_limits = []; } + data.remain_amount = Number( + quotaToDisplayAmount(data.remain_quota || 0).toFixed(6), + ); if (formApiRef.current) { formApiRef.current.setValues({ ...getInitValues(), ...data }); } @@ -209,7 +219,14 @@ const EditTokenModal = (props) => { setLoading(true); if (isEdit) { let { tokenCount: _tc, ...localInputs } = values; - localInputs.remain_quota = parseInt(localInputs.remain_quota); + localInputs.remain_quota = localInputs.unlimited_quota + ? 0 + : displayAmountToQuota(localInputs.remain_amount); + if (!localInputs.unlimited_quota && localInputs.remain_quota <= 0) { + showError(t('请输入金额')); + setLoading(false); + return; + } if (localInputs.expired_time !== -1) { let time = Date.parse(localInputs.expired_time); if (isNaN(time)) { @@ -245,7 +262,14 @@ const EditTokenModal = (props) => { } else { localInputs.name = baseName; } - localInputs.remain_quota = parseInt(localInputs.remain_quota); + localInputs.remain_quota = localInputs.unlimited_quota + ? 0 + : displayAmountToQuota(localInputs.remain_amount); + if (!localInputs.unlimited_quota && localInputs.remain_quota <= 0) { + showError(t('请输入金额')); + setLoading(false); + break; + } if (localInputs.expired_time !== -1) { let time = Date.parse(localInputs.expired_time); @@ -497,28 +521,63 @@ const EditTokenModal = (props) => { - { + const amount = val === '' || val == null ? 0 : val; + formApiRef.current?.setValue('remain_amount', amount); + formApiRef.current?.setValue( + 'remain_quota', + displayAmountToQuota(amount), + ); + }} + style={{ width: '100%' }} + showClear /> + +
setShowQuotaInput((v) => !v)} + > + {showQuotaInput + ? `▾ ${t('收起原生额度输入')}` + : `▸ ${t('使用原生额度输入')}`} +
+
+ { + const quota = val === '' || val == null ? 0 : val; + formApiRef.current?.setValue('remain_quota', quota); + formApiRef.current?.setValue( + 'remain_amount', + Number(quotaToDisplayAmount(quota).toFixed(6)), + ); + }} + style={{ width: '100%' }} + showClear + /> +
+ { const { t } = useTranslation(); const userId = props.editingUser.id; const [loading, setLoading] = useState(true); - const [addQuotaModalOpen, setIsModalOpen] = useState(false); - const [addQuotaLocal, setAddQuotaLocal] = useState(''); - const [addAmountLocal, setAddAmountLocal] = useState(''); + const [adjustModalOpen, setAdjustModalOpen] = useState(false); + const [adjustQuotaLocal, setAdjustQuotaLocal] = useState(''); + const [adjustAmountLocal, setAdjustAmountLocal] = useState(''); + const [adjustMode, setAdjustMode] = useState('add'); + const [adjustLoading, setAdjustLoading] = useState(false); const isMobile = useIsMobile(); const [groupOptions, setGroupOptions] = useState([]); const [bindingModalVisible, setBindingModalVisible] = useState(false); const formApiRef = useRef(null); + const [showAdjustQuotaRaw, setShowAdjustQuotaRaw] = useState(false); + const [showQuotaInput, setShowQuotaInput] = useState(false); + const [inputs, setInputs] = useState(null); const isEdit = Boolean(userId); @@ -85,6 +91,7 @@ const EditUserModal = (props) => { linux_do_id: '', email: '', quota: 0, + quota_amount: 0, group: 'default', remark: '', }); @@ -107,13 +114,22 @@ const EditUserModal = (props) => { const { success, message, data } = res.data; if (success) { data.password = ''; - formApiRef.current?.setValues({ ...getInitValues(), ...data }); + data.quota_amount = Number( + quotaToDisplayAmount(data.quota || 0).toFixed(6), + ); + setInputs({ ...getInitValues(), ...data }); } else { showError(message); } setLoading(false); }; + useEffect(() => { + if (inputs && formApiRef.current) { + formApiRef.current.setValues(inputs); + } + }, [inputs]); + useEffect(() => { loadUser(); if (userId) fetchGroups(); @@ -132,8 +148,8 @@ const EditUserModal = (props) => { const submit = async (values) => { setLoading(true); let payload = { ...values }; - if (typeof payload.quota === 'string') - payload.quota = parseInt(payload.quota) || 0; + delete payload.quota; + delete payload.quota_amount; if (userId) { payload.id = parseInt(userId); } @@ -150,11 +166,60 @@ const EditUserModal = (props) => { setLoading(false); }; - /* --------------------- quota helper -------------------- */ - const addLocalQuota = () => { - const current = parseInt(formApiRef.current?.getValue('quota') || 0); - const delta = parseInt(addQuotaLocal) || 0; - formApiRef.current?.setValue('quota', current + delta); + /* --------------------- atomic quota adjust -------------------- */ + const adjustQuota = async () => { + const quotaVal = parseInt(adjustQuotaLocal) || 0; + if (quotaVal <= 0 && adjustMode !== 'override') return; + if (adjustMode === 'override' && (adjustQuotaLocal === '' || adjustQuotaLocal == null)) return; + setAdjustLoading(true); + try { + const res = await API.post('/api/user/manage', { + id: parseInt(userId), + action: 'add_quota', + mode: adjustMode, + value: adjustMode === 'override' ? quotaVal : Math.abs(quotaVal), + }); + const { success, message } = res.data; + if (success) { + showSuccess(t('调整额度成功')); + setAdjustModalOpen(false); + setAdjustQuotaLocal(''); + setAdjustAmountLocal(''); + const userRes = await API.get(`/api/user/${userId}`); + if (userRes.data.success) { + const data = userRes.data.data; + data.password = ''; + data.quota_amount = Number( + quotaToDisplayAmount(data.quota || 0).toFixed(6), + ); + setInputs({ ...getInitValues(), ...data }); + } + props.refresh(); + } else { + showError(message); + } + } catch (e) { + showError(e.message); + } + setAdjustLoading(false); + }; + + const getPreviewText = () => { + const current = formApiRef.current?.getValue('quota') || 0; + const val = parseInt(adjustQuotaLocal) || 0; + let result; + switch (adjustMode) { + case 'add': + result = current + Math.abs(val); + return `${t('当前额度')}:${renderQuota(current)},+${renderQuota(Math.abs(val))} = ${renderQuota(result)}`; + case 'subtract': + result = current - Math.abs(val); + return `${t('当前额度')}:${renderQuota(current)},-${renderQuota(Math.abs(val))} = ${renderQuota(result)}`; + case 'override': + return `${t('当前额度')}:${renderQuota(current)} → ${renderQuota(val)}`; + default: + return ''; + } }; /* --------------------------- UI --------------------------- */ @@ -305,24 +370,47 @@ const EditUserModal = (props) => { - + + + +
setShowQuotaInput((v) => !v)} + > + {showQuotaInput + ? `▾ ${t('收起原生额度输入')}` + : `▸ ${t('使用原生额度输入')}`} +
+
+ +
+
)} @@ -372,81 +460,102 @@ const EditUserModal = (props) => { formApiRef={formApiRef} /> - {/* 添加额度模态框 */} + {/* 调整额度模态框 */} { - addLocalQuota(); - setIsModalOpen(false); - setAddQuotaLocal(''); - setAddAmountLocal(''); - }} + visible={adjustModalOpen} + onOk={adjustQuota} onCancel={() => { - setIsModalOpen(false); + setAdjustModalOpen(false); + setAdjustQuotaLocal(''); + setAdjustAmountLocal(''); + setAdjustMode('add'); }} + confirmLoading={adjustLoading} closable={null} title={
- - {t('添加额度')} + + {t('调整额度')}
} >
- {(() => { - const current = formApiRef.current?.getValue('quota') || 0; - return ( - - {`${t('新额度:')}${renderQuota(current)} + ${renderQuota(addQuotaLocal)} = ${renderQuota(current + parseInt(addQuotaLocal || 0))}`} - - ); - })()} + + {getPreviewText()} +
- {getCurrencyConfig().type !== 'TOKENS' && ( -
-
- {t('金额')} - - {' '} - ({t('仅用于换算,实际保存的是额度')}) - -
- { - setAddAmountLocal(val); - setAddQuotaLocal( - val != null && val !== '' - ? displayAmountToQuota(Math.abs(val)) * Math.sign(val) - : '', - ); - }} - style={{ width: '100%' }} - showClear - /> +
+
+ {t('操作')}
- )} -
+ { + setAdjustMode(e.target.value); + setAdjustQuotaLocal(''); + setAdjustAmountLocal(''); + }} + style={{ width: '100%' }} + > + {t('添加')} + {t('减少')} + {t('覆盖')} + +
+
+
+ {t('金额')} +
+ { + const amount = val === '' || val == null ? '' : val; + setAdjustAmountLocal(amount); + setAdjustQuotaLocal( + amount === '' + ? '' + : adjustMode === 'override' + ? displayAmountToQuota(amount) + : displayAmountToQuota(Math.abs(amount)), + ); + }} + style={{ width: '100%' }} + showClear + /> +
+
setShowAdjustQuotaRaw((v) => !v)} + > + {showAdjustQuotaRaw + ? `▾ ${t('收起原生额度输入')}` + : `▸ ${t('使用原生额度输入')}`} +
+
{t('额度')}
{ - setAddQuotaLocal(val); - setAddAmountLocal( - val != null && val !== '' - ? Number( - ( - quotaToDisplayAmount(Math.abs(val)) * Math.sign(val) - ).toFixed(2), - ) - : '', + const quota = val === '' || val == null ? '' : val; + setAdjustQuotaLocal(quota); + setAdjustAmountLocal( + quota === '' + ? '' + : adjustMode === 'override' + ? Number(quotaToDisplayAmount(quota).toFixed(6)) + : Number(quotaToDisplayAmount(Math.abs(quota)).toFixed(6)), ); }} style={{ width: '100%' }} diff --git a/web/src/helpers/quota.js b/web/src/helpers/quota.js index 2733af24..1d08aa68 100644 --- a/web/src/helpers/quota.js +++ b/web/src/helpers/quota.js @@ -1,3 +1,21 @@ +/* +Copyright (C) 2025 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 . + +For commercial licensing, please contact support@quantumnous.com +*/ import { getCurrencyConfig } from './render'; export const getQuotaPerUnit = () => { @@ -7,19 +25,23 @@ export const getQuotaPerUnit = () => { export const quotaToDisplayAmount = (quota) => { const q = Number(quota || 0); - if (!Number.isFinite(q) || q <= 0) return 0; + if (!Number.isFinite(q) || q === 0) return 0; + const sign = Math.sign(q); + const abs = Math.abs(q); const { type, rate } = getCurrencyConfig(); if (type === 'TOKENS') return q; - const usd = q / getQuotaPerUnit(); - if (type === 'USD') return usd; - return usd * (rate || 1); + const usd = abs / getQuotaPerUnit(); + if (type === 'USD') return sign * usd; + return sign * usd * (rate || 1); }; export const displayAmountToQuota = (amount) => { const val = Number(amount || 0); - if (!Number.isFinite(val) || val <= 0) return 0; + if (!Number.isFinite(val) || val === 0) return 0; + const sign = Math.sign(val); + const abs = Math.abs(val); const { type, rate } = getCurrencyConfig(); if (type === 'TOKENS') return Math.round(val); - const usd = type === 'USD' ? val : val / (rate || 1); - return Math.round(usd * getQuotaPerUnit()); + const usd = type === 'USD' ? abs : abs / (rate || 1); + return sign * Math.round(usd * getQuotaPerUnit()); }; diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 52ef677f..1a2841c1 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -825,6 +825,8 @@ "原密码": "Original Password", "原生格式": "Native format", "原生额度": "Raw quota", + "使用原生额度输入": "Use raw quota input", + "收起原生额度输入": "Hide raw quota input", "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "Deduplication completed: {{before}} keys before deduplication, {{after}} keys after deduplication", "参与官方同步": "Participate in official sync", "参数": "parameter", @@ -2166,6 +2168,14 @@ "添加键值对": "Add key-value pair", "添加问答": "Add FAQ", "添加额度": "Add quota", + "减少": "Subtract", + "覆盖": "Override", + "调整额度": "Adjust Quota", + "调整额度成功": "Quota adjusted successfully", + "当前额度": "Current quota", + "变更": "Change", + "预计结果": "Estimated result", + "正数为增加,负数为减少": "Positive to add, negative to subtract", "清理不活跃缓存": "Clean up inactive cache", "清理失败": "Cleanup failed", "清理方式": "Cleanup Mode", diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json index bbec562f..db603ed3 100644 --- a/web/src/i18n/locales/fr.json +++ b/web/src/i18n/locales/fr.json @@ -821,6 +821,8 @@ "原密码": "Mot de passe original", "原生格式": "Format natif", "原生额度": "Quota brut", + "使用原生额度输入": "Saisir le quota brut", + "收起原生额度输入": "Masquer la saisie du quota brut", "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "Doublons supprimés : {{before}} clés avant, {{after}} clés après", "参与官方同步": "Participer à la synchronisation officielle", "参数": "paramètre", @@ -2144,6 +2146,14 @@ "添加键值对": "Ajouter une paire clé-valeur", "添加问答": "Ajouter une FAQ", "添加额度": "Ajouter un quota", + "减少": "Soustraire", + "覆盖": "Remplacer", + "调整额度": "Ajuster le quota", + "调整额度成功": "Quota ajusté avec succès", + "当前额度": "Quota actuel", + "变更": "Modification", + "预计结果": "Résultat estimé", + "正数为增加,负数为减少": "Positif pour ajouter, négatif pour soustraire", "清理不活跃缓存": "Nettoyer le cache inactif", "清理失败": "Échec du nettoyage", "清理方式": "Mode de nettoyage", diff --git a/web/src/i18n/locales/ja.json b/web/src/i18n/locales/ja.json index 11ac9365..c449048a 100644 --- a/web/src/i18n/locales/ja.json +++ b/web/src/i18n/locales/ja.json @@ -812,6 +812,8 @@ "原密码": "現在のパスワード", "原生格式": "ネイティブ形式", "原生额度": "生クォータ", + "使用原生额度输入": "生クォータで入力", + "收起原生额度输入": "生クォータ入力を非表示", "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "重複排除完了:重複排除前 {{before}} 個のAPIキー、重複排除後 {{after}} 個のAPIキー", "参与官方同步": "公式との同期", "参数": "パラメータ", @@ -2127,6 +2129,14 @@ "添加键值对": "キー/値ペア追加", "添加问答": "FAQ追加", "添加额度": "残高追加", + "减少": "減少", + "覆盖": "上書き", + "调整额度": "残高調整", + "调整额度成功": "残高の調整に成功しました", + "当前额度": "現在の残高", + "变更": "変更", + "预计结果": "予想結果", + "正数为增加,负数为减少": "正の数で追加、負の数で減少", "清理不活跃缓存": "非アクティブなキャッシュをクリーンアップ", "清理失败": "クリーンアップに失敗しました", "清理方式": "クリーンアップモード", diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index aa7127b7..54f79adf 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -827,6 +827,8 @@ "原密码": "Старый пароль", "原生格式": "Нативный формат", "原生额度": "Исходный лимит", + "使用原生额度输入": "Ввод в исходных единицах", + "收起原生额度输入": "Скрыть ввод в исходных единицах", "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "Дедупликация завершена: до дедупликации {{before}} ключей, после дедупликации {{after}} ключей", "参与官方同步": "Участвовать в официальной синхронизации", "参数": "Параметры", @@ -2156,6 +2158,14 @@ "添加键值对": "Добавить пару ключ-значение", "添加问答": "Добавить вопрос-ответ", "添加额度": "Добавить лимит", + "减少": "Уменьшить", + "覆盖": "Заменить", + "调整额度": "Скорректировать квоту", + "调整额度成功": "Квота успешно скорректирована", + "当前额度": "Текущая квота", + "变更": "Изменение", + "预计结果": "Ожидаемый результат", + "正数为增加,负数为减少": "Положительное для увеличения, отрицательное для уменьшения", "清理不活跃缓存": "Очистить неактивный кэш", "清理失败": "Ошибка очистки", "清理方式": "Режим очистки", diff --git a/web/src/i18n/locales/vi.json b/web/src/i18n/locales/vi.json index 7497cf7f..8ec57416 100644 --- a/web/src/i18n/locales/vi.json +++ b/web/src/i18n/locales/vi.json @@ -813,6 +813,8 @@ "原密码": "Mật khẩu cũ", "原生格式": "Định dạng gốc", "原生额度": "Hạn mức gốc", + "使用原生额度输入": "Nhập hạn mức gốc", + "收起原生额度输入": "Ẩn nhập hạn mức gốc", "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "Hoàn tất loại bỏ trùng lặp: {{before}} khóa trước khi loại bỏ, {{after}} khóa sau khi loại bỏ", "参与官方同步": "Tham gia đồng bộ chính thức", "参数": "tham số", @@ -2221,6 +2223,14 @@ "添加键值对": "Thêm cặp khóa-giá trị", "添加问答": "Thêm hỏi đáp", "添加额度": "Thêm hạn ngạch", + "减少": "Giảm", + "覆盖": "Ghi đè", + "调整额度": "Điều chỉnh hạn ngạch", + "调整额度成功": "Điều chỉnh hạn ngạch thành công", + "当前额度": "Hạn ngạch hiện tại", + "变更": "Thay đổi", + "预计结果": "Kết quả dự kiến", + "正数为增加,负数为减少": "Số dương để tăng, số âm để giảm", "清理": "Dọn dẹp", "清理不活跃缓存": "Xóa cache không hoạt động", "清理历史日志": "Dọn dẹp nhật ký lịch sử", diff --git a/web/src/i18n/locales/zh-CN.json b/web/src/i18n/locales/zh-CN.json index 69e824e6..c1142a08 100644 --- a/web/src/i18n/locales/zh-CN.json +++ b/web/src/i18n/locales/zh-CN.json @@ -1605,6 +1605,14 @@ "添加键值对": "添加键值对", "添加问答": "添加问答", "添加额度": "添加额度", + "减少": "减少", + "覆盖": "覆盖", + "调整额度": "调整额度", + "调整额度成功": "调整额度成功", + "当前额度": "当前额度", + "变更": "变更", + "预计结果": "预计结果", + "正数为增加,负数为减少": "正数为增加,负数为减少", "清理方式": "清理方式", "清理日志文件": "清理日志文件", "清空": "清空", @@ -2737,6 +2745,8 @@ "请输入总额度": "请输入总额度", "0 表示不限": "0 表示不限", "原生额度": "原生额度", + "使用原生额度输入": "使用原生额度输入", + "收起原生额度输入": "收起原生额度输入", "升级分组": "升级分组", "不升级": "不升级", "购买或手动新增订阅会升级到该分组;当套餐失效/过期或手动作废/删除后,将回退到升级前分组。回退不会立即生效,通常会有几分钟延迟。": "购买或手动新增订阅会升级到该分组;当套餐失效/过期或手动作废/删除后,将回退到升级前分组。回退不会立即生效,通常会有几分钟延迟。", diff --git a/web/src/i18n/locales/zh-TW.json b/web/src/i18n/locales/zh-TW.json index ac853e4b..a5cea876 100644 --- a/web/src/i18n/locales/zh-TW.json +++ b/web/src/i18n/locales/zh-TW.json @@ -719,6 +719,8 @@ "原密码": "原密碼", "原生格式": "原生格式", "原生额度": "原生額度", + "使用原生额度输入": "使用原生額度輸入", + "收起原生额度输入": "收起原生額度輸入", "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "去重完成:去重前 {{before}} 個密鑰,去重後 {{after}} 個密鑰", "参与官方同步": "參與官方同步", "参数": "參數", @@ -1905,6 +1907,14 @@ "添加键值对": "添加鍵值對", "添加问答": "添加問答", "添加额度": "添加額度", + "减少": "減少", + "覆盖": "覆蓋", + "调整额度": "調整額度", + "调整额度成功": "調整額度成功", + "当前额度": "當前額度", + "变更": "變更", + "预计结果": "預計結果", + "正数为增加,负数为减少": "正數為增加,負數為減少", "清理不活跃缓存": "清理不活躍快取", "清理失败": "清理失敗", "清理方式": "清理方式", From ed7f839911055fcf414cea6d83fae52762a72f34 Mon Sep 17 00:00:00 2001 From: CaIon Date: Sat, 11 Apr 2026 17:19:38 +0800 Subject: [PATCH 02/46] feat: improve model price error UX with role-aware messages and cleaner UI - Backend: differentiate error messages for admin vs regular users in price.go - Backend: include error_code in channel test response for structured error handling - Frontend: render model_price_error as a styled card in Playground with admin nav button - Frontend: show inline error details and settings link in channel test modal - Frontend: parse error codes from both SSE and non-streaming API responses - i18n: remove redundant "Settings" suffix from setting tab translations (en/fr/ru/ja/vi) - i18n: update "Group & Model Pricing" translations across all locales --- controller/channel-test.go | 18 ++++--- controller/relay.go | 2 +- relay/helper/price.go | 20 +++++++- relay/responses_handler.go | 2 +- types/error.go | 6 +++ .../components/playground/MessageContent.jsx | 41 +++++++++++++++- .../table/channels/modals/ModelTestModal.jsx | 47 ++++++++++++++---- web/src/hooks/channels/useChannelsData.jsx | 8 +-- web/src/hooks/playground/useApiRequest.jsx | 49 ++++++++++++++++--- web/src/i18n/locales/en.json | 25 +++++----- web/src/i18n/locales/fr.json | 9 ++-- web/src/i18n/locales/ja.json | 25 +++++----- web/src/i18n/locales/ru.json | 25 +++++----- web/src/i18n/locales/vi.json | 25 +++++----- web/src/i18n/locales/zh-CN.json | 3 +- web/src/i18n/locales/zh-TW.json | 3 +- 16 files changed, 224 insertions(+), 84 deletions(-) diff --git a/controller/channel-test.go b/controller/channel-test.go index bdd67d27..db0326d3 100644 --- a/controller/channel-test.go +++ b/controller/channel-test.go @@ -150,6 +150,7 @@ func testChannel(channel *model.Channel, testModel string, endpointType string, } } cache.WriteContext(c) + c.Set("id", 1) //c.Request.Header.Set("Authorization", "Bearer "+channel.Key) c.Request.Header.Set("Content-Type", "application/json") @@ -274,7 +275,7 @@ func testChannel(channel *model.Channel, testModel string, endpointType string, return testResult{ context: c, localErr: err, - newAPIError: types.NewError(err, types.ErrorCodeModelPriceError), + newAPIError: types.NewError(err, types.ErrorCodeModelPriceError, types.ErrOptionWithStatusCode(http.StatusBadRequest)), } } @@ -756,11 +757,15 @@ func TestChannel(c *gin.Context) { tik := time.Now() result := testChannel(channel, testModel, endpointType, isStream) if result.localErr != nil { - c.JSON(http.StatusOK, gin.H{ + resp := gin.H{ "success": false, "message": result.localErr.Error(), "time": 0.0, - }) + } + if result.newAPIError != nil { + resp["error_code"] = result.newAPIError.GetErrorCode() + } + c.JSON(http.StatusOK, resp) return } tok := time.Now() @@ -769,9 +774,10 @@ func TestChannel(c *gin.Context) { consumedTime := float64(milliseconds) / 1000.0 if result.newAPIError != nil { c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": result.newAPIError.Error(), - "time": consumedTime, + "success": false, + "message": result.newAPIError.Error(), + "time": consumedTime, + "error_code": result.newAPIError.GetErrorCode(), }) return } diff --git a/controller/relay.go b/controller/relay.go index 593b31b7..a518beed 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -151,7 +151,7 @@ func Relay(c *gin.Context, relayFormat types.RelayFormat) { priceData, err := helper.ModelPriceHelper(c, relayInfo, tokens, meta) if err != nil { - newAPIError = types.NewError(err, types.ErrorCodeModelPriceError) + newAPIError = types.NewError(err, types.ErrorCodeModelPriceError, types.ErrOptionWithStatusCode(http.StatusBadRequest)) return } diff --git a/relay/helper/price.go b/relay/helper/price.go index e9c3b463..8ba0ee8f 100644 --- a/relay/helper/price.go +++ b/relay/helper/price.go @@ -5,6 +5,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/logger" + "github.com/QuantumNous/new-api/model" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/setting/operation_setting" "github.com/QuantumNous/new-api/setting/ratio_setting" @@ -13,6 +14,21 @@ import ( "github.com/gin-gonic/gin" ) +func modelPriceNotConfiguredError(modelName string, userId int) error { + if model.IsAdmin(userId) { + return fmt.Errorf( + "模型 %s 的价格未配置。请前往「系统设置 → 运营设置」开启自用模式,或在「系统设置 → 分组与模型定价设置」中为该模型配置价格;"+ + "Model %s price not configured. Go to System Settings → Operation Settings to enable self-use mode, or configure the model price in System Settings → Group & Model Pricing.", + modelName, modelName, + ) + } + return fmt.Errorf( + "模型 %s 的价格尚未由管理员配置,暂时无法使用,请联系站点管理员开启该模型;"+ + "Model %s has not been priced by the administrator yet. Please contact the site administrator to enable this model.", + modelName, modelName, + ) +} + // https://docs.claude.com/en/docs/build-with-claude/prompt-caching#1-hour-cache-duration const claudeCacheCreation1hMultiplier = 6 / 3.75 @@ -75,7 +91,7 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens acceptUnsetRatio = true } if !acceptUnsetRatio { - return types.PriceData{}, fmt.Errorf("模型 %s 倍率或价格未配置,请联系管理员设置或开始自用模式;Model %s ratio or price not set, please set or start self-use mode", matchName, matchName) + return types.PriceData{}, modelPriceNotConfiguredError(matchName, info.UserId) } } completionRatio = ratio_setting.GetCompletionRatio(info.OriginModelName) @@ -161,7 +177,7 @@ func ModelPriceHelperPerCall(c *gin.Context, info *relaycommon.RelayInfo) (types acceptUnsetRatio = true } if !ratioSuccess && !acceptUnsetRatio { - return types.PriceData{}, fmt.Errorf("模型 %s 倍率或价格未配置,请联系管理员设置或开始自用模式;Model %s ratio or price not set, please set or start self-use mode", matchName, matchName) + return types.PriceData{}, modelPriceNotConfiguredError(matchName, info.UserId) } } } diff --git a/relay/responses_handler.go b/relay/responses_handler.go index 09e490d9..58324aa7 100644 --- a/relay/responses_handler.go +++ b/relay/responses_handler.go @@ -143,7 +143,7 @@ func ResponsesHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError * if err != nil { info.OriginModelName = originModelName info.PriceData = originPriceData - return types.NewError(err, types.ErrorCodeModelPriceError, types.ErrOptionWithSkipRetry()) + return types.NewError(err, types.ErrorCodeModelPriceError, types.ErrOptionWithSkipRetry(), types.ErrOptionWithStatusCode(http.StatusBadRequest)) } service.PostTextConsumeQuota(c, info, usageDto, nil) diff --git a/types/error.go b/types/error.go index 6af39f7e..9717401a 100644 --- a/types/error.go +++ b/types/error.go @@ -390,6 +390,12 @@ func ErrOptionWithNoRecordErrorLog() NewAPIErrorOptions { } } +func ErrOptionWithStatusCode(statusCode int) NewAPIErrorOptions { + return func(e *NewAPIError) { + e.StatusCode = statusCode + } +} + func ErrOptionWithHideErrMsg(replaceStr string) NewAPIErrorOptions { return func(e *NewAPIError) { if common.DebugEnabled { diff --git a/web/src/components/playground/MessageContent.jsx b/web/src/components/playground/MessageContent.jsx index c02ab3a2..94f494bb 100644 --- a/web/src/components/playground/MessageContent.jsx +++ b/web/src/components/playground/MessageContent.jsx @@ -21,8 +21,9 @@ import React, { useRef, useEffect } from 'react'; import { Typography, TextArea, Button } from '@douyinfe/semi-ui'; import MarkdownRenderer from '../common/markdown/MarkdownRenderer'; import ThinkingContent from './ThinkingContent'; -import { Loader2, Check, X } from 'lucide-react'; +import { Loader2, Check, X, Settings, AlertTriangle } from 'lucide-react'; import { useTranslation } from 'react-i18next'; +import { isAdmin } from '../../helpers/utils'; const MessageContent = ({ message, @@ -64,6 +65,44 @@ const MessageContent = ({ errorText = t('请求发生错误'); } + if (message.errorCode === 'model_price_error') { + return ( +
+
+
+ + + {t('模型价格未配置')} + +
+ + {errorText} + + {isAdmin() && ( + + )} +
+
+ ); + } + return (
{errorText} diff --git a/web/src/components/table/channels/modals/ModelTestModal.jsx b/web/src/components/table/channels/modals/ModelTestModal.jsx index 490cf54b..e7f57453 100644 --- a/web/src/components/table/channels/modals/ModelTestModal.jsx +++ b/web/src/components/table/channels/modals/ModelTestModal.jsx @@ -30,6 +30,7 @@ import { Banner, } from '@douyinfe/semi-ui'; import { IconSearch, IconInfoCircle } from '@douyinfe/semi-icons'; +import { Settings } from 'lucide-react'; import { copy, showError, showInfo, showSuccess } from '../../../../helpers'; import { MODEL_TABLE_PAGE_SIZE } from '../../../../constants'; @@ -168,17 +169,43 @@ const ModelTestModal = ({ } return ( -
- - {testResult.success ? t('成功') : t('失败')} - - {testResult.success && ( - - {t('请求时长: ${time}s').replace( - '${time}', - testResult.time.toFixed(2), +
+
+ + {testResult.success ? t('成功') : t('失败')} + + {testResult.success && ( + + {t('请求时长: ${time}s').replace( + '${time}', + testResult.time.toFixed(2), + )} + + )} +
+ {!testResult.success && testResult.message && ( +
+ + {testResult.message} + + {testResult.errorCode === 'model_price_error' && ( + )} - +
)}
); diff --git a/web/src/hooks/channels/useChannelsData.jsx b/web/src/hooks/channels/useChannelsData.jsx index 37ee5010..e0208683 100644 --- a/web/src/hooks/channels/useChannelsData.jsx +++ b/web/src/hooks/channels/useChannelsData.jsx @@ -890,7 +890,7 @@ export const useChannelsData = () => { return Promise.resolve(); } - const { success, message, time } = res.data; + const { success, message, time, error_code } = res.data; // 更新测试结果 setModelTestResults((prev) => ({ @@ -900,6 +900,7 @@ export const useChannelsData = () => { message, time: time || 0, timestamp: Date.now(), + errorCode: error_code || null, }, })); @@ -927,7 +928,7 @@ export const useChannelsData = () => { ); } } else { - showError(`${t('模型')} ${model}: ${message}`); + showError(message); } } catch (error) { // 处理网络错误 @@ -939,9 +940,10 @@ export const useChannelsData = () => { message: error.message || t('网络错误'), time: 0, timestamp: Date.now(), + errorCode: null, }, })); - showError(`${t('模型')} ${model}: ${error.message || t('测试失败')}`); + showError(error.message || t('测试失败')); } finally { // 从正在测试的模型集合中移除 setTestingModels((prev) => { diff --git a/web/src/hooks/playground/useApiRequest.jsx b/web/src/hooks/playground/useApiRequest.jsx index 8ec50cf4..f072084b 100644 --- a/web/src/hooks/playground/useApiRequest.jsx +++ b/web/src/hooks/playground/useApiRequest.jsx @@ -196,10 +196,17 @@ export const useApiRequest = ( if (!response.ok) { let errorBody = ''; + let parsedError = null; try { errorBody = await response.text(); + const errorJson = JSON.parse(errorBody); + if (errorJson?.error) { + parsedError = errorJson.error; + } } catch (e) { - errorBody = '无法读取错误响应体'; + if (!errorBody) { + errorBody = '无法读取错误响应体'; + } } const errorInfo = handleApiError( @@ -215,9 +222,13 @@ export const useApiRequest = ( })); setActiveDebugTab(DEBUG_TABS.RESPONSE); - throw new Error( - `HTTP error! status: ${response.status}, body: ${errorBody}`, + const err = new Error( + parsedError?.message || + `HTTP error! status: ${response.status}, body: ${errorBody}`, ); + err.errorCode = parsedError?.code || null; + err.errorType = parsedError?.type || null; + throw err; } const data = await response.json(); @@ -277,6 +288,7 @@ export const useApiRequest = ( newMessages[newMessages.length - 1] = { ...lastMessage, content: t('请求发生错误: ') + error.message, + errorCode: error.errorCode || null, status: MESSAGE_STATUS.ERROR, ...autoCollapseState, }; @@ -379,7 +391,20 @@ export const useApiRequest = ( // 只有在流没有正常完成且连接状态异常时才处理错误 if (!isStreamComplete && source.readyState !== 2) { console.error('SSE Error:', e); - const errorMessage = e.data || t('请求发生错误'); + let errorMessage = e.data || t('请求发生错误'); + let errorCode = null; + + if (e.data) { + try { + const errorJson = JSON.parse(e.data); + if (errorJson?.error) { + errorMessage = errorJson.error.message || errorMessage; + errorCode = errorJson.error.code || null; + } + } catch (_) { + // not JSON, use raw data as error message + } + } const errorInfo = handleApiError(new Error(errorMessage)); errorInfo.readyState = source.readyState; @@ -393,8 +418,19 @@ export const useApiRequest = ( })); setActiveDebugTab(DEBUG_TABS.RESPONSE); - streamMessageUpdate(errorMessage, 'content'); - completeMessage(MESSAGE_STATUS.ERROR); + setMessage((prevMessage) => { + const newMessages = [...prevMessage]; + const lastMessage = newMessages[newMessages.length - 1]; + if (lastMessage && lastMessage.status !== MESSAGE_STATUS.COMPLETE && lastMessage.status !== MESSAGE_STATUS.ERROR) { + newMessages[newMessages.length - 1] = { + ...lastMessage, + content: (lastMessage.content || '') + errorMessage, + errorCode: errorCode, + status: MESSAGE_STATUS.ERROR, + }; + } + return newMessages; + }); sseSourceRef.current = null; source.close(); } @@ -446,6 +482,7 @@ export const useApiRequest = ( [ setDebugData, setActiveDebugTab, + setMessage, streamMessageUpdate, completeMessage, t, diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 1a2841c1..a72e2c62 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -410,7 +410,7 @@ "以下上游数据可能不可信:": "The following upstream data may not be reliable: ", "以下文件解析失败,已忽略:{{list}}": "The following files failed to parse and have been ignored: {{list}}", "以及": "and", - "仪表盘设置": "Dashboard Settings", + "仪表盘设置": "Dashboard", "价格": "Pricing", "价格摘要": "Price Summary", "价格暂时不可用,请稍后重试": "Price temporarily unavailable, please try again later", @@ -678,7 +678,7 @@ "其他": "Other", "其他注册选项": "Other registration options", "其他登录选项": "Other login options", - "其他设置": "Other Settings", + "其他设置": "Other", "其他详情": "Other details", "内存 阈值 (%)": "Memory Threshold (%)", "内存使用率超过此值时拒绝请求": "Reject requests when memory usage exceeds this value", @@ -699,7 +699,7 @@ "分类名称": "Category Name", "分组": "Group", "分组JSON设置": "Group JSON Settings", - "分组与模型定价设置": "Group and Model Pricing Settings", + "分组与模型定价设置": "Group & Model Pricing", "分组价格": "Group price", "分组倍率": "Group ratio", "分组倍率设置": "Group ratio settings", @@ -1442,7 +1442,7 @@ "思考预算占比": "Thinking budget ratio", "性能指标": "Performance Indicators", "性能监控": "Performance Monitor", - "性能设置": "Performance Settings", + "性能设置": "Performance", "总 GPU 小时": "Total GPU Hours", "总价:文字价格 {{textPrice}} + 音频价格 {{audioPrice}} = {{symbol}}{{total}}": "Total price: text price {{textPrice}} + audio price {{audioPrice}} = {{symbol}}{{total}}", "总分配内存": "Total Allocated Memory", @@ -1596,7 +1596,7 @@ "支付方式名称": "Pay Method Name", "支付方式类型": "Pay Method Type", "支付渠道": "Payment Channels", - "支付设置": "Payment Settings", + "支付设置": "Payment", "支付请求失败": "Payment request failed", "支付金额": "Payment Amount", "支持 Ctrl+V 粘贴图片": "Supports Ctrl+V to paste images", @@ -2005,7 +2005,7 @@ "模型消耗趋势": "Model consumption trend", "模型版本": "Model version", "模型的详细描述和基本特性": "Detailed description and basic characteristics of the model", - "模型相关设置": "Model related settings", + "模型相关设置": "Model Related", "模型社区需要大家的共同维护,如发现数据有误或想贡献新的模型数据,请访问:": "The model community needs everyone's contribution. If you find incorrect data or want to contribute new models, please visit:", "模型管理": "Model Management", "模型组": "Model group", @@ -2018,7 +2018,7 @@ "模型部署": "Model Deployment", "模型部署服务未启用": "Model deployment service is not enabled", "模型部署管理": "Model Deployment Management", - "模型部署设置": "Model Deployment Settings", + "模型部署设置": "Model Deployment", "模型配置": "Model Configuration", "模型重定向": "Model mapping", "模型重定向里的下列模型尚未添加到“模型”列表,调用时会因为缺少可用模型而失败:": "The following models from the redirect have not been added to the “Models” list and requests will fail due to no available model:", @@ -2541,7 +2541,7 @@ "系统文档和帮助信息": "System documentation and help information", "系统消息": "System message", "系统管理功能": "System management functions", - "系统设置": "System Settings", + "系统设置": "System", "系统访问令牌": "System Access Token", "索引": "Index", "紧凑列表": "Compact list", @@ -2570,7 +2570,7 @@ "绘图": "Drawing", "绘图任务记录": "Drawing task records", "绘图日志": "Drawing Logs", - "绘图设置": "Drawing settings", + "绘图设置": "Drawing", "统一的": "The Unified", "统计Tokens": "Statistical Tokens", "统计已重置": "Statistics reset", @@ -2648,7 +2648,7 @@ "聊天区域": "Chat Area", "聊天应用名称": "Chat Application Name", "聊天应用名称已存在,请使用其他名称": "Chat application name already exists, please use another name", - "聊天设置": "Chat settings", + "聊天设置": "Chat", "聊天配置": "Chat configuration", "聊天链接配置错误,请联系管理员": "Chat link configuration error, please contact administrator", "联系我们": "Contact Us", @@ -2898,6 +2898,7 @@ "请求参数无效": "Invalid request parameters", "请求发生错误": "An error occurred with the request", "请求发生错误: ": "An error occurred with the request: ", + "模型价格未配置": "Model Price Not Configured", "请求后端接口失败:": "Failed to request the backend interface: ", "请求失败": "Request failed", "请求头覆盖": "Request header override", @@ -3180,7 +3181,7 @@ "过期时间不能早于当前时间!": "Expiration time cannot be earlier than the current time!", "过期时间快捷设置": "Expiration time quick settings", "过期时间格式错误!": "Expiration time format error!", - "运营设置": "Operation Settings", + "运营设置": "Operation", "运行中": "Running", "运行命令 (Command)": "Command", "运行时长": "Runtime Duration", @@ -3268,7 +3269,7 @@ "通道 ${name} 余额更新成功!": "Channel ${name} quota updated successfully!", "通道 ${name} 测试成功,模型 ${model} 耗时 ${time.toFixed(2)} 秒。": "Channel ${name} test successful, model ${model} took ${time.toFixed(2)} seconds.", "通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。": "Channel ${name} test successful, took ${time.toFixed(2)} seconds.", - "速率限制设置": "Rate limit settings", + "速率限制设置": "Rate Limit", "逻辑": "Logic", "邀请": "Invitations", "邀请人": "Inviter", diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json index db603ed3..b870cb37 100644 --- a/web/src/i18n/locales/fr.json +++ b/web/src/i18n/locales/fr.json @@ -695,7 +695,7 @@ "分类名称": "Nom de la catégorie", "分组": "Groupe", "分组JSON设置": "Group JSON Settings", - "分组与模型定价设置": "Groupe et tarification", + "分组与模型定价设置": "Groupes & tarification des modèles", "分组价格": "Prix de groupe", "分组倍率": "Ratio", "分组倍率设置": "Ratio de groupe", @@ -1439,7 +1439,7 @@ "思考预算占比": "Ratio du budget de la pensée", "性能指标": "Indicateurs de performance", "性能监控": "Surveillance des performances", - "性能设置": "Paramètres de performance", + "性能设置": "Performance", "总 GPU 小时": "Total GPU Hours", "总价:文字价格 {{textPrice}} + 音频价格 {{audioPrice}} = {{symbol}}{{total}}": "Prix total : prix du texte {{textPrice}} + prix de l'audio {{audioPrice}} = {{symbol}}{{total}}", "总分配内存": "Mémoire totale allouée", @@ -1987,7 +1987,7 @@ "模型消耗趋势": "Tendance de la consommation des modèles", "模型版本": "Version du modèle", "模型的详细描述和基本特性": "Description détaillée et caractéristiques de base du modèle", - "模型相关设置": "Paramètres liés au modèle", + "模型相关设置": "Modèle associé", "模型社区需要大家的共同维护,如发现数据有误或想贡献新的模型数据,请访问:": "La communauté des modèles a besoin de la contribution de tous. Si vous trouvez des données incorrectes ou si vous souhaitez contribuer à de nouvelles données de modèle, veuillez visiter :", "模型管理": "Modèles", "模型组": "Groupe de modèles", @@ -2000,7 +2000,7 @@ "模型部署": "Model Deployment", "模型部署服务未启用": "Model deployment service is not enabled", "模型部署管理": "Model Deployment Management", - "模型部署设置": "Model Deployment Settings", + "模型部署设置": "Déploiement de modèles", "模型配置": "Configuration du modèle", "模型重定向": "Redirection de modèle", "模型重定向里的下列模型尚未添加到“模型”列表,调用时会因为缺少可用模型而失败:": "Les modèles suivants provenant de la redirection n'ont pas été ajoutés à la liste « Modèles », l'appel échouera faute de modèle disponible :", @@ -2871,6 +2871,7 @@ "请求参数无效": "Invalid request parameters", "请求发生错误": "Une erreur s'est produite lors de la demande", "请求发生错误: ": "Une erreur s'est produite lors de la demande : ", + "模型价格未配置": "Prix du modèle non configuré", "请求后端接口失败:": "Échec de la requête de l'interface backend : ", "请求失败": "Échec de la demande", "请求头覆盖": "Remplacement des en-têtes de demande", diff --git a/web/src/i18n/locales/ja.json b/web/src/i18n/locales/ja.json index c449048a..18262d09 100644 --- a/web/src/i18n/locales/ja.json +++ b/web/src/i18n/locales/ja.json @@ -401,7 +401,7 @@ "以下上游数据可能不可信:": "以下のアップストリームデータは信頼できない可能性があります:", "以下文件解析失败,已忽略:{{list}}": "以下のファイルは解析に失敗したため無視されました:{{list}}", "以及": "および", - "仪表盘设置": "ダッシュボード設定", + "仪表盘设置": "ダッシュボード", "价格": "料金", "价格摘要": "価格概要", "价格暂时不可用,请稍后重试": "Price temporarily unavailable, please try again later", @@ -665,7 +665,7 @@ "其他": "その他", "其他注册选项": "その他のサインアップオプション", "其他登录选项": "その他のログインオプション", - "其他设置": "その他の設定", + "其他设置": "その他", "其他详情": "Other details", "内存 阈值 (%)": "メモリしきい値 (%)", "内存使用率超过此值时拒绝请求": "メモリ使用率がこの値を超えた場合にリクエストを拒否", @@ -686,7 +686,7 @@ "分类名称": "分類名称", "分组": "グループ", "分组JSON设置": "グループJSON設定", - "分组与模型定价设置": "グループとモデルの料金設定", + "分组与模型定价设置": "グループ&モデル料金設定", "分组价格": "グループ料金", "分组倍率": "グループレート", "分组倍率设置": "グループ倍率設定", @@ -1422,7 +1422,7 @@ "思考预算占比": "思考予算の割合", "性能指标": "性能指標", "性能监控": "パフォーマンス監視", - "性能设置": "パフォーマンス設定", + "性能设置": "パフォーマンス", "总 GPU 小时": "Total GPU Hours", "总价:文字价格 {{textPrice}} + 音频价格 {{audioPrice}} = {{symbol}}{{total}}": "合計料金:テキスト料金 {{textPrice}} + オーディオ料金 {{audioPrice}} = {{symbol}}{{total}}", "总分配内存": "総割り当てメモリ", @@ -1571,7 +1571,7 @@ "支付方式名称": "決済方法名", "支付方式类型": "決済方法タイプ", "支付渠道": "決済チャネル", - "支付设置": "決済設定", + "支付设置": "決済", "支付请求失败": "決済リクエストに失敗しました", "支付金额": "決済金額", "支持 Ctrl+V 粘贴图片": "Ctrl+V で画像を貼り付け可能", @@ -1970,7 +1970,7 @@ "模型消耗趋势": "モデル消費推移", "模型版本": "モデルバージョン", "模型的详细描述和基本特性": "モデルの詳細な説明と基本的な特徴", - "模型相关设置": "モデル関連設定", + "模型相关设置": "モデル関連", "模型社区需要大家的共同维护,如发现数据有误或想贡献新的模型数据,请访问:": "モデルコミュニティは皆様の協力によって維持されています。データに誤りがある場合や、新規モデルデータをコントリビュートしたい場合は、以下にアクセスしてください:", "模型管理": "モデル管理", "模型组": "モデルグループ", @@ -1983,7 +1983,7 @@ "模型部署": "Model Deployment", "模型部署服务未启用": "Model deployment service is not enabled", "模型部署管理": "Model Deployment Management", - "模型部署设置": "Model Deployment Settings", + "模型部署设置": "モデルデプロイ", "模型配置": "モデル設定", "模型重定向": "モデルマッピング", "模型重定向里的下列模型尚未添加到“模型”列表,调用时会因为缺少可用模型而失败:": "The following models from the redirect have not been added to the “Models” list and requests will fail due to no available model:", @@ -2497,7 +2497,7 @@ "系统文档和帮助信息": "システムのドキュメントとヘルプ", "系统消息": "システムメッセージ", "系统管理功能": "システム管理機能", - "系统设置": "システム設定", + "系统设置": "システム", "系统访问令牌": "システムアクセストークン", "索引": "インデックス", "紧凑列表": "コンパクトリスト", @@ -2526,7 +2526,7 @@ "绘图": "画像生成", "绘图任务记录": "画像生成タスク履歴", "绘图日志": "画像生成履歴", - "绘图设置": "画像生成設定", + "绘图设置": "画像生成", "统一的": "統合型", "统计Tokens": "トークン統計", "统计已重置": "統計がリセットされました", @@ -2603,7 +2603,7 @@ "聊天区域": "チャットエリア", "聊天应用名称": "チャットアプリ名", "聊天应用名称已存在,请使用其他名称": "このチャットアプリ名はすでに存在します。別の名称を入力してください", - "聊天设置": "チャット設定", + "聊天设置": "チャット", "聊天配置": "チャット設定", "聊天链接配置错误,请联系管理员": "チャットURLの設定でエラーが発生しました。管理者にお問い合わせください", "联系我们": "お問い合わせ", @@ -2852,6 +2852,7 @@ "请求参数无效": "Invalid request parameters", "请求发生错误": "リクエストでエラーが発生しました", "请求发生错误: ": "リクエストでエラーが発生しました:", + "模型价格未配置": "モデル価格が未設定", "请求后端接口失败:": "バックエンドAPIリクエストに失敗しました:", "请求失败": "リクエストに失敗しました", "请求头覆盖": "リクエストヘッダーの上書き", @@ -3129,7 +3130,7 @@ "过期时间不能早于当前时间!": "有効期限は現在時刻より前に設定できません", "过期时间快捷设置": "有効期限クイック設定", "过期时间格式错误!": "有効期限のフォーマットが正しくありません", - "运营设置": "運用設定", + "运营设置": "運用", "运行中": "Running", "运行命令 (Command)": "Command", "运行时长": "Runtime Duration", @@ -3215,7 +3216,7 @@ "通道 ${name} 余额更新成功!": "チャネル「${name}」のクォータを更新しました。", "通道 ${name} 测试成功,模型 ${model} 耗时 ${time.toFixed(2)} 秒。": "チャネル「${name}」のテストに成功しました。モデル「${model}」の所要時間 ${time.toFixed(2)} 秒。", "通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。": "チャネル「${name}」のテストに成功しました。所要時間 ${time.toFixed(2)} 秒。", - "速率限制设置": "レート制限設定", + "速率限制设置": "レート制限", "逻辑": "ロジック", "邀请": "招待", "邀请人": "招待元", diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index 54f79adf..ed261834 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -408,7 +408,7 @@ "以下上游数据可能不可信:": "Следующие upstream данные могут быть недостоверными:", "以下文件解析失败,已忽略:{{list}}": "Не удалось проанализировать следующие файлы, они проигнорированы: {{list}}", "以及": "а также", - "仪表盘设置": "Настройки панели управления", + "仪表盘设置": "Панель управления", "价格": "Цена", "价格摘要": "Сводка цен", "价格暂时不可用,请稍后重试": "Price temporarily unavailable, please try again later", @@ -680,7 +680,7 @@ "其他": "Другое", "其他注册选项": "Другие варианты регистрации", "其他登录选项": "Другие варианты входа", - "其他设置": "Другие настройки", + "其他设置": "Прочее", "其他详情": "Другие детали", "内存 阈值 (%)": "Порог памяти (%)", "内存使用率超过此值时拒绝请求": "Отклонять запросы, когда использование памяти превышает это значение", @@ -701,7 +701,7 @@ "分类名称": "Название категории", "分组": "Группа", "分组JSON设置": "Group JSON Settings", - "分组与模型定价设置": "Настройки групп и ценообразования моделей", + "分组与模型定价设置": "Группы и цены моделей", "分组价格": "Цена группы", "分组倍率": "Коэффициент группы", "分组倍率设置": "Настройки коэффициента группы", @@ -1451,7 +1451,7 @@ "思考预算占比": "Доля бюджета на размышления", "性能指标": "Показатели производительности", "性能监控": "Мониторинг производительности", - "性能设置": "Настройки производительности", + "性能设置": "Производительность", "总 GPU 小时": "Total GPU Hours", "总价:文字价格 {{textPrice}} + 音频价格 {{audioPrice}} = {{symbol}}{{total}}": "Общая цена: цена текста {{textPrice}} + цена аудио {{audioPrice}} = {{symbol}}{{total}}", "总分配内存": "Общая выделенная память", @@ -1600,7 +1600,7 @@ "支付方式名称": "Название метода оплаты", "支付方式类型": "Тип метода оплаты", "支付渠道": "Платежные каналы", - "支付设置": "Настройки оплаты", + "支付设置": "Оплата", "支付请求失败": "Запрос на оплату не удался", "支付金额": "Сумма оплаты", "支持 Ctrl+V 粘贴图片": "Поддержка Ctrl+V для вставки изображения", @@ -1999,7 +1999,7 @@ "模型消耗趋势": "Тенденции потребления моделей", "模型版本": "Версия модели", "模型的详细描述和基本特性": "Подробное описание и основные характеристики модели", - "模型相关设置": "Настройки, связанные с моделью", + "模型相关设置": "Модели", "模型社区需要大家的共同维护,如发现数据有误或想贡献新的模型数据,请访问:": "Сообщество моделей требует совместного поддержания всеми. Если вы обнаружили ошибки в данных или хотите внести новые данные о моделях, посетите:", "模型管理": "Управление моделями", "模型组": "Группа моделей", @@ -2012,7 +2012,7 @@ "模型部署": "Model Deployment", "模型部署服务未启用": "Model deployment service is not enabled", "模型部署管理": "Model Deployment Management", - "模型部署设置": "Model Deployment Settings", + "模型部署设置": "Развёртывание моделей", "模型配置": "Конфигурация модели", "模型重定向": "Перенаправление модели", "模型重定向里的下列模型尚未添加到“模型”列表,调用时会因为缺少可用模型而失败:": "Следующие модели из перенаправления ещё не добавлены в список «Модели», из-за отсутствия доступных моделей вызовы завершатся ошибкой:", @@ -2530,7 +2530,7 @@ "系统文档和帮助信息": "Системная документация и справочная информация", "系统消息": "Системные сообщения", "系统管理功能": "Функции системного управления", - "系统设置": "Системные настройки", + "系统设置": "Система", "系统访问令牌": "Токен доступа к системе", "索引": "Индекс", "紧凑列表": "Компактный список", @@ -2559,7 +2559,7 @@ "绘图": "Рисование", "绘图任务记录": "Записи задач рисования", "绘图日志": "Журнал рисования", - "绘图设置": "Настройки рисования", + "绘图设置": "Рисование", "统一的": "Единый", "统计Tokens": "Статистика токенов", "统计已重置": "Статистика сброшена", @@ -2636,7 +2636,7 @@ "聊天区域": "Область чата", "聊天应用名称": "Название чат-приложения", "聊天应用名称已存在,请使用其他名称": "Название чат-приложения уже существует, используйте другое название", - "聊天设置": "Настройки чата", + "聊天设置": "Чат", "聊天配置": "Конфигурация чата", "聊天链接配置错误,请联系管理员": "Ошибка конфигурации ссылки чата, свяжитесь с администратором", "联系我们": "Свяжитесь с нами", @@ -2885,6 +2885,7 @@ "请求参数无效": "Invalid request parameters", "请求发生错误": "Произошла ошибка запроса", "请求发生错误: ": "Произошла ошибка запроса: ", + "模型价格未配置": "Цена модели не настроена", "请求后端接口失败:": "Не удалось запросить внутренний интерфейс:", "请求失败": "Запрос не удался", "请求头覆盖": "Переопределение заголовков запроса", @@ -3162,7 +3163,7 @@ "过期时间不能早于当前时间!": "Время истечения не может быть раньше текущего времени!", "过期时间快捷设置": "Быстрая настройка времени истечения", "过期时间格式错误!": "Ошибка формата времени истечения!", - "运营设置": "Операционные настройки", + "运营设置": "Операции", "运行中": "Running", "运行命令 (Command)": "Command", "运行时长": "Runtime Duration", @@ -3248,7 +3249,7 @@ "通道 ${name} 余额更新成功!": "Баланс канала ${name} успешно обновлен!", "通道 ${name} 测试成功,模型 ${model} 耗时 ${time.toFixed(2)} 秒。": "Канал ${name} успешно протестирован, модель ${model} заняла ${time.toFixed(2)} секунд.", "通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。": "Канал ${name} успешно протестирован, заняло ${time.toFixed(2)} секунд.", - "速率限制设置": "Настройки ограничения скорости", + "速率限制设置": "Ограничение скорости", "逻辑": "Логика", "邀请": "Приглашение", "邀请人": "Пригласивший", diff --git a/web/src/i18n/locales/vi.json b/web/src/i18n/locales/vi.json index 8ec57416..f9e211d2 100644 --- a/web/src/i18n/locales/vi.json +++ b/web/src/i18n/locales/vi.json @@ -402,7 +402,7 @@ "以下上游数据可能不可信:": "Dữ liệu thượng nguồn sau đây có thể không đáng tin cậy: ", "以下文件解析失败,已忽略:{{list}}": "Các tệp sau không phân tích được và đã bị bỏ qua: {{list}}", "以及": "và", - "仪表盘设置": "Cài đặt bảng điều khiển", + "仪表盘设置": "Bảng điều khiển", "价格": "Giá cả", "价格摘要": "Tóm tắt giá", "价格暂时不可用,请稍后重试": "Price temporarily unavailable, please try again later", @@ -666,7 +666,7 @@ "其他": "Khác", "其他注册选项": "Tùy chọn đăng ký khác", "其他登录选项": "Tùy chọn đăng nhập khác", - "其他设置": "Cài đặt khác", + "其他设置": "Khác", "其他详情": "Other details", "内存 阈值 (%)": "Ngưỡng bộ nhớ (%)", "内存使用率超过此值时拒绝请求": "Từ chối yêu cầu khi sử dụng bộ nhớ vượt quá giá trị này", @@ -687,7 +687,7 @@ "分类名称": "Tên danh mục", "分组": "Nhóm", "分组JSON设置": "Group JSON Settings", - "分组与模型定价设置": "Cài đặt giá nhóm và mô hình", + "分组与模型定价设置": "Nhóm & định giá mô hình", "分组价格": "Giá nhóm", "分组倍率": "Tỷ lệ nhóm", "分组倍率设置": "Cài đặt tỷ lệ nhóm", @@ -1423,7 +1423,7 @@ "思考预算占比": "Tỷ lệ ngân sách tư duy", "性能指标": "Chỉ số hiệu suất", "性能监控": "Giám sát hiệu suất", - "性能设置": "Cài đặt hiệu suất", + "性能设置": "Hiệu suất", "总 GPU 小时": "Total GPU Hours", "总价:文字价格 {{textPrice}} + 音频价格 {{audioPrice}} = {{symbol}}{{total}}": "Tổng giá: giá văn bản {{textPrice}} + giá âm thanh {{audioPrice}} = {{symbol}}{{total}}", "总分配内存": "Tổng bộ nhớ đã phân bổ", @@ -1572,7 +1572,7 @@ "支付方式名称": "Tên phương thức thanh toán", "支付方式类型": "Loại phương thức thanh toán", "支付渠道": "Kênh thanh toán", - "支付设置": "Cài đặt thanh toán", + "支付设置": "Thanh toán", "支付请求失败": "Yêu cầu thanh toán thất bại", "支付金额": "Số tiền thanh toán", "支持 Ctrl+V 粘贴图片": "Hỗ trợ Ctrl+V để dán hình ảnh", @@ -1984,7 +1984,7 @@ "模型版本": "Phiên bản mô hình", "模型状态": "Trạng thái mô hình", "模型的详细描述和基本特性": "Mô tả chi tiết và các đặc điểm cơ bản của mô hình", - "模型相关设置": "Cài đặt liên quan đến mô hình", + "模型相关设置": "Mô hình liên quan", "模型社区需要大家的共同维护,如发现数据有误或想贡献新的模型数据,请访问:": "Cộng đồng mô hình cần sự đóng góp của mọi người. Nếu bạn phát hiện dữ liệu sai hoặc muốn đóng góp dữ liệu mô hình mới, vui lòng truy cập:", "模型管理": "Quản lý mô hình", "模型类型": "Loại mô hình", @@ -2001,7 +2001,7 @@ "模型部署": "Model Deployment", "模型部署服务未启用": "Model deployment service is not enabled", "模型部署管理": "Model Deployment Management", - "模型部署设置": "Model Deployment Settings", + "模型部署设置": "Triển khai mô hình", "模型配置": "Cấu hình mô hình", "模型重定向": "Chuyển hướng mô hình", "模型重定向里的下列模型尚未添加到“模型”列表,调用时会因为缺少可用模型而失败:": "The following models from the redirect have not been added to the “Models” list and requests will fail due to no available model:", @@ -2774,7 +2774,7 @@ "系统监控": "Giám sát hệ thống", "系统管理": "Quản lý hệ thống", "系统管理功能": "Chức năng quản lý hệ thống", - "系统设置": "Cài đặt hệ thống", + "系统设置": "Hệ thống", "系统访问令牌": "Mã thông báo truy cập hệ thống", "系统负载": "Tải hệ thống", "系统通知": "Thông báo hệ thống", @@ -2827,7 +2827,7 @@ "绘图任务记录": "Hồ sơ tác vụ vẽ", "绘图日志": "Nhật ký vẽ", "绘图模型": "Mô hình vẽ", - "绘图设置": "Cài đặt vẽ", + "绘图设置": "Vẽ", "统一的": "Cổng thống nhất", "统计": "Thống kê", "统计Tokens": "Thống kê Tokens", @@ -2918,7 +2918,7 @@ "聊天区域": "Khu vực trò chuyện", "聊天应用名称": "Tên ứng dụng trò chuyện", "聊天应用名称已存在,请使用其他名称": "Tên ứng dụng trò chuyện đã tồn tại, vui lòng sử dụng tên khác", - "聊天设置": "Cài đặt trò chuyện", + "聊天设置": "Trò chuyện", "聊天配置": "Cấu hình trò chuyện", "聊天链接配置错误,请联系管理员": "Lỗi cấu hình liên kết trò chuyện, vui lòng liên hệ quản trị viên", "联系": "Liên hệ", @@ -3243,6 +3243,7 @@ "请求参数无效": "Invalid request parameters", "请求发生错误": "Đã xảy ra lỗi yêu cầu", "请求发生错误: ": "Đã xảy ra lỗi yêu cầu: ", + "模型价格未配置": "Giá mô hình chưa được cấu hình", "请求后端接口失败:": "Yêu cầu giao diện phụ trợ thất bại: ", "请求失败": "Yêu cầu thất bại", "请求失败,请重试": "Yêu cầu thất bại, vui lòng thử lại", @@ -3607,7 +3608,7 @@ "过期时间不能早于当前时间!": "Thời gian hết hạn không thể sớm hơn thời gian hiện tại!", "过期时间快捷设置": "Cài đặt nhanh thời gian hết hạn", "过期时间格式错误!": "Lỗi định dạng thời gian hết hạn!", - "运营设置": "Cài đặt vận hành", + "运营设置": "Vận hành", "运行中": "Đang chạy", "运行命令 (Command)": "Command", "运行时长": "Runtime Duration", @@ -3731,7 +3732,7 @@ "通道管理": "Quản lý kênh", "通道类型": "Loại kênh", "通道设置": "Cài đặt kênh", - "速率限制设置": "Cài đặt giới hạn tốc độ", + "速率限制设置": "Giới hạn tốc độ", "逻辑": "Logic", "邀请": "Mời", "邀请人": "Người mời", diff --git a/web/src/i18n/locales/zh-CN.json b/web/src/i18n/locales/zh-CN.json index c1142a08..d8b62bed 100644 --- a/web/src/i18n/locales/zh-CN.json +++ b/web/src/i18n/locales/zh-CN.json @@ -286,7 +286,7 @@ "以下上游数据可能不可信:": "以下上游数据可能不可信:", "以下文件解析失败,已忽略:{{list}}": "以下文件解析失败,已忽略:{{list}}", "以及": "以及", - "仪表盘设置": "仪表盘设置", + "仪表盘设置": "仪表盘", "价格": "价格", "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}", "价格:${{price}} * {{ratioType}}:{{ratio}}": "价格:${{price}} * {{ratioType}}:{{ratio}}", @@ -2153,6 +2153,7 @@ "请求参数无效": "请求参数无效", "请求发生错误": "请求发生错误", "请求发生错误: ": "请求发生错误: ", + "模型价格未配置": "模型价格未配置", "请求后端接口失败:": "请求后端接口失败:", "请求失败": "请求失败", "请求头覆盖": "请求头覆盖", diff --git a/web/src/i18n/locales/zh-TW.json b/web/src/i18n/locales/zh-TW.json index a5cea876..bddb9713 100644 --- a/web/src/i18n/locales/zh-TW.json +++ b/web/src/i18n/locales/zh-TW.json @@ -602,7 +602,7 @@ "分类名称": "分類名稱", "分组": "分組", "分组JSON设置": "分組 JSON 設定", - "分组与模型定价设置": "分組與模型定價設定", + "分组与模型定价设置": "分組與模型定價", "分组价格": "分組價格", "分组倍率": "分組倍率", "分组倍率设置": "分組倍率設定", @@ -2563,6 +2563,7 @@ "请求参数无效": "請求參數無效", "请求发生错误": "請求發生錯誤", "请求发生错误: ": "請求發生錯誤: ", + "模型价格未配置": "模型價格未配置", "请求后端接口失败:": "請求後端接口失敗:", "请求失败": "請求失敗", "请求头覆盖": "請求頭覆蓋", From 2819e3a1d10f3c556a92adb0ca3fb0746034d9d3 Mon Sep 17 00:00:00 2001 From: CaIon Date: Sun, 12 Apr 2026 17:11:20 +0800 Subject: [PATCH 03/46] fix: improve login error handling to distinguish database errors from auth failures ValidateAndFill now checks the DB query result and returns sentinel errors (ErrDatabase, ErrInvalidCredentials, ErrUserEmptyCredentials) instead of hardcoded Chinese strings. The controller maps each sentinel to the appropriate i18n message, so users see "please contact admin" on DB errors instead of a misleading "wrong password" message. Non-DB errors still return a unified vague response to avoid leaking user existence. --- controller/user.go | 13 +++++++++---- i18n/locales/en.yaml | 2 +- i18n/locales/zh-CN.yaml | 2 +- i18n/locales/zh-TW.yaml | 2 +- model/user.go | 20 ++++++++++++++++---- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/controller/user.go b/controller/user.go index 8e26320f..7921e337 100644 --- a/controller/user.go +++ b/controller/user.go @@ -52,10 +52,15 @@ func Login(c *gin.Context) { } err = user.ValidateAndFill() if err != nil { - c.JSON(http.StatusOK, gin.H{ - "message": err.Error(), - "success": false, - }) + switch { + case errors.Is(err, model.ErrDatabase): + common.SysLog(fmt.Sprintf("Login database error for user %s: %v", username, err)) + common.ApiErrorI18n(c, i18n.MsgDatabaseError) + case errors.Is(err, model.ErrUserEmptyCredentials): + common.ApiErrorI18n(c, i18n.MsgInvalidParams) + default: + common.ApiErrorI18n(c, i18n.MsgUserUsernameOrPasswordError) + } return } diff --git a/i18n/locales/en.yaml b/i18n/locales/en.yaml index 3cb9ac2c..5618a782 100644 --- a/i18n/locales/en.yaml +++ b/i18n/locales/en.yaml @@ -2,7 +2,7 @@ # Common messages common.invalid_params: "Invalid parameters" -common.database_error: "Database error, please try again later" +common.database_error: "Database error, please contact the administrator" common.retry_later: "Please try again later" common.generate_failed: "Generation failed" common.not_found: "Not found" diff --git a/i18n/locales/zh-CN.yaml b/i18n/locales/zh-CN.yaml index 20f8a77b..e4f1fac1 100644 --- a/i18n/locales/zh-CN.yaml +++ b/i18n/locales/zh-CN.yaml @@ -3,7 +3,7 @@ # Common messages common.invalid_params: "无效的参数" -common.database_error: "数据库错误,请稍后重试" +common.database_error: "数据库出错,请联系管理员" common.retry_later: "请稍后重试" common.generate_failed: "生成失败" common.not_found: "未找到" diff --git a/i18n/locales/zh-TW.yaml b/i18n/locales/zh-TW.yaml index 887322c2..854cd111 100644 --- a/i18n/locales/zh-TW.yaml +++ b/i18n/locales/zh-TW.yaml @@ -3,7 +3,7 @@ # Common messages common.invalid_params: "無效的參數" -common.database_error: "資料庫錯誤,請稍後重試" +common.database_error: "資料庫出錯,請聯繫管理員" common.retry_later: "請稍後重試" common.generate_failed: "生成失敗" common.not_found: "未找到" diff --git a/model/user.go b/model/user.go index 2bd1a8c3..9ff2a15f 100644 --- a/model/user.go +++ b/model/user.go @@ -18,6 +18,12 @@ import ( const UserNameMaxLength = 20 +var ( + ErrDatabase = errors.New("database error") + ErrInvalidCredentials = errors.New("invalid credentials") + ErrUserEmptyCredentials = errors.New("empty credentials") +) + // User if you add sensitive fields, don't forget to clean them in setupLogin function. // Otherwise, the sensitive information will be saved on local storage in plain text! type User struct { @@ -597,13 +603,19 @@ func (user *User) ValidateAndFill() (err error) { password := user.Password username := strings.TrimSpace(user.Username) if username == "" || password == "" { - return errors.New("用户名或密码为空") + return ErrUserEmptyCredentials + } + // find by username or email + err = DB.Where("username = ? OR email = ?", username, username).First(user).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return ErrInvalidCredentials + } + return fmt.Errorf("%w: %v", ErrDatabase, err) } - // find buy username or email - DB.Where("username = ? OR email = ?", username, username).First(user) okay := common.ValidatePasswordAndHash(password, user.Password) if !okay || user.Status != common.UserStatusEnabled { - return errors.New("用户名或密码错误,或用户已被封禁") + return ErrInvalidCredentials } return nil } From 59c582d13c2d146bf865638dea28d8df345cff5f Mon Sep 17 00:00:00 2001 From: CaIon Date: Sun, 12 Apr 2026 17:39:00 +0800 Subject: [PATCH 04/46] fix: harden token auth error handling to prevent info leakage - Create model/errors.go to centralize all sentinel errors - ValidateAccessToken now returns error to distinguish DB failures - ValidateUserToken uses unified ErrTokenInvalid for all auth failures (expired/exhausted/disabled/not-found) to prevent token enumeration - authHelper and TokenAuthReadOnly use i18n messages instead of hardcoded Chinese strings - All err.Error() removed from user-facing responses; DB errors logged server-side and return generic "contact admin" message (HTTP 500) - Migrate ErrRedeemFailed, ErrTwoFANotEnabled to model/errors.go --- i18n/keys.go | 12 +++++++ i18n/locales/en.yaml | 10 ++++++ i18n/locales/zh-CN.yaml | 10 ++++++ i18n/locales/zh-TW.yaml | 10 ++++++ middleware/auth.go | 77 ++++++++++++++++++++++++++++++----------- model/errors.go | 26 ++++++++++++++ model/redemption.go | 3 -- model/token.go | 27 +++++---------- model/twofa.go | 2 -- model/user.go | 22 ++++++------ 10 files changed, 144 insertions(+), 55 deletions(-) create mode 100644 model/errors.go diff --git a/i18n/keys.go b/i18n/keys.go index 67ddbb9a..5123fa9d 100644 --- a/i18n/keys.go +++ b/i18n/keys.go @@ -28,6 +28,18 @@ const ( MsgBatchTooMany = "common.batch_too_many" ) +// Auth middleware messages +const ( + MsgAuthNotLoggedIn = "auth.not_logged_in" + MsgAuthAccessTokenInvalid = "auth.access_token_invalid" + MsgAuthUserInfoInvalid = "auth.user_info_invalid" + MsgAuthUserIdNotProvided = "auth.user_id_not_provided" + MsgAuthUserIdFormatError = "auth.user_id_format_error" + MsgAuthUserIdMismatch = "auth.user_id_mismatch" + MsgAuthUserBanned = "auth.user_banned" + MsgAuthInsufficientPrivilege = "auth.insufficient_privilege" +) + // Token related messages const ( MsgTokenNameTooLong = "token.name_too_long" diff --git a/i18n/locales/en.yaml b/i18n/locales/en.yaml index 5618a782..e9fc80f2 100644 --- a/i18n/locales/en.yaml +++ b/i18n/locales/en.yaml @@ -23,6 +23,16 @@ common.already_exists: "Already exists" common.name_cannot_be_empty: "Name cannot be empty" common.batch_too_many: "Too many items in batch request, maximum is {{.Max}}" +# Auth middleware messages +auth.not_logged_in: "Unauthorized, not logged in and no access token provided" +auth.access_token_invalid: "Unauthorized, invalid access token" +auth.user_info_invalid: "Unauthorized, invalid user info" +auth.user_id_not_provided: "Unauthorized, New-Api-User header not provided" +auth.user_id_format_error: "Unauthorized, New-Api-User header format error" +auth.user_id_mismatch: "Unauthorized, New-Api-User does not match logged in user" +auth.user_banned: "User has been banned" +auth.insufficient_privilege: "Unauthorized, insufficient privileges" + # Token messages token.name_too_long: "Token name is too long" token.quota_negative: "Quota value cannot be negative" diff --git a/i18n/locales/zh-CN.yaml b/i18n/locales/zh-CN.yaml index e4f1fac1..58ba5007 100644 --- a/i18n/locales/zh-CN.yaml +++ b/i18n/locales/zh-CN.yaml @@ -24,6 +24,16 @@ common.already_exists: "已存在" common.name_cannot_be_empty: "名称不能为空" common.batch_too_many: "批量请求数量过多,最多 {{.Max}} 条" +# Auth middleware messages +auth.not_logged_in: "无权进行此操作,未登录且未提供 access token" +auth.access_token_invalid: "无权进行此操作,access token 无效" +auth.user_info_invalid: "无权进行此操作,用户信息无效" +auth.user_id_not_provided: "无权进行此操作,未提供 New-Api-User" +auth.user_id_format_error: "无权进行此操作,New-Api-User 格式错误" +auth.user_id_mismatch: "无权进行此操作,New-Api-User 与登录用户不匹配" +auth.user_banned: "用户已被封禁" +auth.insufficient_privilege: "无权进行此操作,权限不足" + # Token messages token.name_too_long: "令牌名称过长" token.quota_negative: "额度值不能为负数" diff --git a/i18n/locales/zh-TW.yaml b/i18n/locales/zh-TW.yaml index 854cd111..5a24bff7 100644 --- a/i18n/locales/zh-TW.yaml +++ b/i18n/locales/zh-TW.yaml @@ -24,6 +24,16 @@ common.already_exists: "已存在" common.name_cannot_be_empty: "名稱不能為空" common.batch_too_many: "批次請求數量過多,最多 {{.Max}} 條" +# Auth middleware messages +auth.not_logged_in: "無權進行此操作,未登入且未提供 access token" +auth.access_token_invalid: "無權進行此操作,access token 無效" +auth.user_info_invalid: "無權進行此操作,使用者資訊無效" +auth.user_id_not_provided: "無權進行此操作,未提供 New-Api-User" +auth.user_id_format_error: "無權進行此操作,New-Api-User 格式錯誤" +auth.user_id_mismatch: "無權進行此操作,New-Api-User 與登入使用者不匹配" +auth.user_banned: "使用者已被封禁" +auth.insufficient_privilege: "無權進行此操作,權限不足" + # Token messages token.name_too_long: "令牌名稱過長" token.quota_negative: "額度值不能為負數" diff --git a/middleware/auth.go b/middleware/auth.go index 342e7f49..23d933fb 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -1,6 +1,7 @@ package middleware import ( + "errors" "fmt" "net" "net/http" @@ -9,6 +10,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" + "github.com/QuantumNous/new-api/i18n" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/service" @@ -17,6 +19,7 @@ import ( "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" + "gorm.io/gorm" ) func validUserInfo(username string, role int) bool { @@ -43,17 +46,33 @@ func authHelper(c *gin.Context, minRole int) { if accessToken == "" { c.JSON(http.StatusUnauthorized, gin.H{ "success": false, - "message": "无权进行此操作,未登录且未提供 access token", + "message": common.TranslateMessage(c, i18n.MsgAuthNotLoggedIn), }) c.Abort() return } - user := model.ValidateAccessToken(accessToken) + user, authErr := model.ValidateAccessToken(accessToken) + if authErr != nil { + if errors.Is(authErr, model.ErrDatabase) { + common.SysLog("ValidateAccessToken database error: " + authErr.Error()) + c.JSON(http.StatusInternalServerError, gin.H{ + "success": false, + "message": common.TranslateMessage(c, i18n.MsgDatabaseError), + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": common.TranslateMessage(c, i18n.MsgAuthAccessTokenInvalid), + }) + } + c.Abort() + return + } if user != nil && user.Username != "" { if !validUserInfo(user.Username, user.Role) { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "无权进行此操作,用户信息无效", + "message": common.TranslateMessage(c, i18n.MsgAuthUserInfoInvalid), }) c.Abort() return @@ -67,7 +86,7 @@ func authHelper(c *gin.Context, minRole int) { } else { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "无权进行此操作,access token 无效", + "message": common.TranslateMessage(c, i18n.MsgAuthAccessTokenInvalid), }) c.Abort() return @@ -78,7 +97,7 @@ func authHelper(c *gin.Context, minRole int) { if apiUserIdStr == "" { c.JSON(http.StatusUnauthorized, gin.H{ "success": false, - "message": "无权进行此操作,未提供 New-Api-User", + "message": common.TranslateMessage(c, i18n.MsgAuthUserIdNotProvided), }) c.Abort() return @@ -87,7 +106,7 @@ func authHelper(c *gin.Context, minRole int) { if err != nil { c.JSON(http.StatusUnauthorized, gin.H{ "success": false, - "message": "无权进行此操作,New-Api-User 格式错误", + "message": common.TranslateMessage(c, i18n.MsgAuthUserIdFormatError), }) c.Abort() return @@ -96,7 +115,7 @@ func authHelper(c *gin.Context, minRole int) { if id != apiUserId { c.JSON(http.StatusUnauthorized, gin.H{ "success": false, - "message": "无权进行此操作,New-Api-User 与登录用户不匹配", + "message": common.TranslateMessage(c, i18n.MsgAuthUserIdMismatch), }) c.Abort() return @@ -104,7 +123,7 @@ func authHelper(c *gin.Context, minRole int) { if status.(int) == common.UserStatusDisabled { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "用户已被封禁", + "message": common.TranslateMessage(c, i18n.MsgAuthUserBanned), }) c.Abort() return @@ -112,7 +131,7 @@ func authHelper(c *gin.Context, minRole int) { if role.(int) < minRole { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "无权进行此操作,权限不足", + "message": common.TranslateMessage(c, i18n.MsgAuthInsufficientPrivilege), }) c.Abort() return @@ -120,7 +139,7 @@ func authHelper(c *gin.Context, minRole int) { if !validUserInfo(username.(string), role.(int)) { c.JSON(http.StatusOK, gin.H{ "success": false, - "message": "无权进行此操作,用户信息无效", + "message": common.TranslateMessage(c, i18n.MsgAuthUserInfoInvalid), }) c.Abort() return @@ -198,7 +217,7 @@ func TokenAuthReadOnly() func(c *gin.Context) { if key == "" { c.JSON(http.StatusUnauthorized, gin.H{ "success": false, - "message": "未提供 Authorization 请求头", + "message": common.TranslateMessage(c, i18n.MsgTokenNotProvided), }) c.Abort() return @@ -212,19 +231,28 @@ func TokenAuthReadOnly() func(c *gin.Context) { token, err := model.GetTokenByKey(key, false) if err != nil { - c.JSON(http.StatusUnauthorized, gin.H{ - "success": false, - "message": "无效的令牌", - }) + if errors.Is(err, gorm.ErrRecordNotFound) { + c.JSON(http.StatusUnauthorized, gin.H{ + "success": false, + "message": common.TranslateMessage(c, i18n.MsgTokenInvalid), + }) + } else { + common.SysLog("TokenAuthReadOnly GetTokenByKey database error: " + err.Error()) + c.JSON(http.StatusInternalServerError, gin.H{ + "success": false, + "message": common.TranslateMessage(c, i18n.MsgDatabaseError), + }) + } c.Abort() return } userCache, err := model.GetUserCache(token.UserId) if err != nil { + common.SysLog(fmt.Sprintf("TokenAuthReadOnly GetUserCache error for user %d: %v", token.UserId, err)) c.JSON(http.StatusInternalServerError, gin.H{ "success": false, - "message": err.Error(), + "message": common.TranslateMessage(c, i18n.MsgDatabaseError), }) c.Abort() return @@ -232,7 +260,7 @@ func TokenAuthReadOnly() func(c *gin.Context) { if userCache.Status != common.UserStatusEnabled { c.JSON(http.StatusForbidden, gin.H{ "success": false, - "message": "用户已被封禁", + "message": common.TranslateMessage(c, i18n.MsgAuthUserBanned), }) c.Abort() return @@ -309,7 +337,14 @@ func TokenAuth() func(c *gin.Context) { } } if err != nil { - abortWithOpenAiMessage(c, http.StatusUnauthorized, err.Error()) + if errors.Is(err, model.ErrDatabase) { + common.SysLog("TokenAuth ValidateUserToken database error: " + err.Error()) + abortWithOpenAiMessage(c, http.StatusInternalServerError, + common.TranslateMessage(c, i18n.MsgDatabaseError)) + } else { + abortWithOpenAiMessage(c, http.StatusUnauthorized, + common.TranslateMessage(c, i18n.MsgTokenInvalid)) + } return } @@ -331,12 +366,14 @@ func TokenAuth() func(c *gin.Context) { userCache, err := model.GetUserCache(token.UserId) if err != nil { - abortWithOpenAiMessage(c, http.StatusInternalServerError, err.Error()) + common.SysLog(fmt.Sprintf("TokenAuth GetUserCache error for user %d: %v", token.UserId, err)) + abortWithOpenAiMessage(c, http.StatusInternalServerError, + common.TranslateMessage(c, i18n.MsgDatabaseError)) return } userEnabled := userCache.Status == common.UserStatusEnabled if !userEnabled { - abortWithOpenAiMessage(c, http.StatusForbidden, "用户已被封禁") + abortWithOpenAiMessage(c, http.StatusForbidden, common.TranslateMessage(c, i18n.MsgAuthUserBanned)) return } diff --git a/model/errors.go b/model/errors.go new file mode 100644 index 00000000..a942a5bc --- /dev/null +++ b/model/errors.go @@ -0,0 +1,26 @@ +package model + +import "errors" + +// Common errors +var ( + ErrDatabase = errors.New("database error") +) + +// User auth errors +var ( + ErrInvalidCredentials = errors.New("invalid credentials") + ErrUserEmptyCredentials = errors.New("empty credentials") +) + +// Token auth errors +var ( + ErrTokenNotProvided = errors.New("token not provided") + ErrTokenInvalid = errors.New("token invalid") +) + +// Redemption errors +var ErrRedeemFailed = errors.New("redeem.failed") + +// 2FA errors +var ErrTwoFANotEnabled = errors.New("2fa not enabled") diff --git a/model/redemption.go b/model/redemption.go index 378976a3..b0ccb5df 100644 --- a/model/redemption.go +++ b/model/redemption.go @@ -11,9 +11,6 @@ import ( "gorm.io/gorm" ) -// ErrRedeemFailed is returned when redemption fails due to database error -var ErrRedeemFailed = errors.New("redeem.failed") - type Redemption struct { Id int `json:"id"` UserId int `json:"user_id"` diff --git a/model/token.go b/model/token.go index b7989ad1..8cfcd618 100644 --- a/model/token.go +++ b/model/token.go @@ -187,19 +187,14 @@ func SearchUserTokens(userId int, keyword string, token string, offset int, limi func ValidateUserToken(key string) (token *Token, err error) { if key == "" { - return nil, errors.New("未提供令牌") + return nil, ErrTokenNotProvided } token, err = GetTokenByKey(key, false) if err == nil { - if token.Status == common.TokenStatusExhausted { - keyPrefix := key[:3] - keySuffix := key[len(key)-3:] - return token, errors.New("该令牌额度已用尽 TokenStatusExhausted[sk-" + keyPrefix + "***" + keySuffix + "]") - } else if token.Status == common.TokenStatusExpired { - return token, errors.New("该令牌已过期") - } - if token.Status != common.TokenStatusEnabled { - return token, errors.New("该令牌状态不可用") + if token.Status == common.TokenStatusExhausted || + token.Status == common.TokenStatusExpired || + token.Status != common.TokenStatusEnabled { + return token, ErrTokenInvalid } if token.ExpiredTime != -1 && token.ExpiredTime < common.GetTimestamp() { if !common.RedisEnabled { @@ -209,29 +204,25 @@ func ValidateUserToken(key string) (token *Token, err error) { common.SysLog("failed to update token status" + err.Error()) } } - return token, errors.New("该令牌已过期") + return token, ErrTokenInvalid } if !token.UnlimitedQuota && token.RemainQuota <= 0 { if !common.RedisEnabled { - // in this case, we can make sure the token is exhausted token.Status = common.TokenStatusExhausted err := token.SelectUpdate() if err != nil { common.SysLog("failed to update token status" + err.Error()) } } - keyPrefix := key[:3] - keySuffix := key[len(key)-3:] - return token, fmt.Errorf("[sk-%s***%s] 该令牌额度已用尽 !token.UnlimitedQuota && token.RemainQuota = %d", keyPrefix, keySuffix, token.RemainQuota) + return token, ErrTokenInvalid } return token, nil } common.SysLog("ValidateUserToken: failed to get token: " + err.Error()) if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, errors.New("无效的令牌") - } else { - return nil, errors.New("无效的令牌,数据库查询出错,请联系管理员") + return nil, ErrTokenInvalid } + return nil, fmt.Errorf("%w: %v", ErrDatabase, err) } func GetTokenByIds(id int, userId int) (*Token, error) { diff --git a/model/twofa.go b/model/twofa.go index e63c6662..a2d0c7e1 100644 --- a/model/twofa.go +++ b/model/twofa.go @@ -10,8 +10,6 @@ import ( "gorm.io/gorm" ) -var ErrTwoFANotEnabled = errors.New("用户未启用2FA") - // TwoFA 用户2FA设置表 type TwoFA struct { Id int `json:"id" gorm:"primaryKey"` diff --git a/model/user.go b/model/user.go index 9ff2a15f..79e63e8f 100644 --- a/model/user.go +++ b/model/user.go @@ -18,12 +18,6 @@ import ( const UserNameMaxLength = 20 -var ( - ErrDatabase = errors.New("database error") - ErrInvalidCredentials = errors.New("invalid credentials") - ErrUserEmptyCredentials = errors.New("empty credentials") -) - // User if you add sensitive fields, don't forget to clean them in setupLogin function. // Otherwise, the sensitive information will be saved on local storage in plain text! type User struct { @@ -766,16 +760,20 @@ func IsAdmin(userId int) bool { // return user.Status == common.UserStatusEnabled, nil //} -func ValidateAccessToken(token string) (user *User) { +func ValidateAccessToken(token string) (*User, error) { if token == "" { - return nil + return nil, nil } token = strings.Replace(token, "Bearer ", "", 1) - user = &User{} - if DB.Where("access_token = ?", token).First(user).RowsAffected == 1 { - return user + user := &User{} + err := DB.Where("access_token = ?", token).First(user).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, fmt.Errorf("%w: %v", ErrDatabase, err) } - return nil + return user, nil } // GetUserQuota gets quota from Redis first, falls back to DB if needed From b4df9955f46d4473bffd86b0f24c3261823617a5 Mon Sep 17 00:00:00 2001 From: skynono Date: Sun, 12 Apr 2026 17:41:26 +0800 Subject: [PATCH 05/46] fix: isStream status in error logs instead of hardcoded false (#4195) --- constant/context_key.go | 1 + controller/relay.go | 2 +- relay/common/relay_info.go | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/constant/context_key.go b/constant/context_key.go index 2ba2fe27..c28ad202 100644 --- a/constant/context_key.go +++ b/constant/context_key.go @@ -65,4 +65,5 @@ const ( // ContextKeyLanguage stores the user's language preference for i18n ContextKeyLanguage ContextKey = "language" + ContextKeyIsStream ContextKey = "is_stream" ) diff --git a/controller/relay.go b/controller/relay.go index a518beed..a6f3f035 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -389,7 +389,7 @@ func processChannelError(c *gin.Context, channelError types.ChannelError, err *t startTime = time.Now() } useTimeSeconds := int(time.Since(startTime).Seconds()) - model.RecordErrorLog(c, userId, channelId, modelName, tokenName, err.MaskSensitiveErrorWithStatusCode(), tokenId, useTimeSeconds, false, userGroup, other) + model.RecordErrorLog(c, userId, channelId, modelName, tokenName, err.MaskSensitiveErrorWithStatusCode(), tokenId, useTimeSeconds, common.GetContextKeyBool(c, constant.ContextKeyIsStream), userGroup, other) } } diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index 7d4f063c..0bb75ee6 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -438,6 +438,7 @@ func genBaseRelayInfo(c *gin.Context, request dto.Request) *RelayInfo { if request != nil { isStream = request.IsStream(c) } + c.Set(string(constant.ContextKeyIsStream), isStream) // firstResponseTime = time.Now() - 1 second From b81d3427ee2b8ae3251bae5cb0635b15cc9a61c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 23:52:04 +0800 Subject: [PATCH 06/46] chore(deps): bump axios from 1.13.5 to 1.15.0 in /web (#4201) Bumps [axios](https://github.com/axios/axios) from 1.13.5 to 1.15.0. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.13.5...v1.15.0) --- updated-dependencies: - dependency-name: axios dependency-version: 1.15.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/package.json b/web/package.json index 97c7c821..83b5d230 100644 --- a/web/package.json +++ b/web/package.json @@ -10,7 +10,7 @@ "@visactor/react-vchart": "~1.8.8", "@visactor/vchart": "~1.8.8", "@visactor/vchart-semi-theme": "~1.8.8", - "axios": "1.13.5", + "axios": "1.15.0", "clsx": "^2.1.1", "dayjs": "^1.11.11", "history": "^5.3.0", From 3d0ac2d0497854c1c0542db469b3be302436aaac Mon Sep 17 00:00:00 2001 From: CaIon Date: Sun, 12 Apr 2026 23:55:07 +0800 Subject: [PATCH 07/46] chore(deps): update axios --- web/bun.lock | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/web/bun.lock b/web/bun.lock index 9a841922..da3c1e45 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -1,6 +1,5 @@ { "lockfileVersion": 1, - "configVersion": 0, "workspaces": { "": { "name": "react-template", @@ -11,7 +10,7 @@ "@visactor/react-vchart": "~1.8.8", "@visactor/vchart": "~1.8.8", "@visactor/vchart-semi-theme": "~1.8.8", - "axios": "1.13.5", + "axios": "1.15.0", "clsx": "^2.1.1", "dayjs": "^1.11.11", "history": "^5.3.0", @@ -777,7 +776,7 @@ "autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="], - "axios": ["axios@1.13.5", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q=="], + "axios": ["axios@1.15.0", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^2.1.0" } }, "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q=="], "babel-plugin-macros": ["babel-plugin-macros@3.1.0", "", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="], @@ -1657,7 +1656,7 @@ "protocol-buffers-schema": ["protocol-buffers-schema@3.6.0", "", {}, "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw=="], - "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + "proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="], "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], From 8b2216152799cbcf8ec4a1574e5c4632caf72cb4 Mon Sep 17 00:00:00 2001 From: CaIon Date: Mon, 13 Apr 2026 14:36:22 +0800 Subject: [PATCH 08/46] fix: set TopP to nil in Claude request configuration --- relay/channel/claude/relay-claude.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relay/channel/claude/relay-claude.go b/relay/channel/claude/relay-claude.go index 2982945c..3968a268 100644 --- a/relay/channel/claude/relay-claude.go +++ b/relay/channel/claude/relay-claude.go @@ -160,7 +160,7 @@ func RequestOpenAI2ClaudeMessage(c *gin.Context, textRequest dto.GeneralOpenAIRe Type: "adaptive", } claudeRequest.OutputConfig = json.RawMessage(fmt.Sprintf(`{"effort":"%s"}`, effortLevel)) - claudeRequest.TopP = common.GetPointer[float64](0) + claudeRequest.TopP = nil claudeRequest.Temperature = common.GetPointer[float64](1.0) } else if model_setting.GetClaudeSettings().ThinkingAdapterEnabled && strings.HasSuffix(textRequest.Model, "-thinking") { From c20060931bfb8a3e8c1f877e2343e67fe35d4225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=90=A7=E9=82=A6?= Date: Mon, 13 Apr 2026 14:41:40 +0800 Subject: [PATCH 09/46] fix(GroupTable): prevent Input cursor jumping to end on keystroke (#4208) Refactor updateRow/addRow/removeRow to use functional setRows(prev => ...) and ref-based onChange/duplicateNames access, making columns useMemo stable across keystrokes so Semi UI Table does not re-mount Input components. --- .../Setting/Ratio/components/GroupTable.jsx | 95 ++++++++++--------- 1 file changed, 52 insertions(+), 43 deletions(-) diff --git a/web/src/pages/Setting/Ratio/components/GroupTable.jsx b/web/src/pages/Setting/Ratio/components/GroupTable.jsx index 9cf08b25..3594d3e8 100644 --- a/web/src/pages/Setting/Ratio/components/GroupTable.jsx +++ b/web/src/pages/Setting/Ratio/components/GroupTable.jsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, useMemo } from 'react'; +import React, { useState, useCallback, useMemo, useRef } from 'react'; import { Button, Input, @@ -61,60 +61,63 @@ export function serializeGroupTable(rows) { }; } -export default function GroupTable({ - groupRatio, - userUsableGroups, - onChange, -}) { +export default function GroupTable({ groupRatio, userUsableGroups, onChange }) { const { t } = useTranslation(); const [rows, setRows] = useState(() => buildRows(groupRatio, userUsableGroups), ); - const emitChange = useCallback( - (newRows) => { - setRows(newRows); - onChange?.(serializeGroupTable(newRows)); - }, - [onChange], - ); + // Use functional setRows to keep updateRow/addRow/removeRow referentially + // stable, preventing columns useMemo from rebuilding on every keystroke + // which causes the Input cursor to jump to end (cursor reset bug). + const onChangeRef = useRef(onChange); + onChangeRef.current = onChange; + + const emitAndSet = useCallback((updater) => { + setRows((prev) => { + const next = typeof updater === 'function' ? updater(prev) : updater; + onChangeRef.current?.(serializeGroupTable(next)); + return next; + }); + }, []); const updateRow = useCallback( (id, field, value) => { - const next = rows.map((r) => - r._id === id ? { ...r, [field]: value } : r, + emitAndSet((prev) => + prev.map((r) => (r._id === id ? { ...r, [field]: value } : r)), ); - emitChange(next); }, - [rows, emitChange], + [emitAndSet], ); const addRow = useCallback(() => { - const existingNames = new Set(rows.map((r) => r.name)); - let counter = 1; - let newName = `group_${counter}`; - while (existingNames.has(newName)) { - counter++; - newName = `group_${counter}`; - } - emitChange([ - ...rows, - { - _id: uid(), - name: newName, - ratio: 1, - selectable: true, - description: '', - }, - ]); - }, [rows, emitChange]); + emitAndSet((prev) => { + const existingNames = new Set(prev.map((r) => r.name)); + let counter = 1; + let newName = `group_${counter}`; + while (existingNames.has(newName)) { + counter++; + newName = `group_${counter}`; + } + return [ + ...prev, + { + _id: uid(), + name: newName, + ratio: 1, + selectable: true, + description: '', + }, + ]; + }); + }, [emitAndSet]); const removeRow = useCallback( (id) => { - emitChange(rows.filter((r) => r._id !== id)); + emitAndSet((prev) => prev.filter((r) => r._id !== id)); }, - [rows, emitChange], + [emitAndSet], ); const groupNames = useMemo(() => rows.map((r) => r.name), [rows]); @@ -127,6 +130,11 @@ export default function GroupTable({ return new Set(Object.keys(counts).filter((k) => counts[k] > 1)); }, [groupNames]); + // Use ref so column render functions always read the latest duplicate set + // without adding duplicateNames to columns deps (which would break cursor). + const duplicateNamesRef = useRef(duplicateNames); + duplicateNamesRef.current = duplicateNames; + const columns = useMemo( () => [ { @@ -138,7 +146,9 @@ export default function GroupTable({ updateRow(record._id, 'name', v)} /> ), @@ -212,7 +222,7 @@ export default function GroupTable({ ), }, ], - [t, duplicateNames, updateRow, removeRow], + [t, updateRow, removeRow], ); return ( @@ -223,9 +233,7 @@ export default function GroupTable({ rowKey='_id' hidePagination size='small' - empty={ - {t('暂无分组,点击下方按钮添加')} - } + empty={{t('暂无分组,点击下方按钮添加')}} />
{duplicateNames.size > 0 && ( - {t('存在重复的分组名称:')}{Array.from(duplicateNames).join(', ')} + {t('存在重复的分组名称:')} + {Array.from(duplicateNames).join(', ')} )}
From 2bedd31b427a6ebd243808e29aead17038265124 Mon Sep 17 00:00:00 2001 From: MS Date: Mon, 13 Apr 2026 14:48:32 +0800 Subject: [PATCH 10/46] feat: display next quota reset time in subscription card (#4181) Show the next quota reset time for active subscriptions in the "My Subscriptions" section when a reset period is configured (next_reset_time > 0). Hidden when the subscription plan has no quota reset configured. --- web/src/components/topup/SubscriptionPlansCard.jsx | 8 ++++++++ web/src/i18n/locales/en.json | 1 + web/src/i18n/locales/fr.json | 1 + web/src/i18n/locales/ja.json | 1 + web/src/i18n/locales/ru.json | 1 + web/src/i18n/locales/vi.json | 1 + web/src/i18n/locales/zh-CN.json | 1 + web/src/i18n/locales/zh-TW.json | 1 + 8 files changed, 15 insertions(+) diff --git a/web/src/components/topup/SubscriptionPlansCard.jsx b/web/src/components/topup/SubscriptionPlansCard.jsx index a619c745..9c508283 100644 --- a/web/src/components/topup/SubscriptionPlansCard.jsx +++ b/web/src/components/topup/SubscriptionPlansCard.jsx @@ -442,6 +442,14 @@ const SubscriptionPlansCard = ({ (subscription?.end_time || 0) * 1000, ).toLocaleString()}
+ {isActive && subscription?.next_reset_time > 0 && ( +
+ {t('下一次重置')}:{' '} + {new Date( + subscription.next_reset_time * 1000, + ).toLocaleString()} +
+ )}
{t('总额度')}:{' '} {totalAmount > 0 ? ( diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index a72e2c62..eade595e 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -440,6 +440,7 @@ "余额充值管理": "Balance recharge management", "作废": "Invalidate", "作废于": "Invalidated at", + "下一次重置": "Next reset", "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "After invalidation, the subscription becomes invalid immediately. History is not affected. Continue?", "作用域": "Scope", "作用域:包含分组": "Scope: Include Group", diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json index b870cb37..fed6b191 100644 --- a/web/src/i18n/locales/fr.json +++ b/web/src/i18n/locales/fr.json @@ -435,6 +435,7 @@ "余额充值管理": "Recharge du solde", "作废": "Invalider", "作废于": "Invalidé le", + "下一次重置": "Prochaine réinitialisation", "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "Après invalidation, l'abonnement devient immédiatement invalide. L'historique n'est pas affecté. Continuer ?", "作用域": "Portée", "作用域:包含分组": "Portée : inclure le groupe", diff --git a/web/src/i18n/locales/ja.json b/web/src/i18n/locales/ja.json index 18262d09..61641dfb 100644 --- a/web/src/i18n/locales/ja.json +++ b/web/src/i18n/locales/ja.json @@ -431,6 +431,7 @@ "余额充值管理": "残高チャージ管理", "作废": "無効化", "作废于": "無効化日", + "下一次重置": "次回リセット", "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "無効化するとこのサブスクリプションは直ちに失効します。履歴には影響しません。続行しますか?", "作用域": "スコープ", "作用域:包含分组": "スコープ:グループを含む", diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index ed261834..0986d336 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -438,6 +438,7 @@ "余额充值管理": "Управление пополнением баланса", "作废": "Аннулировать", "作废于": "Аннулировано", + "下一次重置": "Следующий сброс", "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "После аннулирования подписка сразу станет недействительной. История не изменится. Продолжить?", "作用域": "Область действия", "作用域:包含分组": "Область действия: включить группу", diff --git a/web/src/i18n/locales/vi.json b/web/src/i18n/locales/vi.json index f9e211d2..266fab5f 100644 --- a/web/src/i18n/locales/vi.json +++ b/web/src/i18n/locales/vi.json @@ -432,6 +432,7 @@ "余额充值管理": "Quản lý nạp tiền số dư", "作废": "Vô hiệu", "作废于": "Vô hiệu vào", + "下一次重置": "Đặt lại tiếp theo", "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "Sau khi vô hiệu, đăng ký sẽ mất hiệu lực ngay. Lịch sử không bị ảnh hưởng. Tiếp tục?", "作用域": "Phạm vi", "作用域:包含分组": "Phạm vi: Bao gồm nhóm", diff --git a/web/src/i18n/locales/zh-CN.json b/web/src/i18n/locales/zh-CN.json index d8b62bed..80f1a962 100644 --- a/web/src/i18n/locales/zh-CN.json +++ b/web/src/i18n/locales/zh-CN.json @@ -2797,6 +2797,7 @@ "至": "至", "过期于": "过期于", "作废于": "作废于", + "下一次重置": "下一次重置", "购买套餐后即可享受模型权益": "购买套餐后即可享受模型权益", "限购": "限购", "推荐": "推荐", diff --git a/web/src/i18n/locales/zh-TW.json b/web/src/i18n/locales/zh-TW.json index bddb9713..aaf6d33c 100644 --- a/web/src/i18n/locales/zh-TW.json +++ b/web/src/i18n/locales/zh-TW.json @@ -379,6 +379,7 @@ "余额充值管理": "餘額儲值管理", "作废": "作廢", "作废于": "作廢於", + "下一次重置": "下一次重置", "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "作廢後該訂閱將立即失效,歷史記錄不受影響。是否繼續?", "你似乎并没有修改什么": "你似乎並沒有修改什麼", "你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "你可以在「自訂模型名稱」處手動添加它們,然後點擊填入後再提交,或者直接使用下方操作自動處理。", From 7cfaf6c3355013c0e4ad1d3ad171a0b756891dfb Mon Sep 17 00:00:00 2001 From: CaIon Date: Mon, 13 Apr 2026 15:12:12 +0800 Subject: [PATCH 11/46] feat: enhance dashboard charts with improved dimension handling and ranking logic --- .../hooks/dashboard/useDashboardCharts.jsx | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/web/src/hooks/dashboard/useDashboardCharts.jsx b/web/src/hooks/dashboard/useDashboardCharts.jsx index 0d138350..ef0d47b0 100644 --- a/web/src/hooks/dashboard/useDashboardCharts.jsx +++ b/web/src/hooks/dashboard/useDashboardCharts.jsx @@ -214,6 +214,29 @@ export const useDashboardCharts = ( }, ], }, + dimension: { + content: [ + { + key: (datum) => datum['Model'], + value: (datum) => datum['Count'] || 0, + }, + ], + updateContent: (array) => { + array.sort((a, b) => b.value - a.value); + let sum = 0; + for (let i = 0; i < array.length; i++) { + let value = parseFloat(array[i].value); + if (isNaN(value)) value = 0; + sum += value; + array[i].value = renderNumber(value); + } + array.unshift({ + key: t('总计'), + value: renderNumber(sum), + }); + return array; + }, + }, }, color: { specified: modelColorMap, @@ -335,6 +358,27 @@ export const useDashboardCharts = ( value: (datum) => renderQuota(datum['rawQuota'] || 0, 4), }], }, + dimension: { + content: [{ + key: (datum) => datum['User'], + value: (datum) => datum['rawQuota'] || 0, + }], + updateContent: (array) => { + array.sort((a, b) => b.value - a.value); + let sum = 0; + for (let i = 0; i < array.length; i++) { + let value = parseFloat(array[i].value); + if (isNaN(value)) value = 0; + sum += value; + array[i].value = renderQuota(value, 4); + } + array.unshift({ + key: t('总计'), + value: renderQuota(sum, 4), + }); + return array; + }, + }, }, color: { type: 'ordinal', range: USER_COLORS }, }); @@ -463,13 +507,25 @@ export const useDashboardCharts = ( modelLineData.sort((a, b) => a.Time.localeCompare(b.Time)); // ===== 模型调用次数排行柱状图 ===== - const rankData = Array.from(modelTotals) + const MAX_RANK_MODELS = 20; + const allRankData = Array.from(modelTotals) .map(([model, count]) => ({ Model: model, Count: count, })) .sort((a, b) => b.Count - a.Count); + let rankData; + if (allRankData.length > MAX_RANK_MODELS) { + const topModels = allRankData.slice(0, MAX_RANK_MODELS); + const otherCount = allRankData + .slice(MAX_RANK_MODELS) + .reduce((sum, item) => sum + item.Count, 0); + rankData = [...topModels, { Model: t('其他'), Count: otherCount }]; + } else { + rankData = allRankData; + } + updateChartSpec( setSpecModelLine, modelLineData, From 3ab65a822174d1c984c61fa1a6f45feaee80ae56 Mon Sep 17 00:00:00 2001 From: woan1136 Date: Mon, 13 Apr 2026 15:23:38 +0800 Subject: [PATCH 12/46] fix: add Azure channel support for /v1/responses/compact URL routing (#4149) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Azure channel's GetRequestURL method only handled RelayModeResponses but missed RelayModeResponsesCompact. This caused compact requests to fall through to the generic deployments URL pattern, producing an incorrect path that Azure returns 404 for. This fix extends the existing responses API special handling to also cover the compact mode, appending /compact to the subUrl when the relay mode is ResponsesCompact. Affected URLs (before → after): - Normal Azure: /openai/deployments/{model}/responses/compact → /openai/v1/responses/compact - cognitiveservices: same pattern → /openai/responses/compact - Custom AzureResponsesVersion: properly respected for compact too Co-authored-by: 彭俊杰 --- relay/channel/openai/adaptor.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/relay/channel/openai/adaptor.go b/relay/channel/openai/adaptor.go index 2b7f71d6..56a58f28 100644 --- a/relay/channel/openai/adaptor.go +++ b/relay/channel/openai/adaptor.go @@ -136,8 +136,8 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) { task = "chat/completions" + task } - // 特殊处理 responses API - if info.RelayMode == relayconstant.RelayModeResponses { + // 特殊处理 responses API(包含 compact) + if info.RelayMode == relayconstant.RelayModeResponses || info.RelayMode == relayconstant.RelayModeResponsesCompact { responsesApiVersion := "preview" subUrl := "/openai/v1/responses" @@ -150,6 +150,11 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) { responsesApiVersion = info.ChannelOtherSettings.AzureResponsesVersion } + // compact 模式追加 /compact + if info.RelayMode == relayconstant.RelayModeResponsesCompact { + subUrl = subUrl + "/compact" + } + requestURL = fmt.Sprintf("%s?api-version=%s", subUrl, responsesApiVersion) return relaycommon.GetFullRequestURL(info.ChannelBaseUrl, requestURL, info.ChannelType), nil } From d15e14b117584294f94f0a9ee19f0c3d24c35db1 Mon Sep 17 00:00:00 2001 From: feitianbubu Date: Mon, 13 Apr 2026 15:47:15 +0800 Subject: [PATCH 13/46] feat: include admin username in quota adjustment logs --- controller/user.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/controller/user.go b/controller/user.go index 7921e337..a12d5c66 100644 --- a/controller/user.go +++ b/controller/user.go @@ -912,6 +912,7 @@ func ManageUser(c *gin.Context) { } user.Role = common.RoleCommonUser case "add_quota": + adminName := c.GetString("username") switch req.Mode { case "add": if req.Value <= 0 { @@ -923,7 +924,7 @@ func ManageUser(c *gin.Context) { return } model.RecordLog(user.Id, model.LogTypeManage, - fmt.Sprintf("管理员增加用户额度 %s", logger.LogQuota(req.Value))) + fmt.Sprintf("管理员(%s)增加用户额度 %s", adminName, logger.LogQuota(req.Value))) case "subtract": if req.Value <= 0 { common.ApiErrorI18n(c, i18n.MsgUserQuotaChangeZero) @@ -934,7 +935,7 @@ func ManageUser(c *gin.Context) { return } model.RecordLog(user.Id, model.LogTypeManage, - fmt.Sprintf("管理员减少用户额度 %s", logger.LogQuota(req.Value))) + fmt.Sprintf("管理员(%s)减少用户额度 %s", adminName, logger.LogQuota(req.Value))) case "override": oldQuota := user.Quota if err := model.DB.Model(&model.User{}).Where("id = ?", user.Id).Update("quota", req.Value).Error; err != nil { @@ -942,7 +943,7 @@ func ManageUser(c *gin.Context) { return } model.RecordLog(user.Id, model.LogTypeManage, - fmt.Sprintf("管理员覆盖用户额度从 %s 为 %s", logger.LogQuota(oldQuota), logger.LogQuota(req.Value))) + fmt.Sprintf("管理员(%s)覆盖用户额度从 %s 为 %s", adminName, logger.LogQuota(oldQuota), logger.LogQuota(req.Value))) default: common.ApiErrorI18n(c, i18n.MsgInvalidParams) return From 8c8661d0d77c859e2853b886fd6c5d7649eeb401 Mon Sep 17 00:00:00 2001 From: CaIon Date: Mon, 13 Apr 2026 16:39:12 +0800 Subject: [PATCH 14/46] refactor: clean up unused imports and commented-out code in channel.go --- service/channel.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/service/channel.go b/service/channel.go index 96bc1efe..f93b29a2 100644 --- a/service/channel.go +++ b/service/channel.go @@ -2,11 +2,9 @@ package service import ( "fmt" - "net/http" "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/setting/operation_setting" @@ -63,12 +61,12 @@ func ShouldDisableChannel(channelType int, err *types.NewAPIError) bool { //if err.StatusCode == http.StatusUnauthorized { // return true //} - if err.StatusCode == http.StatusForbidden { - switch channelType { - case constant.ChannelTypeGemini: - return true - } - } + //if err.StatusCode == http.StatusForbidden { + // switch channelType { + // case constant.ChannelTypeGemini: + // return true + // } + //} oaiErr := err.ToOpenAIError() switch oaiErr.Code { case "invalid_api_key": From ee7cedd5776cafbd9dd39fdee88cee51614b293d Mon Sep 17 00:00:00 2001 From: power <244875489@qq.com> Date: Wed, 15 Apr 2026 20:51:10 +0800 Subject: [PATCH 15/46] fix: use json.RawMessage for Instructions field in OpenAIResponsesResponse (#4260) The Instructions field in OpenAIResponsesResponse was defined as string, but upstream providers may return null or non-string JSON values for this field. This causes json.Unmarshal to fail, resulting in HTTP 500 on /v1/responses endpoint. Other fields in the same struct (Status, ToolChoice, Truncation, etc.) already use json.RawMessage. The request-side DTO (openai_request.go) also defines Instructions as json.RawMessage. This fix aligns the response-side with both patterns. Co-authored-by: 40005415C\Administrator Co-authored-by: Claude Opus 4.6 --- dto/openai_response.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dto/openai_response.go b/dto/openai_response.go index b5bc7b0d..c3673bb4 100644 --- a/dto/openai_response.go +++ b/dto/openai_response.go @@ -272,7 +272,7 @@ type OpenAIResponsesResponse struct { Status json.RawMessage `json:"status"` Error any `json:"error,omitempty"` IncompleteDetails *IncompleteDetails `json:"incomplete_details,omitempty"` - Instructions string `json:"instructions"` + Instructions json.RawMessage `json:"instructions"` MaxOutputTokens int `json:"max_output_tokens"` Model string `json:"model"` Output []ResponsesOutput `json:"output"` From d0c2d2c6fbb6beb33ac8f5d88b5cbb165663c036 Mon Sep 17 00:00:00 2001 From: wans10 <51012039+wans10@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:53:58 +0800 Subject: [PATCH 16/46] =?UTF-8?q?fix(channel):=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=A4=9A=E5=AF=86=E9=92=A5=E7=AE=A1=E7=90=86=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=E7=B4=A2=E5=BC=95=E6=98=BE=E7=A4=BA=EF=BC=8C=E5=B0=86=E7=B4=A2?= =?UTF-8?q?=E5=BC=95=E5=80=BC=E8=B0=83=E6=95=B4=E4=B8=BA=E4=BB=8E1?= =?UTF-8?q?=E5=BC=80=E5=A7=8B=20(#4231)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/table/channels/modals/MultiKeyManageModal.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/table/channels/modals/MultiKeyManageModal.jsx b/web/src/components/table/channels/modals/MultiKeyManageModal.jsx index 5e496131..fa7870da 100644 --- a/web/src/components/table/channels/modals/MultiKeyManageModal.jsx +++ b/web/src/components/table/channels/modals/MultiKeyManageModal.jsx @@ -360,7 +360,7 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => { { title: t('索引'), dataIndex: 'index', - render: (text) => `#${text}`, + render: (text) => `#${Number(text) + 1}`, }, // { // title: t('密钥预览'), From f7adf02eb4b4afcae3af7d581e4b88fa9e32420c Mon Sep 17 00:00:00 2001 From: Seefs <40468931+seefs001@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:55:01 +0800 Subject: [PATCH 17/46] feat(claude): add cache_control and speed passthrough controls (#4247) --- docs/openapi/relay.json | 54 ++++++++++++++++++- dto/channel_settings.go | 1 + dto/claude.go | 12 +++-- relay/common/override.go | 1 + relay/common/override_test.go | 20 ++++++- relay/common/relay_info.go | 8 +++ .../channels/modals/EditChannelModal.jsx | 7 +++ web/src/i18n/locales/en.json | 2 + web/src/i18n/locales/fr.json | 2 + web/src/i18n/locales/ja.json | 2 + web/src/i18n/locales/ru.json | 2 + web/src/i18n/locales/vi.json | 2 + web/src/i18n/locales/zh-CN.json | 2 + web/src/i18n/locales/zh-TW.json | 4 ++ 14 files changed, 113 insertions(+), 6 deletions(-) diff --git a/docs/openapi/relay.json b/docs/openapi/relay.json index b6dfbd31..62a0b65b 100644 --- a/docs/openapi/relay.json +++ b/docs/openapi/relay.json @@ -3281,6 +3281,13 @@ } ] }, + "cache_control": { + "type": "object", + "properties": {} + }, + "inference_geo": { + "type": "string" + }, "max_tokens": { "type": "integer", "minimum": 1 @@ -3333,7 +3340,8 @@ "enum": [ "auto", "any", - "tool" + "tool", + "none" ] }, "name": { @@ -3358,6 +3366,36 @@ } } }, + "context_management": { + "type": "object", + "properties": {} + }, + "output_config": { + "type": "object", + "properties": {} + }, + "output_format": { + "type": "object", + "properties": {} + }, + "container": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": {} + } + ] + }, + "mcp_servers": { + "type": "array", + "items": { + "type": "object", + "properties": {} + } + }, "metadata": { "type": "object", "properties": { @@ -3365,6 +3403,20 @@ "type": "string" } } + }, + "speed": { + "type": "string", + "enum": [ + "standard", + "fast" + ] + }, + "service_tier": { + "type": "string", + "enum": [ + "auto", + "standard_only" + ] } } }, diff --git a/dto/channel_settings.go b/dto/channel_settings.go index 8d7466d2..b6a1ab9f 100644 --- a/dto/channel_settings.go +++ b/dto/channel_settings.go @@ -30,6 +30,7 @@ type ChannelOtherSettings struct { ClaudeBetaQuery bool `json:"claude_beta_query,omitempty"` // Claude 渠道是否强制追加 ?beta=true AllowServiceTier bool `json:"allow_service_tier,omitempty"` // 是否允许 service_tier 透传(默认过滤以避免额外计费) AllowInferenceGeo bool `json:"allow_inference_geo,omitempty"` // 是否允许 inference_geo 透传(仅 Claude,默认过滤以满足数据驻留合规 + AllowSpeed bool `json:"allow_speed,omitempty"` // 是否允许 speed 透传(仅 Claude,默认过滤以避免意外切换推理速度模式) AllowSafetyIdentifier bool `json:"allow_safety_identifier,omitempty"` // 是否允许 safety_identifier 透传(默认过滤以保护用户隐私) DisableStore bool `json:"disable_store,omitempty"` // 是否禁用 store 透传(默认允许透传,禁用后可能导致 Codex 无法使用) AllowIncludeObfuscation bool `json:"allow_include_obfuscation,omitempty"` // 是否允许 stream_options.include_obfuscation 透传(默认过滤以避免关闭流混淆保护) diff --git a/dto/claude.go b/dto/claude.go index d292f97e..b35d6565 100644 --- a/dto/claude.go +++ b/dto/claude.go @@ -204,10 +204,11 @@ type ClaudeToolChoice struct { } type ClaudeRequest struct { - Model string `json:"model"` - Prompt string `json:"prompt,omitempty"` - System any `json:"system,omitempty"` - Messages []ClaudeMessage `json:"messages,omitempty"` + Model string `json:"model"` + Prompt string `json:"prompt,omitempty"` + System any `json:"system,omitempty"` + Messages []ClaudeMessage `json:"messages,omitempty"` + CacheControl json.RawMessage `json:"cache_control,omitempty"` // InferenceGeo controls Claude data residency region. // This field is filtered by default and can be enabled via channel setting allow_inference_geo. InferenceGeo string `json:"inference_geo,omitempty"` @@ -227,6 +228,9 @@ type ClaudeRequest struct { Thinking *Thinking `json:"thinking,omitempty"` McpServers json.RawMessage `json:"mcp_servers,omitempty"` Metadata json.RawMessage `json:"metadata,omitempty"` + // Speed specifies the Claude inference speed mode. + // This field is filtered by default and can be enabled via channel setting allow_speed. + Speed json.RawMessage `json:"speed,omitempty"` // ServiceTier specifies upstream service level and may affect billing. // This field is filtered by default and can be enabled via channel setting allow_service_tier. ServiceTier string `json:"service_tier,omitempty"` diff --git a/relay/common/override.go b/relay/common/override.go index af0b4361..1a28303f 100644 --- a/relay/common/override.go +++ b/relay/common/override.go @@ -32,6 +32,7 @@ var paramOverrideKeyAuditPaths = map[string]struct{}{ "upstream_model": {}, "service_tier": {}, "inference_geo": {}, + "speed": {}, } type paramOverrideAuditRecorder struct { diff --git a/relay/common/override_test.go b/relay/common/override_test.go index 1a7793ba..6e35cf73 100644 --- a/relay/common/override_test.go +++ b/relay/common/override_test.go @@ -2038,6 +2038,8 @@ func TestRemoveDisabledFieldsDefaultFiltering(t *testing.T) { input := `{ "service_tier":"flex", "inference_geo":"eu", + "speed":"fast", + "cache_control":{"type":"ephemeral"}, "safety_identifier":"user-123", "store":true, "stream_options":{"include_obfuscation":false} @@ -2048,7 +2050,7 @@ func TestRemoveDisabledFieldsDefaultFiltering(t *testing.T) { if err != nil { t.Fatalf("RemoveDisabledFields returned error: %v", err) } - assertJSONEqual(t, `{"store":true}`, string(out)) + assertJSONEqual(t, `{"cache_control":{"type":"ephemeral"},"store":true}`, string(out)) } func TestRemoveDisabledFieldsAllowInferenceGeo(t *testing.T) { @@ -2067,6 +2069,22 @@ func TestRemoveDisabledFieldsAllowInferenceGeo(t *testing.T) { assertJSONEqual(t, `{"inference_geo":"eu","store":true}`, string(out)) } +func TestRemoveDisabledFieldsAllowSpeed(t *testing.T) { + input := `{ + "speed":"fast", + "store":true + }` + settings := dto.ChannelOtherSettings{ + AllowSpeed: true, + } + + out, err := RemoveDisabledFields([]byte(input), settings, false) + if err != nil { + t.Fatalf("RemoveDisabledFields returned error: %v", err) + } + assertJSONEqual(t, `{"speed":"fast","store":true}`, string(out)) +} + func TestApplyParamOverrideWithRelayInfoRecordsOperationAuditInDebugMode(t *testing.T) { originalDebugEnabled := common2.DebugEnabled common2.DebugEnabled = true diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index 0bb75ee6..2e157fc8 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -771,6 +771,7 @@ func FailTaskInfo(reason string) *TaskInfo { // RemoveDisabledFields 从请求 JSON 数据中移除渠道设置中禁用的字段 // service_tier: 服务层级字段,可能导致额外计费(OpenAI、Claude、Responses API 支持) // inference_geo: Claude 数据驻留推理区域字段(仅 Claude 支持,默认过滤) +// speed: Claude 推理速度模式字段(仅 Claude 支持,默认过滤) // store: 数据存储授权字段,涉及用户隐私(仅 OpenAI、Responses API 支持,默认允许透传,禁用后可能导致 Codex 无法使用) // safety_identifier: 安全标识符,用于向 OpenAI 报告违规用户(仅 OpenAI 支持,涉及用户隐私) // stream_options.include_obfuscation: 响应流混淆控制字段(仅 OpenAI Responses API 支持) @@ -799,6 +800,13 @@ func RemoveDisabledFields(jsonData []byte, channelOtherSettings dto.ChannelOther } } + // 默认移除 speed,除非明确允许(避免意外切换 Claude 推理速度模式) + if !channelOtherSettings.AllowSpeed { + if _, exists := data["speed"]; exists { + delete(data, "speed") + } + } + // 默认允许 store 透传,除非明确禁用(禁用可能影响 Codex 使用) if channelOtherSettings.DisableStore { if _, exists := data["store"]; exists { diff --git a/web/src/components/table/channels/modals/EditChannelModal.jsx b/web/src/components/table/channels/modals/EditChannelModal.jsx index 899e2905..f0395d2f 100644 --- a/web/src/components/table/channels/modals/EditChannelModal.jsx +++ b/web/src/components/table/channels/modals/EditChannelModal.jsx @@ -208,6 +208,7 @@ const EditChannelModal = (props) => { allow_safety_identifier: false, allow_include_obfuscation: false, allow_inference_geo: false, + allow_speed: false, claude_beta_query: false, upstream_model_update_check_enabled: false, upstream_model_update_auto_sync_enabled: false, @@ -890,6 +891,7 @@ const EditChannelModal = (props) => { parsedSettings.allow_include_obfuscation || false; data.allow_inference_geo = parsedSettings.allow_inference_geo || false; + data.allow_speed = parsedSettings.allow_speed || false; data.claude_beta_query = parsedSettings.claude_beta_query || false; data.upstream_model_update_check_enabled = parsedSettings.upstream_model_update_check_enabled === true; @@ -919,6 +921,7 @@ const EditChannelModal = (props) => { data.allow_safety_identifier = false; data.allow_include_obfuscation = false; data.allow_inference_geo = false; + data.allow_speed = false; data.claude_beta_query = false; data.upstream_model_update_check_enabled = false; data.upstream_model_update_auto_sync_enabled = false; @@ -936,6 +939,7 @@ const EditChannelModal = (props) => { data.allow_safety_identifier = false; data.allow_include_obfuscation = false; data.allow_inference_geo = false; + data.allow_speed = false; data.claude_beta_query = false; data.upstream_model_update_check_enabled = false; data.upstream_model_update_auto_sync_enabled = false; @@ -1776,6 +1780,7 @@ const EditChannelModal = (props) => { } if (localInputs.type === 14) { settings.allow_inference_geo = localInputs.allow_inference_geo === true; + settings.allow_speed = localInputs.allow_speed === true; settings.claude_beta_query = localInputs.claude_beta_query === true; } } @@ -1823,6 +1828,7 @@ const EditChannelModal = (props) => { delete localInputs.allow_safety_identifier; delete localInputs.allow_include_obfuscation; delete localInputs.allow_inference_geo; + delete localInputs.allow_speed; delete localInputs.claude_beta_query; delete localInputs.upstream_model_update_check_enabled; delete localInputs.upstream_model_update_auto_sync_enabled; @@ -2480,6 +2486,7 @@ const EditChannelModal = (props) => {
handleChannelOtherSettingsChange('allow_service_tier', value)} extraText={t('service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用')} /> handleChannelOtherSettingsChange('allow_inference_geo', value)} extraText={t('inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息')} /> + handleChannelOtherSettingsChange('allow_speed', value)} extraText={t('speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式')} /> )}
diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index eade595e..e273c38b 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -250,6 +250,7 @@ "price_xxx 的商品价格 ID,新建产品后可获得": "Product price ID for price_xxx, available after creating new product", "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "The safety_identifier field helps OpenAI identify application users who may violate usage policies. Disabled by default to protect user privacy", "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "The service_tier field is used to specify service level. Allowing pass-through may result in higher billing than expected. Disabled by default to avoid extra charges", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "The speed field controls Claude inference speed mode. Disabled by default to avoid unintentionally switching to fast mode", "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "Stripe key for sk_xxx or rk_xxx, sensitive information not displayed", "standard 已被移除,vip 用户看不到": "standard has been removed, vip users cannot see it", "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "The store field authorizes OpenAI to store request data for product evaluation and optimization. Disabled by default. Enabling may cause Codex to malfunction", @@ -581,6 +582,7 @@ "允许 inference_geo 透传": "Allow inference_geo Pass-through", "允许 safety_identifier 透传": "Allow safety_identifier Pass-through", "允许 service_tier 透传": "Allow service_tier Pass-through", + "允许 speed 透传": "Allow speed Pass-through", "允许 stream_options.include_obfuscation 透传": "Allow stream_options.include_obfuscation Pass-through", "允许不安全的 Origin(HTTP)": "Allow insecure Origin (HTTP)", "允许回调(会泄露服务器 IP 地址)": "Allow callback (will leak server IP address)", diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json index fed6b191..61c6c370 100644 --- a/web/src/i18n/locales/fr.json +++ b/web/src/i18n/locales/fr.json @@ -246,6 +246,7 @@ "price_xxx 的商品价格 ID,新建产品后可获得": "ID de prix du produit price_xxx, peut être obtenu après la création d'un nouveau produit", "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "Le champ safety_identifier aide OpenAI à identifier les utilisateurs d'applications susceptibles de violer les politiques d'utilisation. Désactivé par défaut pour protéger la confidentialité des utilisateurs", "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "Le champ service_tier est utilisé pour spécifier le niveau de service. Permettre le passage peut entraîner une facturation plus élevée que prévu. Désactivé par défaut pour éviter des frais supplémentaires", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "Le champ speed contrôle le mode de vitesse d'inférence de Claude. Désactivé par défaut pour éviter un passage involontaire au mode fast", "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "Clé secrète Stripe sk_xxx ou rk_xxx, les informations sensibles ne sont pas affichées", "standard 已被移除,vip 用户看不到": "standard has been removed, vip users cannot see it", "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "Le champ store autorise OpenAI à stocker les données de requête pour l'évaluation et l'optimisation du produit. Désactivé par défaut. L'activation peut causer un dysfonctionnement de Codex", @@ -574,6 +575,7 @@ "允许 inference_geo 透传": "Autoriser la transmission de inference_geo", "允许 safety_identifier 透传": "Autoriser le passage de safety_identifier", "允许 service_tier 透传": "Autoriser le passage de service_tier", + "允许 speed 透传": "Autoriser la transmission de speed", "允许 stream_options.include_obfuscation 透传": "Autoriser la transmission de stream_options.include_obfuscation", "允许不安全的 Origin(HTTP)": "Autoriser une origine non sécurisée (HTTP)", "允许回调(会泄露服务器 IP 地址)": "Autoriser le rappel (divulguera l'adresse IP du serveur)", diff --git a/web/src/i18n/locales/ja.json b/web/src/i18n/locales/ja.json index 61641dfb..e0dc5a72 100644 --- a/web/src/i18n/locales/ja.json +++ b/web/src/i18n/locales/ja.json @@ -242,6 +242,7 @@ "price_xxx 的商品价格 ID,新建产品后可获得": "price_xxx の料金ID。新規製品の作成後に取得できます", "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "safety_identifierフィールドは、OpenAIが利用ポリシーに違反する可能性のあるアプリユーザーを特定するために使用されます。ユーザーのプライバシーを保護するため、デフォルトでは無効です", "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "service_tierフィールドはサービス階層の指定に使用されます。パススルーを許可すると実際の課金額が想定を上回る場合があるため、追加料金を避けるためにデフォルトでは無効になっています", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "speed フィールドは Claude の推論速度モードを制御します。意図せず fast モードへ切り替わるのを避けるため、デフォルトで無効です", "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "sk_xxx または rk_xxx のStripe APIキー。機密情報は表示されません", "standard 已被移除,vip 用户看不到": "standard は削除され、vipユーザーには表示されません", "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "storeフィールドは、製品の評価と最適化のためにOpenAIがリクエストデータを保存することを許可します。デフォルトでは無効です。有効にすると、Codexが正常に利用できなくなる場合があります", @@ -570,6 +571,7 @@ "允许 inference_geo 透传": "inference_geoパススルーを許可", "允许 safety_identifier 透传": "safety_identifierのパススルーを許可する", "允许 service_tier 透传": "service_tierのパススルーを許可する", + "允许 speed 透传": "speed パススルーを許可", "允许 stream_options.include_obfuscation 透传": "stream_options.include_obfuscationパススルーを許可", "允许不安全的 Origin(HTTP)": "安全でないオリジン(HTTP)を許可する", "允许回调(会泄露服务器 IP 地址)": "コールバックを許可する(サーバーIPアドレスが漏洩します)", diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index 0986d336..a3503904 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -249,6 +249,7 @@ "price_xxx 的商品价格 ID,新建产品后可获得": "ID цены товара price_xxx, можно получить после создания нового продукта", "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "Поле safety_identifier помогает OpenAI идентифицировать пользователей приложений, которые могут нарушать политику использования. По умолчанию отключено для защиты конфиденциальности пользователей", "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "Поле service_tier используется для указания уровня сервиса, позволяет передавать параметры, которые могут привести к фактической оплате выше ожидаемой. По умолчанию отключено для избежания дополнительных расходов", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "Поле speed управляет режимом скорости инференса Claude. По умолчанию отключено, чтобы избежать непреднамеренного переключения в режим fast", "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "Ключ Stripe sk_xxx или rk_xxx, конфиденциальная информация не отображается", "standard 已被移除,vip 用户看不到": "standard has been removed, vip users cannot see it", "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "Поле store используется для авторизации OpenAI хранить данные запросов для оценки и оптимизации продукта. По умолчанию отключено, после включения может привести к неработоспособности Codex", @@ -577,6 +578,7 @@ "允许 inference_geo 透传": "Разрешить передачу inference_geo", "允许 safety_identifier 透传": "Разрешить сквозную передачу safety_identifier", "允许 service_tier 透传": "Разрешить сквозную передачу service_tier", + "允许 speed 透传": "Разрешить передачу speed", "允许 stream_options.include_obfuscation 透传": "Разрешить передачу stream_options.include_obfuscation", "允许不安全的 Origin(HTTP)": "Разрешить небезопасные Origin (HTTP)", "允许回调(会泄露服务器 IP 地址)": "Разрешить обратные вызовы (может раскрыть IP-адрес сервера)", diff --git a/web/src/i18n/locales/vi.json b/web/src/i18n/locales/vi.json index 266fab5f..dc3de89f 100644 --- a/web/src/i18n/locales/vi.json +++ b/web/src/i18n/locales/vi.json @@ -243,6 +243,7 @@ "price_xxx 的商品价格 ID,新建产品后可获得": "ID giá sản phẩm cho price_xxx, có sẵn sau khi tạo sản phẩm mới", "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "Trường safety_identifier giúp OpenAI xác định người dùng ứng dụng có thể vi phạm chính sách sử dụng. Tắt theo mặc định để bảo vệ quyền riêng tư của người dùng", "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "Trường service_tier được sử dụng để chỉ định cấp độ dịch vụ. Cho phép truyền qua có thể dẫn đến việc tính phí thực tế cao hơn dự kiến. Tắt theo mặc định để tránh phí bổ sung", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "Trường speed kiểm soát chế độ tốc độ suy luận Claude. Mặc định tắt để tránh vô tình chuyển sang chế độ fast", "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "Khóa Stripe cho sk_xxx hoặc rk_xxx, thông tin nhạy cảm không được hiển thị", "standard 已被移除,vip 用户看不到": "standard has been removed, vip users cannot see it", "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "Trường store ủy quyền cho OpenAI lưu trữ dữ liệu yêu cầu để đánh giá và tối ưu hóa sản phẩm. Tắt theo mặc định. Bật có thể khiến Codex hoạt động không chính xác", @@ -571,6 +572,7 @@ "允许 inference_geo 透传": "Cho phép truyền inference_geo", "允许 safety_identifier 透传": "Cho phép safety_identifier truyền qua", "允许 service_tier 透传": "Cho phép service_tier truyền qua", + "允许 speed 透传": "Cho phép truyền speed", "允许 stream_options.include_obfuscation 透传": "Cho phép truyền stream_options.include_obfuscation", "允许不安全的 Origin(HTTP)": "Cho phép Origin không an toàn (HTTP)", "允许回调(会泄露服务器 IP 地址)": "Cho phép gọi lại (sẽ làm lộ địa chỉ IP máy chủ)", diff --git a/web/src/i18n/locales/zh-CN.json b/web/src/i18n/locales/zh-CN.json index 80f1a962..a64e50d0 100644 --- a/web/src/i18n/locales/zh-CN.json +++ b/web/src/i18n/locales/zh-CN.json @@ -140,6 +140,7 @@ "Reasoning Effort": "Reasoning Effort", "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私", "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式", "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示", "SMTP 发送者邮箱": "SMTP 发送者邮箱", "SMTP 服务器地址": "SMTP 服务器地址", @@ -410,6 +411,7 @@ "允许 HTTP 协议图片请求(适用于自部署代理)": "允许 HTTP 协议图片请求(适用于自部署代理)", "允许 safety_identifier 透传": "允许 safety_identifier 透传", "允许 service_tier 透传": "允许 service_tier 透传", + "允许 speed 透传": "允许 speed 透传", "允许 Turnstile 用户校验": "允许 Turnstile 用户校验", "允许不安全的 Origin(HTTP)": "允许不安全的 Origin(HTTP)", "允许回调(会泄露服务器 IP 地址)": "允许回调(会泄露服务器 IP 地址)", diff --git a/web/src/i18n/locales/zh-TW.json b/web/src/i18n/locales/zh-TW.json index aaf6d33c..1f9351c6 100644 --- a/web/src/i18n/locales/zh-TW.json +++ b/web/src/i18n/locales/zh-TW.json @@ -198,9 +198,11 @@ "default为默认设置,可单独设置每个分类的安全等级": "default為預設設定,可單獨設定每個分類的安全等級", "default为默认设置,可单独设置每个模型的版本": "default為預設設定,可單獨設定每個模型的版本", "false": "false", + "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "inference_geo 字段用於控制 Claude 資料駐留推理區域。預設關閉以避免未經授權透傳地域資訊", "price_xxx 的商品价格 ID,新建产品后可获得": "price_xxx 的商品價格 ID,新建產品後可獲得", "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "safety_identifier 字段用於幫助 OpenAI 識別可能違反使用政策的應用程式使用者。預設關閉以保護使用者隱私", "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "service_tier 字段用於指定服務層級,允許透傳可能導致實際計費高於預期。預設關閉以避免額外費用", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "speed 字段用於控制 Claude 推理速度模式。預設關閉以避免意外切換到 fast 模式", "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "sk_xxx 或 rk_xxx 的 Stripe 密鑰,敏感資訊不顯示", "standard 已被移除,vip 用户看不到": "standard 已被移除,vip 使用者看不到", "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "store 字段用於授權 OpenAI 存儲請求數據以評估和優化產品。預設關閉,開啟後可能導致 Codex 無法正常使用", @@ -498,6 +500,8 @@ "允许 Turnstile 用户校验": "允許 Turnstile 使用者校驗", "允许 safety_identifier 透传": "允許 safety_identifier 透傳", "允许 service_tier 透传": "允許 service_tier 透傳", + "允许 inference_geo 透传": "允許 inference_geo 透傳", + "允许 speed 透传": "允許 speed 透傳", "允许不安全的 Origin(HTTP)": "允許不安全的 Origin(HTTP)", "允许回调(会泄露服务器 IP 地址)": "允許回調(會洩露伺服器 IP 位址)", "允许在 Stripe 支付中输入促销码": "允許在 Stripe 支付中輸入促銷碼", From b2a40d338170ca50c49bbe7b2e9b7562427867b9 Mon Sep 17 00:00:00 2001 From: CaIon Date: Wed, 15 Apr 2026 20:55:31 +0800 Subject: [PATCH 18/46] feat: enhance Stripe webhook handling for async payment events --- controller/topup_stripe.go | 73 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/controller/topup_stripe.go b/controller/topup_stripe.go index e1718cc5..5c92d272 100644 --- a/controller/topup_stripe.go +++ b/controller/topup_stripe.go @@ -146,6 +146,12 @@ func RequestStripePay(c *gin.Context) { } func StripeWebhook(c *gin.Context) { + if setting.StripeWebhookSecret == "" { + log.Println("Stripe Webhook Secret 未配置,拒绝处理") + c.AbortWithStatus(http.StatusForbidden) + return + } + payload, err := io.ReadAll(c.Request.Body) if err != nil { log.Printf("解析Stripe Webhook参数失败: %v\n", err) @@ -154,8 +160,7 @@ func StripeWebhook(c *gin.Context) { } signature := c.GetHeader("Stripe-Signature") - endpointSecret := setting.StripeWebhookSecret - event, err := webhook.ConstructEventWithOptions(payload, signature, endpointSecret, webhook.ConstructEventOptions{ + event, err := webhook.ConstructEventWithOptions(payload, signature, setting.StripeWebhookSecret, webhook.ConstructEventOptions{ IgnoreAPIVersionMismatch: true, }) @@ -170,6 +175,10 @@ func StripeWebhook(c *gin.Context) { sessionCompleted(event) case stripe.EventTypeCheckoutSessionExpired: sessionExpired(event) + case stripe.EventTypeCheckoutSessionAsyncPaymentSucceeded: + sessionAsyncPaymentSucceeded(event) + case stripe.EventTypeCheckoutSessionAsyncPaymentFailed: + sessionAsyncPaymentFailed(event) default: log.Printf("不支持的Stripe Webhook事件类型: %s\n", event.Type) } @@ -186,7 +195,65 @@ func sessionCompleted(event stripe.Event) { return } - // Try complete subscription order first + paymentStatus := event.GetObjectValue("payment_status") + if paymentStatus != "paid" { + log.Printf("Stripe Checkout 支付尚未完成,payment_status: %s, ref: %s(等待异步支付结果)", paymentStatus, referenceId) + return + } + + fulfillOrder(event, referenceId, customerId) +} + +// sessionAsyncPaymentSucceeded handles delayed payment methods (bank transfer, SEPA, etc.) +// that confirm payment after the checkout session completes. +func sessionAsyncPaymentSucceeded(event stripe.Event) { + customerId := event.GetObjectValue("customer") + referenceId := event.GetObjectValue("client_reference_id") + log.Printf("Stripe 异步支付成功: %s", referenceId) + + fulfillOrder(event, referenceId, customerId) +} + +// sessionAsyncPaymentFailed marks orders as failed when delayed payment methods +// ultimately fail (e.g. bank transfer not received, SEPA rejected). +func sessionAsyncPaymentFailed(event stripe.Event) { + referenceId := event.GetObjectValue("client_reference_id") + log.Printf("Stripe 异步支付失败: %s", referenceId) + + if len(referenceId) == 0 { + log.Println("异步支付失败事件未提供支付单号") + return + } + + LockOrder(referenceId) + defer UnlockOrder(referenceId) + + topUp := model.GetTopUpByTradeNo(referenceId) + if topUp == nil { + log.Println("异步支付失败,充值订单不存在:", referenceId) + return + } + + if topUp.Status != common.TopUpStatusPending { + log.Printf("异步支付失败,订单状态非pending: %s, ref: %s", topUp.Status, referenceId) + return + } + + topUp.Status = common.TopUpStatusFailed + if err := topUp.Update(); err != nil { + log.Printf("标记充值订单失败出错: %v, ref: %s", err, referenceId) + return + } + log.Printf("充值订单已标记为失败: %s", referenceId) +} + +// fulfillOrder is the shared logic for crediting quota after payment is confirmed. +func fulfillOrder(event stripe.Event, referenceId string, customerId string) { + if len(referenceId) == 0 { + log.Println("未提供支付单号") + return + } + LockOrder(referenceId) defer UnlockOrder(referenceId) payload := map[string]any{ From 8aaec8b1cc927c1fbe5a70e0c05712a1f072c00e Mon Sep 17 00:00:00 2001 From: CaIon Date: Wed, 15 Apr 2026 21:17:49 +0800 Subject: [PATCH 19/46] feat: add PaymentMethod field to TopUp model and enhance payment method validation in topup controllers --- controller/topup.go | 4 ++++ controller/topup_creem.go | 13 +++++++------ controller/topup_stripe.go | 5 +++++ model/topup.go | 32 +++++++++++++++++++++++--------- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/controller/topup.go b/controller/topup.go index e7a392a4..dee6d889 100644 --- a/controller/topup.go +++ b/controller/topup.go @@ -340,6 +340,10 @@ func EpayNotify(c *gin.Context) { log.Printf("易支付回调未找到订单: %v", verifyInfo) return } + if topUp.PaymentMethod == "stripe" || topUp.PaymentMethod == "creem" || topUp.PaymentMethod == "waffo" { + log.Printf("易支付回调订单支付方式不匹配: %s, 订单号: %s", topUp.PaymentMethod, verifyInfo.ServiceTradeNo) + return + } if topUp.Status == "pending" { topUp.Status = "success" err := topUp.Update() diff --git a/controller/topup_creem.go b/controller/topup_creem.go index 54b67b85..ffff716b 100644 --- a/controller/topup_creem.go +++ b/controller/topup_creem.go @@ -108,12 +108,13 @@ func (*CreemAdaptor) RequestPay(c *gin.Context, req *CreemPayRequest) { // 先创建订单记录,使用产品配置的金额和充值额度 topUp := &model.TopUp{ - UserId: id, - Amount: selectedProduct.Quota, // 充值额度 - Money: selectedProduct.Price, // 支付金额 - TradeNo: referenceId, - CreateTime: time.Now().Unix(), - Status: common.TopUpStatusPending, + UserId: id, + Amount: selectedProduct.Quota, // 充值额度 + Money: selectedProduct.Price, // 支付金额 + TradeNo: referenceId, + PaymentMethod: PaymentMethodCreem, + CreateTime: time.Now().Unix(), + Status: common.TopUpStatusPending, } err = topUp.Insert() if err != nil { diff --git a/controller/topup_stripe.go b/controller/topup_stripe.go index 5c92d272..c2f65472 100644 --- a/controller/topup_stripe.go +++ b/controller/topup_stripe.go @@ -234,6 +234,11 @@ func sessionAsyncPaymentFailed(event stripe.Event) { return } + if topUp.PaymentMethod != PaymentMethodStripe { + log.Printf("异步支付失败,订单支付方式不匹配: %s, ref: %s", topUp.PaymentMethod, referenceId) + return + } + if topUp.Status != common.TopUpStatusPending { log.Printf("异步支付失败,订单状态非pending: %s, ref: %s", topUp.Status, referenceId) return diff --git a/model/topup.go b/model/topup.go index d8c92bfe..b6329e08 100644 --- a/model/topup.go +++ b/model/topup.go @@ -12,17 +12,19 @@ import ( ) type TopUp struct { - Id int `json:"id"` - UserId int `json:"user_id" gorm:"index"` - Amount int64 `json:"amount"` - Money float64 `json:"money"` - TradeNo string `json:"trade_no" gorm:"unique;type:varchar(255);index"` - PaymentMethod string `json:"payment_method" gorm:"type:varchar(50)"` - CreateTime int64 `json:"create_time"` - CompleteTime int64 `json:"complete_time"` - Status string `json:"status"` + Id int `json:"id"` + UserId int `json:"user_id" gorm:"index"` + Amount int64 `json:"amount"` + Money float64 `json:"money"` + TradeNo string `json:"trade_no" gorm:"unique;type:varchar(255);index"` + PaymentMethod string `json:"payment_method" gorm:"type:varchar(50)"` + CreateTime int64 `json:"create_time"` + CompleteTime int64 `json:"complete_time"` + Status string `json:"status"` } +var ErrPaymentMethodMismatch = errors.New("payment method mismatch") + func (topUp *TopUp) Insert() error { var err error err = DB.Create(topUp).Error @@ -74,6 +76,10 @@ func Recharge(referenceId string, customerId string) (err error) { return errors.New("充值订单不存在") } + if topUp.PaymentMethod != "stripe" { + return ErrPaymentMethodMismatch + } + if topUp.Status != common.TopUpStatusPending { return errors.New("充值订单状态错误") } @@ -325,6 +331,10 @@ func RechargeCreem(referenceId string, customerEmail string, customerName string return errors.New("充值订单不存在") } + if topUp.PaymentMethod != "creem" { + return ErrPaymentMethodMismatch + } + if topUp.Status != common.TopUpStatusPending { return errors.New("充值订单状态错误") } @@ -396,6 +406,10 @@ func RechargeWaffo(tradeNo string) (err error) { return errors.New("充值订单不存在") } + if topUp.PaymentMethod != "waffo" { + return ErrPaymentMethodMismatch + } + if topUp.Status == common.TopUpStatusSuccess { return nil // 幂等:已成功直接返回 } From 3cad6b9d7fc5668b1c7cc9995ddfba5291512d0f Mon Sep 17 00:00:00 2001 From: CaIon Date: Thu, 16 Apr 2026 17:44:38 +0800 Subject: [PATCH 20/46] fix(claude): improve handling of empty string content in OpenAI to Claude message conversion --- relay/channel/claude/relay-claude.go | 30 ++++++++++++++++++---------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/relay/channel/claude/relay-claude.go b/relay/channel/claude/relay-claude.go index 3968a268..e7f351b7 100644 --- a/relay/channel/claude/relay-claude.go +++ b/relay/channel/claude/relay-claude.go @@ -258,7 +258,7 @@ func RequestOpenAI2ClaudeMessage(c *gin.Context, textRequest dto.GeneralOpenAIRe formatMessages = formatMessages[:len(formatMessages)-1] } } - if fmtMessage.Content == nil { + if fmtMessage.Content == nil || (fmtMessage.IsStringContent() && fmtMessage.StringContent() == "") { fmtMessage.SetStringContent("...") } formatMessages = append(formatMessages, fmtMessage) @@ -274,14 +274,16 @@ func RequestOpenAI2ClaudeMessage(c *gin.Context, textRequest dto.GeneralOpenAIRe if message.Role == "system" { // 根据Claude API规范,system字段使用数组格式更有通用性 if message.IsStringContent() { - systemMessages = append(systemMessages, dto.ClaudeMediaMessage{ - Type: "text", - Text: common.GetPointer[string](message.StringContent()), - }) + if text := message.StringContent(); text != "" { + systemMessages = append(systemMessages, dto.ClaudeMediaMessage{ + Type: "text", + Text: common.GetPointer[string](text), + }) + } } else { // 支持复合内容的system消息(虽然不常见,但需要考虑完整性) for _, ctx := range message.ParseContent() { - if ctx.Type == "text" { + if ctx.Type == "text" && ctx.Text != "" { systemMessages = append(systemMessages, dto.ClaudeMediaMessage{ Type: "text", Text: common.GetPointer[string](ctx.Text), @@ -339,16 +341,22 @@ func RequestOpenAI2ClaudeMessage(c *gin.Context, textRequest dto.GeneralOpenAIRe } } } else if message.IsStringContent() && message.ToolCalls == nil { - claudeMessage.Content = message.StringContent() + text := message.StringContent() + if text == "" { + text = "..." + } + claudeMessage.Content = text } else { claudeMediaMessages := make([]dto.ClaudeMediaMessage, 0) for _, mediaMessage := range message.ParseContent() { switch mediaMessage.Type { case "text": - claudeMediaMessages = append(claudeMediaMessages, dto.ClaudeMediaMessage{ - Type: "text", - Text: common.GetPointer[string](mediaMessage.Text), - }) + if mediaMessage.Text != "" { + claudeMediaMessages = append(claudeMediaMessages, dto.ClaudeMediaMessage{ + Type: "text", + Text: common.GetPointer[string](mediaMessage.Text), + }) + } default: source := mediaMessage.ToFileSource() if source == nil { From 22e509c1efb2260e1537c78684f1a5e9f053b75a Mon Sep 17 00:00:00 2001 From: CaIon Date: Thu, 16 Apr 2026 20:56:44 +0800 Subject: [PATCH 21/46] refactor: simplify ShouldDisableChannel function by removing unused parameters and commented-out code --- controller/channel-test.go | 2 +- controller/relay.go | 2 +- service/channel.go | 37 +------------------------------------ 3 files changed, 3 insertions(+), 38 deletions(-) diff --git a/controller/channel-test.go b/controller/channel-test.go index db0326d3..8d62a4ac 100644 --- a/controller/channel-test.go +++ b/controller/channel-test.go @@ -830,7 +830,7 @@ func testAllChannels(notify bool) error { newAPIError := result.newAPIError // request error disables the channel if newAPIError != nil { - shouldBanChannel = service.ShouldDisableChannel(channel.Type, result.newAPIError) + shouldBanChannel = service.ShouldDisableChannel(result.newAPIError) } // 当错误检查通过,才检查响应时间 diff --git a/controller/relay.go b/controller/relay.go index a6f3f035..c97ab45b 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -351,7 +351,7 @@ func processChannelError(c *gin.Context, channelError types.ChannelError, err *t logger.LogError(c, fmt.Sprintf("channel error (channel #%d, status code: %d): %s", channelError.ChannelId, err.StatusCode, err.Error())) // 不要使用context获取渠道信息,异步处理时可能会出现渠道信息不一致的情况 // do not use context to get channel info, there may be inconsistent channel info when processing asynchronously - if service.ShouldDisableChannel(channelError.ChannelType, err) && channelError.AutoBan { + if service.ShouldDisableChannel(err) && channelError.AutoBan { gopool.Go(func() { service.DisableChannel(channelError, err.ErrorWithStatusCode()) }) diff --git a/service/channel.go b/service/channel.go index f93b29a2..3fde6e20 100644 --- a/service/channel.go +++ b/service/channel.go @@ -42,7 +42,7 @@ func EnableChannel(channelId int, usingKey string, channelName string) { } } -func ShouldDisableChannel(channelType int, err *types.NewAPIError) bool { +func ShouldDisableChannel(err *types.NewAPIError) bool { if !common.AutomaticDisableChannelEnabled { return false } @@ -58,41 +58,6 @@ func ShouldDisableChannel(channelType int, err *types.NewAPIError) bool { if operation_setting.ShouldDisableByStatusCode(err.StatusCode) { return true } - //if err.StatusCode == http.StatusUnauthorized { - // return true - //} - //if err.StatusCode == http.StatusForbidden { - // switch channelType { - // case constant.ChannelTypeGemini: - // return true - // } - //} - oaiErr := err.ToOpenAIError() - switch oaiErr.Code { - case "invalid_api_key": - return true - case "account_deactivated": - return true - case "billing_not_active": - return true - case "pre_consume_token_quota_failed": - return true - case "Arrearage": - return true - } - switch oaiErr.Type { - case "insufficient_quota": - return true - case "insufficient_user_quota": - return true - // https://docs.anthropic.com/claude/reference/errors - case "authentication_error": - return true - case "permission_error": - return true - case "forbidden": - return true - } lowerMessage := strings.ToLower(err.Error()) search, _ := AcSearch(lowerMessage, operation_setting.AutomaticDisableKeywords, true) From dd57eeb5142212da15e68f22a8bf389a90706793 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 22:45:12 +0000 Subject: [PATCH 22/46] chore(deps): bump github.com/jackc/pgx/v5 from 5.7.1 to 5.9.0 Bumps [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) from 5.7.1 to 5.9.0. - [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md) - [Commits](https://github.com/jackc/pgx/compare/v5.7.1...v5.9.0) --- updated-dependencies: - dependency-name: github.com/jackc/pgx/v5 dependency-version: 5.9.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a078b209..ac2acdab 100644 --- a/go.mod +++ b/go.mod @@ -96,7 +96,7 @@ require ( github.com/icza/bitio v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgx/v5 v5.7.1 // indirect + github.com/jackc/pgx/v5 v5.9.0 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jfreymuth/vorbis v1.0.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect diff --git a/go.sum b/go.sum index 8b687906..2aafa91f 100644 --- a/go.sum +++ b/go.sum @@ -152,8 +152,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.7.1 h1:x7SYsPBYDkHDksogeSmZZ5xzThcTgRz++I5E+ePFUcs= -github.com/jackc/pgx/v5 v5.7.1/go.mod h1:e7O26IywZZ+naJtWWos6i6fvWK+29etgITqrqHLfoZA= +github.com/jackc/pgx/v5 v5.9.0 h1:T/dI+2TvmI2H8s/KH1/lXIbz1CUFk3gn5oTjr0/mBsE= +github.com/jackc/pgx/v5 v5.9.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jfreymuth/oggvorbis v1.0.5 h1:u+Ck+R0eLSRhgq8WTmffYnrVtSztJcYrl588DM4e3kQ= From 47d7bca268494d66cd80f7200e8e29c1ae335feb Mon Sep 17 00:00:00 2001 From: papersnake Date: Fri, 17 Apr 2026 13:52:34 +0800 Subject: [PATCH 23/46] feat: support claude-opus-4-7 (#4293) * feat: support claude-opus-4-7 * feat: summarized display for opus 4.7 --- dto/claude.go | 5 +++ relay/channel/aws/constants.go | 6 ++++ relay/channel/claude/constants.go | 7 ++++ relay/channel/claude/relay-claude.go | 51 +++++++++++++++++++--------- relay/channel/vertex/adaptor.go | 1 + relay/claude_handler.go | 45 +++++++++++++++++------- setting/ratio_setting/cache_ratio.go | 14 ++++++++ setting/ratio_setting/model_ratio.go | 6 ++++ setting/reasoning/suffix.go | 2 +- 9 files changed, 107 insertions(+), 30 deletions(-) diff --git a/dto/claude.go b/dto/claude.go index b35d6565..d7fed412 100644 --- a/dto/claude.go +++ b/dto/claude.go @@ -448,6 +448,11 @@ func ProcessTools(tools []any) ([]*Tool, []*ClaudeWebSearchTool) { type Thinking struct { Type string `json:"type,omitempty"` BudgetTokens *int `json:"budget_tokens,omitempty"` + // Display controls whether thinking content is returned in the response. + // Used with adaptive thinking on Claude Opus 4.7+: "summarized" restores + // the visible summary that was default on Opus 4.6; "omitted" (default on + // 4.7) suppresses it. Pass-through field from upstream Anthropic API. + Display string `json:"display,omitempty"` } func (c *Thinking) GetBudgetTokens() int { diff --git a/relay/channel/aws/constants.go b/relay/channel/aws/constants.go index 55f87ecf..ff1f377e 100644 --- a/relay/channel/aws/constants.go +++ b/relay/channel/aws/constants.go @@ -18,6 +18,7 @@ var awsModelIDMap = map[string]string{ "claude-haiku-4-5-20251001": "anthropic.claude-haiku-4-5-20251001-v1:0", "claude-opus-4-5-20251101": "anthropic.claude-opus-4-5-20251101-v1:0", "claude-opus-4-6": "anthropic.claude-opus-4-6-v1", + "claude-opus-4-7": "anthropic.claude-opus-4-7", // Nova models "nova-micro-v1:0": "amazon.nova-micro-v1:0", "nova-lite-v1:0": "amazon.nova-lite-v1:0", @@ -91,6 +92,11 @@ var awsModelCanCrossRegionMap = map[string]map[string]bool{ "ap": true, "eu": true, }, + "anthropic.claude-opus-4-7": { + "us": true, + "ap": true, + "eu": true, + }, "anthropic.claude-haiku-4-5-20251001-v1:0": { "us": true, "ap": true, diff --git a/relay/channel/claude/constants.go b/relay/channel/claude/constants.go index 1a3fac56..3c516aef 100644 --- a/relay/channel/claude/constants.go +++ b/relay/channel/claude/constants.go @@ -26,6 +26,13 @@ var ModelList = []string{ "claude-opus-4-6-medium", "claude-opus-4-6-low", "claude-sonnet-4-6", + "claude-opus-4-7", + "claude-opus-4-7-max", + "claude-opus-4-7-xhigh", + "claude-opus-4-7-high", + "claude-opus-4-7-medium", + "claude-opus-4-7-low", + "claude-opus-4-7-thinking", } var ChannelName = "claude" diff --git a/relay/channel/claude/relay-claude.go b/relay/channel/claude/relay-claude.go index e7f351b7..fa823452 100644 --- a/relay/channel/claude/relay-claude.go +++ b/relay/channel/claude/relay-claude.go @@ -154,33 +154,52 @@ func RequestOpenAI2ClaudeMessage(c *gin.Context, textRequest dto.GeneralOpenAIRe } if baseModel, effortLevel, ok := reasoning.TrimEffortSuffix(textRequest.Model); ok && effortLevel != "" && - strings.HasPrefix(textRequest.Model, "claude-opus-4-6") { + (strings.HasPrefix(textRequest.Model, "claude-opus-4-6") || strings.HasPrefix(textRequest.Model, "claude-opus-4-7")) { claudeRequest.Model = baseModel claudeRequest.Thinking = &dto.Thinking{ Type: "adaptive", } claudeRequest.OutputConfig = json.RawMessage(fmt.Sprintf(`{"effort":"%s"}`, effortLevel)) - claudeRequest.TopP = nil - claudeRequest.Temperature = common.GetPointer[float64](1.0) + if strings.HasPrefix(baseModel, "claude-opus-4-7") { + // Opus 4.7 rejects non-default temperature/top_p/top_k with 400 + // and defaults display to "omitted"; restore the 4.6 visible summary. + claudeRequest.Thinking.Display = "summarized" + claudeRequest.Temperature = nil + claudeRequest.TopP = nil + claudeRequest.TopK = nil + } else { + claudeRequest.TopP = nil + claudeRequest.Temperature = common.GetPointer[float64](1.0) + } } else if model_setting.GetClaudeSettings().ThinkingAdapterEnabled && strings.HasSuffix(textRequest.Model, "-thinking") { - // 因为BudgetTokens 必须大于1024 - if claudeRequest.MaxTokens == nil || *claudeRequest.MaxTokens < 1280 { - claudeRequest.MaxTokens = common.GetPointer[uint](1280) - } + trimmedModel := strings.TrimSuffix(textRequest.Model, "-thinking") + if strings.HasPrefix(trimmedModel, "claude-opus-4-7") { + // Opus 4.7 rejects thinking.type="enabled"; use adaptive at high effort. + claudeRequest.Thinking = &dto.Thinking{Type: "adaptive", Display: "summarized"} + claudeRequest.OutputConfig = json.RawMessage(`{"effort":"high"}`) + claudeRequest.Temperature = nil + claudeRequest.TopP = nil + claudeRequest.TopK = nil + } else { + // 因为BudgetTokens 必须大于1024 + if claudeRequest.MaxTokens == nil || *claudeRequest.MaxTokens < 1280 { + claudeRequest.MaxTokens = common.GetPointer[uint](1280) + } - // BudgetTokens 为 max_tokens 的 80% - claudeRequest.Thinking = &dto.Thinking{ - Type: "enabled", - BudgetTokens: common.GetPointer[int](int(float64(*claudeRequest.MaxTokens) * model_setting.GetClaudeSettings().ThinkingAdapterBudgetTokensPercentage)), + // BudgetTokens 为 max_tokens 的 80% + claudeRequest.Thinking = &dto.Thinking{ + Type: "enabled", + BudgetTokens: common.GetPointer[int](int(float64(*claudeRequest.MaxTokens) * model_setting.GetClaudeSettings().ThinkingAdapterBudgetTokensPercentage)), + } + // TODO: 临时处理 + // https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#important-considerations-when-using-extended-thinking + claudeRequest.TopP = nil + claudeRequest.Temperature = common.GetPointer[float64](1.0) } - // TODO: 临时处理 - // https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#important-considerations-when-using-extended-thinking - claudeRequest.TopP = nil - claudeRequest.Temperature = common.GetPointer[float64](1.0) if !model_setting.ShouldPreserveThinkingSuffix(textRequest.Model) { - claudeRequest.Model = strings.TrimSuffix(textRequest.Model, "-thinking") + claudeRequest.Model = trimmedModel } } diff --git a/relay/channel/vertex/adaptor.go b/relay/channel/vertex/adaptor.go index 7e56c52b..5cf1fcc0 100644 --- a/relay/channel/vertex/adaptor.go +++ b/relay/channel/vertex/adaptor.go @@ -44,6 +44,7 @@ var claudeModelMap = map[string]string{ "claude-haiku-4-5-20251001": "claude-haiku-4-5@20251001", "claude-opus-4-5-20251101": "claude-opus-4-5@20251101", "claude-opus-4-6": "claude-opus-4-6", + "claude-opus-4-7": "claude-opus-4-7", } const anthropicVersion = "vertex-2023-10-16" diff --git a/relay/claude_handler.go b/relay/claude_handler.go index dc4c93f8..54f8ced2 100644 --- a/relay/claude_handler.go +++ b/relay/claude_handler.go @@ -53,30 +53,49 @@ func ClaudeHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *typ } if baseModel, effortLevel, ok := reasoning.TrimEffortSuffix(request.Model); ok && effortLevel != "" && - strings.HasPrefix(request.Model, "claude-opus-4-6") { + (strings.HasPrefix(request.Model, "claude-opus-4-6") || strings.HasPrefix(request.Model, "claude-opus-4-7")) { request.Model = baseModel request.Thinking = &dto.Thinking{ Type: "adaptive", } request.OutputConfig = json.RawMessage(fmt.Sprintf(`{"effort":"%s"}`, effortLevel)) - request.Temperature = common.GetPointer[float64](1.0) + if strings.HasPrefix(request.Model, "claude-opus-4-7") { + // Opus 4.7 rejects non-default temperature/top_p/top_k with 400 + // and defaults display to "omitted"; restore the 4.6 visible summary. + request.Thinking.Display = "summarized" + request.Temperature = nil + request.TopP = nil + request.TopK = nil + } else { + request.Temperature = common.GetPointer[float64](1.0) + } info.UpstreamModelName = request.Model } else if model_setting.GetClaudeSettings().ThinkingAdapterEnabled && strings.HasSuffix(request.Model, "-thinking") { if request.Thinking == nil { - // 因为BudgetTokens 必须大于1024 - if request.MaxTokens == nil || *request.MaxTokens < 1280 { - request.MaxTokens = common.GetPointer[uint](1280) - } + baseModel := strings.TrimSuffix(request.Model, "-thinking") + if strings.HasPrefix(baseModel, "claude-opus-4-7") { + // Opus 4.7 rejects thinking.type="enabled"; use adaptive at high effort. + request.Thinking = &dto.Thinking{Type: "adaptive", Display: "summarized"} + request.OutputConfig = json.RawMessage(`{"effort":"high"}`) + request.Temperature = nil + request.TopP = nil + request.TopK = nil + } else { + // 因为BudgetTokens 必须大于1024 + if request.MaxTokens == nil || *request.MaxTokens < 1280 { + request.MaxTokens = common.GetPointer[uint](1280) + } - // BudgetTokens 为 max_tokens 的 80% - request.Thinking = &dto.Thinking{ - Type: "enabled", - BudgetTokens: common.GetPointer[int](int(float64(*request.MaxTokens) * model_setting.GetClaudeSettings().ThinkingAdapterBudgetTokensPercentage)), + // BudgetTokens 为 max_tokens 的 80% + request.Thinking = &dto.Thinking{ + Type: "enabled", + BudgetTokens: common.GetPointer[int](int(float64(*request.MaxTokens) * model_setting.GetClaudeSettings().ThinkingAdapterBudgetTokensPercentage)), + } + // TODO: 临时处理 + // https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#important-considerations-when-using-extended-thinking + request.Temperature = common.GetPointer[float64](1.0) } - // TODO: 临时处理 - // https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#important-considerations-when-using-extended-thinking - request.Temperature = common.GetPointer[float64](1.0) } if !model_setting.ShouldPreserveThinkingSuffix(info.OriginModelName) { request.Model = strings.TrimSuffix(request.Model, "-thinking") diff --git a/setting/ratio_setting/cache_ratio.go b/setting/ratio_setting/cache_ratio.go index 2c75ab48..fe6e3b32 100644 --- a/setting/ratio_setting/cache_ratio.go +++ b/setting/ratio_setting/cache_ratio.go @@ -64,6 +64,13 @@ var defaultCacheRatio = map[string]float64{ "claude-opus-4-6-high": 0.1, "claude-opus-4-6-medium": 0.1, "claude-opus-4-6-low": 0.1, + "claude-opus-4-7": 0.1, + "claude-opus-4-7-thinking": 0.1, + "claude-opus-4-7-max": 0.1, + "claude-opus-4-7-xhigh": 0.1, + "claude-opus-4-7-high": 0.1, + "claude-opus-4-7-medium": 0.1, + "claude-opus-4-7-low": 0.1, } var defaultCreateCacheRatio = map[string]float64{ @@ -92,6 +99,13 @@ var defaultCreateCacheRatio = map[string]float64{ "claude-opus-4-6-high": 1.25, "claude-opus-4-6-medium": 1.25, "claude-opus-4-6-low": 1.25, + "claude-opus-4-7": 1.25, + "claude-opus-4-7-thinking": 1.25, + "claude-opus-4-7-max": 1.25, + "claude-opus-4-7-xhigh": 1.25, + "claude-opus-4-7-high": 1.25, + "claude-opus-4-7-medium": 1.25, + "claude-opus-4-7-low": 1.25, } //var defaultCreateCacheRatio = map[string]float64{} diff --git a/setting/ratio_setting/model_ratio.go b/setting/ratio_setting/model_ratio.go index 6899d92c..7556fd94 100644 --- a/setting/ratio_setting/model_ratio.go +++ b/setting/ratio_setting/model_ratio.go @@ -146,6 +146,12 @@ var defaultModelRatio = map[string]float64{ "claude-opus-4-6-high": 2.5, "claude-opus-4-6-medium": 2.5, "claude-opus-4-6-low": 2.5, + "claude-opus-4-7": 2.5, + "claude-opus-4-7-max": 2.5, + "claude-opus-4-7-xhigh": 2.5, + "claude-opus-4-7-high": 2.5, + "claude-opus-4-7-medium": 2.5, + "claude-opus-4-7-low": 2.5, "claude-3-opus-20240229": 7.5, // $15 / 1M tokens "claude-opus-4-20250514": 7.5, "claude-opus-4-1-20250805": 7.5, diff --git a/setting/reasoning/suffix.go b/setting/reasoning/suffix.go index fb66c601..2b95de6d 100644 --- a/setting/reasoning/suffix.go +++ b/setting/reasoning/suffix.go @@ -6,7 +6,7 @@ import ( "github.com/samber/lo" ) -var EffortSuffixes = []string{"-max", "-high", "-medium", "-low", "-minimal"} +var EffortSuffixes = []string{"-max", "-xhigh", "-high", "-medium", "-low", "-minimal"} // TrimEffortSuffix -> modelName level(low) exists func TrimEffortSuffix(modelName string) (string, string, bool) { From 45cc95a25cb4ca843730777f1b9d3bb3e87cdb91 Mon Sep 17 00:00:00 2001 From: KoellM Date: Fri, 17 Apr 2026 19:49:19 +0800 Subject: [PATCH 24/46] fix(gemini): add IncludeServerSideToolInvocations field to ToolConfig --- dto/gemini.go | 1 + 1 file changed, 1 insertion(+) diff --git a/dto/gemini.go b/dto/gemini.go index 029c3f03..fd8b5a0b 100644 --- a/dto/gemini.go +++ b/dto/gemini.go @@ -46,6 +46,7 @@ func (r *GeminiChatRequest) UnmarshalJSON(data []byte) error { type ToolConfig struct { FunctionCallingConfig *FunctionCallingConfig `json:"functionCallingConfig,omitempty"` RetrievalConfig *RetrievalConfig `json:"retrievalConfig,omitempty"` + IncludeServerSideToolInvocations *bool `json:"includeServerSideToolInvocations,omitempty"` } type FunctionCallingConfig struct { From e2807c5f950229770ff9d4ffc47bb968547887e4 Mon Sep 17 00:00:00 2001 From: CaIon Date: Fri, 17 Apr 2026 23:46:28 +0800 Subject: [PATCH 25/46] feat: enhance SSRF protection --- common/ssrf_protection.go | 100 +++++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 28 deletions(-) diff --git a/common/ssrf_protection.go b/common/ssrf_protection.go index 3cd5c2ea..1e0c00d6 100644 --- a/common/ssrf_protection.go +++ b/common/ssrf_protection.go @@ -29,45 +29,89 @@ var DefaultSSRFProtection = &SSRFProtection{ AllowedPorts: []int{}, } -// isPrivateIP 检查IP是否为私有地址 +// privateIPv4Nets IPv4 私有/保留/特殊用途网段 +// 参考 IANA IPv4 Special-Purpose Address Registry +// https://www.iana.org/assignments/iana-ipv4-special-registry/ +var privateIPv4Nets = []net.IPNet{ + {IP: net.IPv4(0, 0, 0, 0), Mask: net.CIDRMask(8, 32)}, // 0.0.0.0/8 ("This network" / 未指定) + {IP: net.IPv4(10, 0, 0, 0), Mask: net.CIDRMask(8, 32)}, // 10.0.0.0/8 (私有) + {IP: net.IPv4(100, 64, 0, 0), Mask: net.CIDRMask(10, 32)}, // 100.64.0.0/10 (运营商级 NAT / CGNAT) + {IP: net.IPv4(127, 0, 0, 0), Mask: net.CIDRMask(8, 32)}, // 127.0.0.0/8 (回环) + {IP: net.IPv4(169, 254, 0, 0), Mask: net.CIDRMask(16, 32)}, // 169.254.0.0/16 (链路本地) + {IP: net.IPv4(172, 16, 0, 0), Mask: net.CIDRMask(12, 32)}, // 172.16.0.0/12 (私有) + {IP: net.IPv4(192, 0, 0, 0), Mask: net.CIDRMask(24, 32)}, // 192.0.0.0/24 (IETF 协议分配) + {IP: net.IPv4(192, 0, 2, 0), Mask: net.CIDRMask(24, 32)}, // 192.0.2.0/24 (TEST-NET-1) + {IP: net.IPv4(192, 168, 0, 0), Mask: net.CIDRMask(16, 32)}, // 192.168.0.0/16 (私有) + {IP: net.IPv4(198, 18, 0, 0), Mask: net.CIDRMask(15, 32)}, // 198.18.0.0/15 (基准测试) + {IP: net.IPv4(198, 51, 100, 0), Mask: net.CIDRMask(24, 32)}, // 198.51.100.0/24 (TEST-NET-2) + {IP: net.IPv4(203, 0, 113, 0), Mask: net.CIDRMask(24, 32)}, // 203.0.113.0/24 (TEST-NET-3) + {IP: net.IPv4(224, 0, 0, 0), Mask: net.CIDRMask(4, 32)}, // 224.0.0.0/4 (组播) + {IP: net.IPv4(240, 0, 0, 0), Mask: net.CIDRMask(4, 32)}, // 240.0.0.0/4 (保留) + {IP: net.IPv4(255, 255, 255, 255), Mask: net.CIDRMask(32, 32)}, // 255.255.255.255/32 (受限广播) +} + +// privateIPv6Nets IPv6 私有/保留/特殊用途网段 +// 参考 IANA IPv6 Special-Purpose Address Registry +// https://www.iana.org/assignments/iana-ipv6-special-registry/ +var privateIPv6Nets = func() []net.IPNet { + cidrs := []string{ + "::/128", // 未指定地址 + "::1/128", // 回环 + "::ffff:0:0/96", // IPv4-mapped + "64:ff9b::/96", // IPv4/IPv6 translation + "100::/64", // Discard-Only + "2001::/23", // IETF Protocol Assignments + "2001:db8::/32", // 文档 + "fc00::/7", // Unique Local Address (ULA) + "fe80::/10", // 链路本地 + "ff00::/8", // 组播 + } + nets := make([]net.IPNet, 0, len(cidrs)) + for _, c := range cidrs { + if _, n, err := net.ParseCIDR(c); err == nil && n != nil { + nets = append(nets, *n) + } + } + return nets +}() + +// isPrivateIP 检查IP是否为私有/保留/特殊用途地址 func isPrivateIP(ip net.IP) bool { + if ip == nil { + return true + } + // 未指定地址 (0.0.0.0, ::) + if ip.IsUnspecified() { + return true + } + // 回环、链路本地 (unicast/multicast) if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() { return true } - - // 检查私有网段 - private := []net.IPNet{ - {IP: net.IPv4(10, 0, 0, 0), Mask: net.CIDRMask(8, 32)}, // 10.0.0.0/8 - {IP: net.IPv4(172, 16, 0, 0), Mask: net.CIDRMask(12, 32)}, // 172.16.0.0/12 - {IP: net.IPv4(192, 168, 0, 0), Mask: net.CIDRMask(16, 32)}, // 192.168.0.0/16 - {IP: net.IPv4(127, 0, 0, 0), Mask: net.CIDRMask(8, 32)}, // 127.0.0.0/8 - {IP: net.IPv4(169, 254, 0, 0), Mask: net.CIDRMask(16, 32)}, // 169.254.0.0/16 (链路本地) - {IP: net.IPv4(224, 0, 0, 0), Mask: net.CIDRMask(4, 32)}, // 224.0.0.0/4 (组播) - {IP: net.IPv4(240, 0, 0, 0), Mask: net.CIDRMask(4, 32)}, // 240.0.0.0/4 (保留) + // 接口本地组播 (IPv6 ff01::/16 等) + if ip.IsInterfaceLocalMulticast() { + return true } - for _, privateNet := range private { + if v4 := ip.To4(); v4 != nil { + for _, privateNet := range privateIPv4Nets { + if privateNet.Contains(v4) { + return true + } + } + return false + } + + // IPv6 检查 + for _, privateNet := range privateIPv6Nets { if privateNet.Contains(ip) { return true } } - - // 检查IPv6私有地址 - if ip.To4() == nil { - // IPv6 loopback - if ip.Equal(net.IPv6loopback) { - return true - } - // IPv6 link-local - if strings.HasPrefix(ip.String(), "fe80:") { - return true - } - // IPv6 unique local - if strings.HasPrefix(ip.String(), "fc") || strings.HasPrefix(ip.String(), "fd") { - return true - } + // 兜底: Go 标准库识别的其他私有地址 + if ip.IsPrivate() { + return true } - return false } From 209d90e86175900675625222ea1498436d7933fe Mon Sep 17 00:00:00 2001 From: CaIon Date: Fri, 17 Apr 2026 23:51:30 +0800 Subject: [PATCH 26/46] feat(topup): add admin-only audit info to top-up logs Thread caller IP from webhook/admin controllers through model recharge functions and record a new RecordTopupLog entry with admin_info (server IP, caller IP, order payment method, callback payment method, system version). Frontend shows these fields in the expanded log row and the IP column for admins on top-up logs, while non-admins continue to see admin_info stripped by formatUserLogs. --- controller/topup.go | 4 +- controller/topup_creem.go | 2 +- controller/topup_stripe.go | 21 +- controller/topup_waffo.go | 2 +- model/log.go | 27 + model/topup.go | 18 +- .../table/usage-logs/UsageLogsColumnDefs.jsx | 7 +- web/src/hooks/usage-logs/useUsageLogsData.jsx | 37 +- web/src/i18n/locales/en.json | 536 +++--- web/src/i18n/locales/fr.json | 573 ++++--- web/src/i18n/locales/ja.json | 533 +++--- web/src/i18n/locales/ru.json | 579 ++++--- web/src/i18n/locales/vi.json | 538 +++--- web/src/i18n/locales/zh-CN.json | 1490 ++++++++++++----- web/src/i18n/locales/zh-TW.json | 820 +++++++-- 15 files changed, 3570 insertions(+), 1617 deletions(-) diff --git a/controller/topup.go b/controller/topup.go index dee6d889..395156e4 100644 --- a/controller/topup.go +++ b/controller/topup.go @@ -362,7 +362,7 @@ func EpayNotify(c *gin.Context) { return } log.Printf("易支付回调更新用户成功 %v", topUp) - model.RecordLog(topUp.UserId, model.LogTypeTopup, fmt.Sprintf("使用在线充值成功,充值金额: %v,支付金额:%f", logger.LogQuota(quotaToAdd), topUp.Money)) + model.RecordTopupLog(topUp.UserId, fmt.Sprintf("使用在线充值成功,充值金额: %v,支付金额:%f", logger.LogQuota(quotaToAdd), topUp.Money), c.ClientIP(), topUp.PaymentMethod, "epay") } } else { log.Printf("易支付异常回调: %v", verifyInfo) @@ -461,7 +461,7 @@ func AdminCompleteTopUp(c *gin.Context) { LockOrder(req.TradeNo) defer UnlockOrder(req.TradeNo) - if err := model.ManualCompleteTopUp(req.TradeNo); err != nil { + if err := model.ManualCompleteTopUp(req.TradeNo, c.ClientIP()); err != nil { common.ApiError(c, err) return } diff --git a/controller/topup_creem.go b/controller/topup_creem.go index ffff716b..0b6b1627 100644 --- a/controller/topup_creem.go +++ b/controller/topup_creem.go @@ -353,7 +353,7 @@ func handleCheckoutCompleted(c *gin.Context, event *CreemWebhookEvent) { log.Printf("警告:Creem回调中客户姓名为空 - 订单号: %s", referenceId) } - err := model.RechargeCreem(referenceId, customerEmail, customerName) + err := model.RechargeCreem(referenceId, customerEmail, customerName, c.ClientIP()) if err != nil { log.Printf("Creem充值处理失败: %s, 订单号: %s", err.Error(), referenceId) c.AbortWithStatus(http.StatusInternalServerError) diff --git a/controller/topup_stripe.go b/controller/topup_stripe.go index c2f65472..bb8e24fa 100644 --- a/controller/topup_stripe.go +++ b/controller/topup_stripe.go @@ -170,15 +170,16 @@ func StripeWebhook(c *gin.Context) { return } + callerIp := c.ClientIP() switch event.Type { case stripe.EventTypeCheckoutSessionCompleted: - sessionCompleted(event) + sessionCompleted(event, callerIp) case stripe.EventTypeCheckoutSessionExpired: sessionExpired(event) case stripe.EventTypeCheckoutSessionAsyncPaymentSucceeded: - sessionAsyncPaymentSucceeded(event) + sessionAsyncPaymentSucceeded(event, callerIp) case stripe.EventTypeCheckoutSessionAsyncPaymentFailed: - sessionAsyncPaymentFailed(event) + sessionAsyncPaymentFailed(event, callerIp) default: log.Printf("不支持的Stripe Webhook事件类型: %s\n", event.Type) } @@ -186,7 +187,7 @@ func StripeWebhook(c *gin.Context) { c.Status(http.StatusOK) } -func sessionCompleted(event stripe.Event) { +func sessionCompleted(event stripe.Event, callerIp string) { customerId := event.GetObjectValue("customer") referenceId := event.GetObjectValue("client_reference_id") status := event.GetObjectValue("status") @@ -201,22 +202,22 @@ func sessionCompleted(event stripe.Event) { return } - fulfillOrder(event, referenceId, customerId) + fulfillOrder(event, referenceId, customerId, callerIp) } // sessionAsyncPaymentSucceeded handles delayed payment methods (bank transfer, SEPA, etc.) // that confirm payment after the checkout session completes. -func sessionAsyncPaymentSucceeded(event stripe.Event) { +func sessionAsyncPaymentSucceeded(event stripe.Event, callerIp string) { customerId := event.GetObjectValue("customer") referenceId := event.GetObjectValue("client_reference_id") log.Printf("Stripe 异步支付成功: %s", referenceId) - fulfillOrder(event, referenceId, customerId) + fulfillOrder(event, referenceId, customerId, callerIp) } // sessionAsyncPaymentFailed marks orders as failed when delayed payment methods // ultimately fail (e.g. bank transfer not received, SEPA rejected). -func sessionAsyncPaymentFailed(event stripe.Event) { +func sessionAsyncPaymentFailed(event stripe.Event, callerIp string) { referenceId := event.GetObjectValue("client_reference_id") log.Printf("Stripe 异步支付失败: %s", referenceId) @@ -253,7 +254,7 @@ func sessionAsyncPaymentFailed(event stripe.Event) { } // fulfillOrder is the shared logic for crediting quota after payment is confirmed. -func fulfillOrder(event stripe.Event, referenceId string, customerId string) { +func fulfillOrder(event stripe.Event, referenceId string, customerId string, callerIp string) { if len(referenceId) == 0 { log.Println("未提供支付单号") return @@ -274,7 +275,7 @@ func fulfillOrder(event stripe.Event, referenceId string, customerId string) { return } - err := model.Recharge(referenceId, customerId) + err := model.Recharge(referenceId, customerId, callerIp) if err != nil { log.Println(err.Error(), referenceId) return diff --git a/controller/topup_waffo.go b/controller/topup_waffo.go index fce37642..f55baefb 100644 --- a/controller/topup_waffo.go +++ b/controller/topup_waffo.go @@ -357,7 +357,7 @@ func handleWaffoPayment(c *gin.Context, wh *core.WebhookHandler, result *core.Pa LockOrder(merchantOrderId) defer UnlockOrder(merchantOrderId) - if err := model.RechargeWaffo(merchantOrderId); err != nil { + if err := model.RechargeWaffo(merchantOrderId, c.ClientIP()); err != nil { log.Printf("Waffo 充值处理失败: %v, 订单: %s", err, merchantOrderId) sendWaffoWebhookResponse(c, wh, false, err.Error()) return diff --git a/model/log.go b/model/log.go index 68bc6504..f9d15985 100644 --- a/model/log.go +++ b/model/log.go @@ -90,6 +90,33 @@ func RecordLog(userId int, logType int, content string) { } } +func RecordTopupLog(userId int, content string, callerIp string, paymentMethod string, callbackPaymentMethod string) { + username, _ := GetUsernameById(userId, false) + adminInfo := map[string]interface{}{ + "server_ip": common.GetIp(), + "caller_ip": callerIp, + "payment_method": paymentMethod, + "callback_payment_method": callbackPaymentMethod, + "version": common.Version, + } + other := map[string]interface{}{ + "admin_info": adminInfo, + } + log := &Log{ + UserId: userId, + Username: username, + CreatedAt: common.GetTimestamp(), + Type: LogTypeTopup, + Content: content, + Ip: callerIp, + Other: common.MapToJsonStr(other), + } + err := LOG_DB.Create(log).Error + if err != nil { + common.SysLog("failed to record topup log: " + err.Error()) + } +} + func RecordErrorLog(c *gin.Context, userId int, channelId int, modelName string, tokenName string, content string, tokenId int, useTimeSeconds int, isStream bool, group string, other map[string]interface{}) { logger.LogInfo(c, fmt.Sprintf("record error log: userId=%d, channelId=%d, modelName=%s, tokenName=%s, content=%s", userId, channelId, modelName, tokenName, content)) diff --git a/model/topup.go b/model/topup.go index b6329e08..00ad5c49 100644 --- a/model/topup.go +++ b/model/topup.go @@ -57,7 +57,7 @@ func GetTopUpByTradeNo(tradeNo string) *TopUp { return topUp } -func Recharge(referenceId string, customerId string) (err error) { +func Recharge(referenceId string, customerId string, callerIp string) (err error) { if referenceId == "" { return errors.New("未提供支付单号") } @@ -105,7 +105,7 @@ func Recharge(referenceId string, customerId string) (err error) { return errors.New("充值失败,请稍后重试") } - RecordLog(topUp.UserId, LogTypeTopup, fmt.Sprintf("使用在线充值成功,充值金额: %v,支付金额:%d", logger.FormatQuota(int(quota)), topUp.Amount)) + RecordTopupLog(topUp.UserId, fmt.Sprintf("使用在线充值成功,充值金额: %v,支付金额:%d", logger.FormatQuota(int(quota)), topUp.Amount), callerIp, topUp.PaymentMethod, "stripe") return nil } @@ -242,7 +242,7 @@ func SearchAllTopUps(keyword string, pageInfo *common.PageInfo) (topups []*TopUp } // ManualCompleteTopUp 管理员手动完成订单并给用户充值 -func ManualCompleteTopUp(tradeNo string) error { +func ManualCompleteTopUp(tradeNo string, callerIp string) error { if tradeNo == "" { return errors.New("未提供订单号") } @@ -255,6 +255,7 @@ func ManualCompleteTopUp(tradeNo string) error { var userId int var quotaToAdd int var payMoney float64 + var paymentMethod string err := DB.Transaction(func(tx *gorm.DB) error { topUp := &TopUp{} @@ -301,6 +302,7 @@ func ManualCompleteTopUp(tradeNo string) error { userId = topUp.UserId payMoney = topUp.Money + paymentMethod = topUp.PaymentMethod return nil }) @@ -309,10 +311,10 @@ func ManualCompleteTopUp(tradeNo string) error { } // 事务外记录日志,避免阻塞 - RecordLog(userId, LogTypeTopup, fmt.Sprintf("管理员补单成功,充值金额: %v,支付金额:%f", logger.FormatQuota(quotaToAdd), payMoney)) + RecordTopupLog(userId, fmt.Sprintf("管理员补单成功,充值金额: %v,支付金额:%f", logger.FormatQuota(quotaToAdd), payMoney), callerIp, paymentMethod, "admin") return nil } -func RechargeCreem(referenceId string, customerEmail string, customerName string) (err error) { +func RechargeCreem(referenceId string, customerEmail string, customerName string, callerIp string) (err error) { if referenceId == "" { return errors.New("未提供支付单号") } @@ -382,12 +384,12 @@ func RechargeCreem(referenceId string, customerEmail string, customerName string return errors.New("充值失败,请稍后重试") } - RecordLog(topUp.UserId, LogTypeTopup, fmt.Sprintf("使用Creem充值成功,充值额度: %v,支付金额:%.2f", quota, topUp.Money)) + RecordTopupLog(topUp.UserId, fmt.Sprintf("使用Creem充值成功,充值额度: %v,支付金额:%.2f", quota, topUp.Money), callerIp, topUp.PaymentMethod, "creem") return nil } -func RechargeWaffo(tradeNo string) (err error) { +func RechargeWaffo(tradeNo string, callerIp string) (err error) { if tradeNo == "" { return errors.New("未提供支付单号") } @@ -444,7 +446,7 @@ func RechargeWaffo(tradeNo string) (err error) { } if quotaToAdd > 0 { - RecordLog(topUp.UserId, LogTypeTopup, fmt.Sprintf("Waffo充值成功,充值额度: %v,支付金额: %.2f", logger.FormatQuota(quotaToAdd), topUp.Money)) + RecordTopupLog(topUp.UserId, fmt.Sprintf("Waffo充值成功,充值额度: %v,支付金额: %.2f", logger.FormatQuota(quotaToAdd), topUp.Money), callerIp, topUp.PaymentMethod, "waffo") } return nil diff --git a/web/src/components/table/usage-logs/UsageLogsColumnDefs.jsx b/web/src/components/table/usage-logs/UsageLogsColumnDefs.jsx index d5243afc..e71fcb5e 100644 --- a/web/src/components/table/usage-logs/UsageLogsColumnDefs.jsx +++ b/web/src/components/table/usage-logs/UsageLogsColumnDefs.jsx @@ -876,7 +876,12 @@ export const getLogsColumns = ({ ), dataIndex: 'ip', render: (text, record, index) => { - return (record.type === 2 || record.type === 5) && text ? ( + const showIp = + (record.type === 2 || + record.type === 5 || + (isAdminUser && record.type === 1)) && + text; + return showIp ? ( { ), }); } - if (isAdminUser && logs[i].type !== 6) { + if (isAdminUser && logs[i].type !== 6 && logs[i].type !== 1) { expandDataLocal.push({ key: t('请求转换'), value: requestConversionDisplayValue(other?.request_conversion), }); } - if (isAdminUser && logs[i].type !== 6) { + if (isAdminUser && logs[i].type !== 6 && logs[i].type !== 1) { let localCountMode = ''; if (other?.admin_info?.local_count_tokens) { localCountMode = t('本地计费'); @@ -713,6 +713,39 @@ export const useLogsData = () => { value: localCountMode, }); } + if (isAdminUser && logs[i].type === 1 && other?.admin_info) { + const adminInfo = other.admin_info; + if (adminInfo.payment_method) { + expandDataLocal.push({ + key: t('订单支付方式'), + value: adminInfo.payment_method, + }); + } + if (adminInfo.callback_payment_method) { + expandDataLocal.push({ + key: t('回调支付方式'), + value: adminInfo.callback_payment_method, + }); + } + if (adminInfo.caller_ip) { + expandDataLocal.push({ + key: t('回调调用者IP'), + value: adminInfo.caller_ip, + }); + } + if (adminInfo.server_ip) { + expandDataLocal.push({ + key: t('服务器IP'), + value: adminInfo.server_ip, + }); + } + if (adminInfo.version) { + expandDataLocal.push({ + key: t('系统版本'), + value: adminInfo.version, + }); + } + } expandDatesLocal[logs[i].key] = expandDataLocal; } diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index e273c38b..cd7a1bbd 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -9,27 +9,50 @@ " 吗?": "?", " 秒": "s", " 秒。": " seconds.", - "$/1M tokens": "$/1M tokens", + ",当前无生效订阅,将自动使用钱包": ", no active subscription. Wallet will be used automatically.", + ",时间:": ",time:", + ",点击更新": ", click Update", + "(共 {{total}} 个,省略 {{omit}} 个)": "", + "(共 {{total}} 个)": "", + "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(Currently only supports Epay interface, the default callback address is the server address above!)", "(筛选后显示 {{count}} 条)_one": "(Showing {{count}} item after filtering)", "(筛选后显示 {{count}} 条)_other": "(Showing {{count}} items after filtering)", "(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(Input {{input}} tokens / 1M tokens * {{symbol}}{{price}}", "(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(Input {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + Audio input {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}", "(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(Input {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + Cache {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}", "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(Input {{nonImageInput}} tokens + Image input {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}", + "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "The maximum value of [Maximum request count] and [Maximum request completion count] is 2147483647.", + "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[Maximum request count] must be greater than or equal to 0, [Maximum request completion count] must be greater than or equal to 1.", + "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", + "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", + "{{count}} 项操作_one": "", + "{{count}} 项操作_other": "", + "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", + "{{name}} ID": "{{name}} ID", + "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", + "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", + "「用户可选」决定用户创建令牌时能否自主选择该分组。未勾选的分组只能由管理员在后台分配给用户,不会出现在用户的令牌创建页面中。": "\"User Selectable\" controls whether users can choose this group when creating tokens. Unchecked groups can only be assigned by admin and won't appear in the token creation page.", + "• 视频服务商的跨域限制": "• Cross-origin limitations from the video provider", + "• 防盗链保护机制": "• Hotlink protection mechanisms", + "• 需要特定的请求头或认证": "• Specific headers or authentication are required", + "© {{currentYear}}": "© {{currentYear}}", + "| 基于": " | Based on ", + "$/1M tokens": "$/1M tokens", + "$/次": "", "0 - 最低": "0 - Lowest", "0 表示不限": "0 means unlimited", "0.002-1之间的小数": "Decimal between 0.002-1", "0.1以上的小数": "Decimal above 0.1", - "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。": "1) Click \"Open Authorization Page\" to complete login; 2) The browser will redirect to localhost (it's OK if the page doesn't load); 3) Copy the full URL from the address bar and paste it below; 4) Click \"Generate and Fill\".", "1. 管理员在此创建分组并设置倍率": "1. Admin creates groups and sets ratios here", + "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。": "1) Click \"Open Authorization Page\" to complete login; 2) The browser will redirect to localhost (it's OK if the page doesn't load); 3) Copy the full URL from the address bar and paste it below; 4) Click \"Generate and Fill\".", "10 - 最高": "10 - Highest", "1h缓存创建 {{price}} / 1M tokens": "1h cache creation {{price}} / 1M tokens", "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "1h cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "1h cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio: {{ratio}})", + "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "1h cache creation: {{tokens}} / 1M * model ratio {{modelRatio}} * 1h cache creation ratio {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", "1h缓存创建价格 {{symbol}}{{price}} / 1M tokens": "1h cache creation price {{symbol}}{{price}} / 1M tokens", "1h缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h缓存创建倍率: {{cacheCreationRatio1h}})": "1h cache creation price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h cache creation ratio: {{cacheCreationRatio1h}})", "1h缓存创建价格:{{symbol}}{{price}} / 1M tokens": "1h cache creation price: {{symbol}}{{price}} / 1M tokens", - "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "1h cache creation: {{tokens}} / 1M * model ratio {{modelRatio}} * 1h cache creation ratio {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", "2 - 低": "2 - Low", "2. 管理员在用户管理中将用户分配到对应分组(如 vip)": "2. Admin assigns users to groups in User Management (e.g., vip)", "2025年5月10日后添加的渠道,不需要再在部署的时候移除模型名称中的\".\"": "After May 10, 2025, channels added do not need to remove the dot in the model name during deployment", @@ -40,22 +63,25 @@ "5m缓存创建 {{price}} / 1M tokens": "5m cache creation {{price}} / 1M tokens", "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "5m cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "5m cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio: {{ratio}})", + "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "5m cache creation: {{tokens}} / 1M * model ratio {{modelRatio}} * 5m cache creation ratio {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", "5m缓存创建价格 {{symbol}}{{price}} / 1M tokens": "5m cache creation price {{symbol}}{{price}} / 1M tokens", "5m缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m缓存创建倍率: {{cacheCreationRatio5m}})": "5m cache creation price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m cache creation ratio: {{cacheCreationRatio5m}})", "5m缓存创建价格:{{symbol}}{{price}} / 1M tokens": "5m cache creation price: {{symbol}}{{price}} / 1M tokens", - "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "5m cache creation: {{tokens}} / 1M * model ratio {{modelRatio}} * 5m cache creation ratio {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", "8 - 高": "8 - High", "AGPL v3.0协议": "AGPL v3.0 License", "AI 对话": "AI Chat", "AI模型测试环境": "AI model testing environment", "AI模型配置": "AI model configuration", "AK/SK 模式:使用 AccessKey 和 SecretAccessKey;API Key 模式:使用 API Key": "AK/SK mode uses AccessKey and SecretAccessKey; API Key mode uses an API Key", + "anthropic-beta JSON 示例": "", "API Key": "API Key", "API Key 模式下不支持批量创建": "Batch creation not supported in API Key mode", "API Key 验证失败": "API Key verification failed", "API Key 验证成功!连接到 io.net 服务正常": "API Key verification successful! Connection to io.net service is normal", "API 地址和相关配置": "API URL and related configuration", "API 密钥": "API Key", + "API 密钥 (沙盒)": "", + "API 密钥 (生产)": "", "API 文档": "API Documentation", "API 配置": "API Configuration", "API令牌管理": "API token management", @@ -65,15 +91,14 @@ "API地址": "Base URL", "API渠道配置": "API channel configuration", "API端点": "API endpoints", - "Authorization Endpoint": "Authorization Endpoint", "Authorization callback URL 填": "Fill in the Authorization callback URL", + "Authorization Endpoint": "Authorization Endpoint", + "auto分组调用链路": "auto group call chain", "Available": "Available", "Bark推送URL": "Bark Push URL", "Bark推送URL必须以http://或https://开头": "Bark push URL must start with http:// or https://", "Bark通知": "Bark notification", "Basic Auth 头": "Basic Auth Header", - "CPU 使用率超过此值时拒绝请求": "Reject requests when CPU usage exceeds this value", - "CPU 阈值 (%)": "CPU Threshold (%)", "Cache Directory": "Cache Directory", "Cached tokens": "Cached tokens", "Cached tokens 占比口径由后端返回:Claude 语义按 cached/(prompt+cached),其余按 cached/prompt。": "Cached token ratio is returned by the backend: Claude calculates as cached/(prompt+cached), others as cached/prompt.", @@ -90,13 +115,20 @@ "Client Secret": "Client Secret", "Codex 授权": "Codex Authorization", "Codex 渠道不支持批量创建": "Codex channel does not support batch creation", + "common.changeLanguage": "Change Language", "Completion tokens": "Completion tokens", "Configuration": "Configuration", + "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。": "context_int/context_string reads from request context; gjson reads from the entry request JSON body using gjson path.", + "CPU 使用率超过此值时拒绝请求": "Reject requests when CPU usage exceeds this value", + "CPU 阈值 (%)": "CPU Threshold (%)", "Creem API 密钥,敏感信息不显示": "Creem API key, sensitive information not displayed", "Creem Setting Tips": "Creem only supports preset fixed-amount products. These products and their prices need to be created and configured in advance on the Creem website, so custom dynamic amount top-ups are not supported. Configure the product name and price on Creem, obtain the Product Id, and then fill it in for the product below. Set the top-up amount and display price for this product in the new API.", "Creem 介绍": "Creem is the payment partner you always deserved, we strive for simplicity and straightforwardness on our APIs.", "Creem 充值": "Creem Recharge", "Creem 设置": "Creem Setting", + "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。": "default and vip can only be assigned to users by admin in \"User Management\". Suitable for tiered pricing, internal testing, or other scenarios where user self-selection is not desired.", + "default为默认设置,可单独设置每个分类的安全等级": "\"default\" is the default setting, and each category can be set separately", + "default为默认设置,可单独设置每个模型的版本": "\"default\" is the default setting, and each model can be set separately", "Dify渠道只适配chatflow和agent,并且agent不支持图片!": "Dify channel only supports chatflow and agent, and agent does not support images!", "Discord": "Discord", "Discord Client ID": "Discord Client ID", @@ -106,12 +138,11 @@ "Discovery scopes": "Discovery scopes", "Discovery 建议 scopes:": "Recommended Discovery scopes:", "EUR (欧元)": "EUR (Euro)", + "false": "false", "GC execution failed": "GC execution failed", "GC 已执行": "GC executed", "GC 执行失败": "GC execution failed", "GC 次数": "GC Count", - "GPU/容器": "GPU/Container", - "GPU数量": "Number of GPUs", "Gemini安全设置": "Gemini safety settings", "Gemini思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Gemini thinking adaptation BudgetTokens = MaxTokens * BudgetTokens percentage", "Gemini思考适配设置": "Gemini thinking adaptation settings", @@ -126,10 +157,14 @@ "Gotify服务器地址": "Gotify server address", "Gotify服务器地址必须以http://或https://开头": "Gotify server address must start with http:// or https://", "Gotify通知": "Gotify notification", + "GPU/容器": "GPU/Container", + "GPU数量": "Number of GPUs", "Grok设置": "Grok Settings", "Haiku 模型": "Haiku Model", "Homepage URL 填": "Fill in the Homepage URL", "ID": "ID", + "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护": "include_obfuscation controls obfuscation fields in Responses stream. Disabled by default to prevent clients from disabling this security protection", + "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "The inference_geo field controls Claude's data residency inference region. Disabled by default to prevent unauthorized pass-through of geographic information", "IP": "IP", "IP白名单": "IP Whitelist", "IP白名单(支持CIDR表达式)": "IP whitelist (supports CIDR expressions)", @@ -157,8 +192,8 @@ "LinuxDO": "LinuxDO", "LinuxDO ID": "LinuxDO ID", "Logo 图片地址": "Logo image address", - "MIT许可证": "MIT License", "Midjourney 任务记录": "Midjourney Task Records", + "MIT许可证": "MIT License", "New API项目仓库地址:": "New API project repository address: ", "NewAPI 默认不会将入口请求的 User-Agent 透传到上游渠道;该条件仅用于识别访问本站点的客户端。": "NewAPI does not pass the incoming request's User-Agent to upstream channels by default; this condition is only used to identify clients accessing this site.", "OAuth Client ID": "OAuth Client ID", @@ -171,7 +206,6 @@ "Ollama 模型管理": "Ollama Model Management", "Ollama 版本信息": "Ollama Version Info", "Opus 模型": "Opus Model", - "POST 参数": "POST Parameters", "Passkey": "Passkey", "Passkey 已解绑": "Passkey removed", "Passkey 已重置": "Passkey has been reset", @@ -179,52 +213,73 @@ "Passkey 注册失败,请重试": "Passkey registration failed. Please try again.", "Passkey 注册成功": "Passkey registration successful", "Passkey 登录": "Passkey Login", + "Pay Method Name": "", + "Pay Method Type": "", "Ping间隔(秒)": "Ping Interval (seconds)", + "POST 参数": "POST Parameters", + "price_xxx 的商品价格 ID,新建产品后可获得": "Product price ID for price_xxx, available after creating new product", "Prompt cache hit tokens": "Prompt cache hit tokens", "Prompt tokens": "Prompt tokens", "Reasoning Effort": "Reasoning Effort", "Recharge Quota": "Recharge Quota", "Request ID": "Request ID", + "RSA 私钥 (沙盒)": "", + "RSA 私钥 (生产)": "", + "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "The safety_identifier field helps OpenAI identify application users who may violate usage policies. Disabled by default to protect user privacy", + "Scopes(可选)": "Scopes (optional)", + "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "The service_tier field is used to specify service level. Allowing pass-through may result in higher billing than expected. Disabled by default to avoid extra charges", + "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "Stripe key for sk_xxx or rk_xxx, sensitive information not displayed", "SMTP 发送者邮箱": "SMTP Sender Email", "SMTP 服务器地址": "SMTP Server Address", "SMTP 端口": "SMTP Port", "SMTP 访问凭证": "SMTP Access Credential", "SMTP 账户": "SMTP Account", + "Sonnet 模型": "Sonnet Model", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "The speed field controls Claude inference speed mode. Disabled by default to avoid unintentionally switching to fast mode", "SSE 事件": "SSE Events", "SSE数据流": "SSE Stream", "SSRF防护开关详细说明": "Master switch controls whether SSRF protection is enabled. When disabled, all SSRF checks are bypassed, allowing access to any URL. ⚠️ Only disable this feature in completely trusted environments.", "SSRF防护设置": "SSRF Protection Settings", "SSRF防护详细说明": "SSRF protection prevents malicious users from using your server to access internal network resources. Configure whitelists for trusted domains/IPs and restrict allowed ports. Applies to file downloads, webhooks, and notifications.", - "Scopes(可选)": "Scopes (optional)", - "Sonnet 模型": "Sonnet Model", + "standard 已被移除,vip 用户看不到": "standard has been removed, vip users cannot see it", + "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "The store field authorizes OpenAI to store request data for product evaluation and optimization. Disabled by default. Enabling may cause Codex to malfunction", "Stripe 设置": "Stripe Settings", "Stripe/Creem 商品ID(可选)": "Stripe/Creem Product ID (optional)", "Stripe/Creem 需在第三方平台创建商品并填入 ID": "Stripe/Creem products must be created on the third-party platform and the ID filled in", - "TTL(秒)": "TTL (seconds)", - "TTL(秒,0 表示默认)": "TTL (seconds, 0 for default)", "Telegram": "Telegram", "Telegram Bot Token": "Telegram Bot Token", "Telegram Bot 名称": "Telegram Bot Name", "Telegram ID": "Telegram ID", "Token Endpoint": "Token Endpoint", + "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。": "Tokens are converted to quota/usage count by ratio. After the request completes, the difference is settled (additional deduction/refund).", "Total tokens": "Total tokens", + "true": "true", + "TTL(秒,0 表示默认)": "TTL (seconds, 0 for default)", + "TTL(秒)": "TTL (seconds)", "Turnstile Secret Key": "Turnstile Secret Key", "Turnstile Site Key": "Turnstile Site Key", - "URL 标识,只能包含小写字母、数字和连字符": "URL identifier, only lowercase letters, numbers, and hyphens allowed", - "URL链接": "URL Link", - "USD (美元)": "USD (US Dollar)", "Unix时间戳": "Unix timestamp", "Uptime Kuma地址": "Uptime Kuma Address", "Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)": "Uptime Kuma monitoring category management, you can configure multiple monitoring categories for service status display (maximum 20)", + "URL 标识,只能包含小写字母、数字和连字符": "URL identifier, only lowercase letters, numbers, and hyphens allowed", + "URL链接": "URL Link", + "USD (美元)": "USD (US Dollar)", "User Info Endpoint": "User Info Endpoint", "User-Agent include(每行一个,可不写)": "User-Agent include (one per line, optional)", "Value 正则": "Value Regex", "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AI does not support the functionResponse.id field. When enabled, this field will be automatically removed", - "WeChat Server 服务器地址": "WeChat Server Address", - "WeChat Server 访问凭证": "WeChat Server Access Credential", + "Waffo API 参数,可空,例如:CREDITCARD,DEBITCARD(最多64位)": "", + "Waffo API 参数,可空(最多64位)": "", + "Waffo 充值": "", + "Waffo 充值的最低数量,默认 1": "", + "Waffo 公钥 (沙盒)": "", + "Waffo 公钥 (生产)": "", + "Waffo 商户 ID": "", + "Waffo 是一个支付聚合平台,支持多种支付方式。": "", + "Waffo 设置": "", "Web 搜索 {{count}} 次 * {{symbol}}{{price}} / 1K 次": "Web search {{count}} calls * {{symbol}}{{price}} / 1K calls", - "Web 搜索调用 {{webSearchCallCount}} 次": "Web search called {{webSearchCallCount}} times", "Web 搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Web search: {{count}} / 1K * unit price {{price}} * {{ratioType}} {{ratio}} = {{amount}}", + "Web 搜索调用 {{webSearchCallCount}} 次": "Web search called {{webSearchCallCount}} times", "Webhook 密钥": "Webhook Secret", "Webhook 签名密钥": "Webhook Signature Key", "Webhook地址": "Webhook URL", @@ -232,62 +287,38 @@ "Webhook请求结构说明": "Webhook request structure description", "Webhook通知": "Webhook notification", "Web搜索价格:{{symbol}}{{price}} / 1K 次": "Web Search Price: {{symbol}}{{price}} / 1K requests", + "WeChat Server 服务器地址": "WeChat Server Address", + "WeChat Server 访问凭证": "WeChat Server Access Credential", "Well-Known URL": "Well-Known URL", "Well-Known URL 必须以 http:// 或 https:// 开头": "Well-Known URL must start with http:// or https://", + "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示": "Webhook signature key for whsec_xxx, sensitive information not displayed", "Worker地址": "Worker Address", "Worker密钥": "Worker Key", - "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "The maximum value of [Maximum request count] and [Maximum request completion count] is 2147483647.", - "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[Maximum request count] must be greater than or equal to 0, [Maximum request completion count] must be greater than or equal to 1.", - "auto分组调用链路": "auto group call chain", - "common.changeLanguage": "Change Language", - "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。": "context_int/context_string reads from request context; gjson reads from the entry request JSON body using gjson path.", - "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。": "default and vip can only be assigned to users by admin in \"User Management\". Suitable for tiered pricing, internal testing, or other scenarios where user self-selection is not desired.", - "default为默认设置,可单独设置每个分类的安全等级": "\"default\" is the default setting, and each category can be set separately", - "default为默认设置,可单独设置每个模型的版本": "\"default\" is the default setting, and each model can be set separately", - "false": "false", - "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护": "include_obfuscation controls obfuscation fields in Responses stream. Disabled by default to prevent clients from disabling this security protection", - "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "The inference_geo field controls Claude's data residency inference region. Disabled by default to prevent unauthorized pass-through of geographic information", - "price_xxx 的商品价格 ID,新建产品后可获得": "Product price ID for price_xxx, available after creating new product", - "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "The safety_identifier field helps OpenAI identify application users who may violate usage policies. Disabled by default to protect user privacy", - "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "The service_tier field is used to specify service level. Allowing pass-through may result in higher billing than expected. Disabled by default to avoid extra charges", - "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "The speed field controls Claude inference speed mode. Disabled by default to avoid unintentionally switching to fast mode", - "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "Stripe key for sk_xxx or rk_xxx, sensitive information not displayed", - "standard 已被移除,vip 用户看不到": "standard has been removed, vip users cannot see it", - "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "The store field authorizes OpenAI to store request data for product evaluation and optimization. Disabled by default. Enabling may cause Codex to malfunction", - "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。": "Tokens are converted to quota/usage count by ratio. After the request completes, the difference is settled (additional deduction/refund).", - "true": "true", - "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示": "Webhook signature key for whsec_xxx, sensitive information not displayed", - "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", - "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", - "{{name}} ID": "{{name}} ID", - "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", - "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", - "| 基于": " | Based on ", - "© {{currentYear}}": "© {{currentYear}}", - "• 视频服务商的跨域限制": "• Cross-origin limitations from the video provider", - "• 防盗链保护机制": "• Hotlink protection mechanisms", - "• 需要特定的请求头或认证": "• Specific headers or authentication are required", - "「用户可选」决定用户创建令牌时能否自主选择该分组。未勾选的分组只能由管理员在后台分配给用户,不会出现在用户的令牌创建页面中。": "\"User Selectable\" controls whether users can choose this group when creating tokens. Unchecked groups can only be assigned by admin and won't appear in the token creation page.", "一个月": "A month", "一天": "One day", "一小时": "One hour", "一次调用消耗多少刀,优先级大于模型倍率": "How much USD one call costs, priority over model ratio", - "一行一个屏蔽词,不需要符号分割": "One line per sensitive word, no symbols are required", "一行一个,不区分大小写": "One line per keyword, not case-sensitive", + "一行一个屏蔽词,不需要符号分割": "One line per sensitive word, no symbols are required", "一键填充到 FluentRead": "One-click fill to FluentRead", "三种操作的区别:": "Differences between the three operations:", "上一个表单块": "Previous form block", "上一步": "Previous", + "上传 PNG/JPG/SVG 图片,建议尺寸 ≤ 128×128px": "", + "上传图片": "", "上次保存: ": "Last saved: ", + "上次检测到可加入模型": "", + "上次检测时间": "", "上游倍率同步": "Upstream ratio synchronization", "上游模型管理": "Upstream Model Management", "上游返回": "Upstream response", "下一个表单块": "Next form block", + "下一次重置": "Next reset", "下一步": "Next", "下午好": "Good afternoon", "下载日志": "Download Logs", "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "The fields below show which backend values will be written after saving, so you can keep them aligned with the raw JSON editors.", + "下面是带注释的示例,仅用于参考;实际保存时请删除注释。": "", "不会出现": "will not appear", "不再提醒": "Do not remind again", "不勾选用户可选": "Not user-selectable", @@ -312,10 +343,10 @@ "两次输入的密码不一致": "The two passwords entered do not match", "两次输入的密码不一致!": "The passwords entered twice are inconsistent!", "两步验证": "Two-Factor Authentication", + "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。": "Two-factor authentication (2FA) provides additional security protection for your account. After enabling, you need to enter your password and the verification code generated by the authenticator application when logging in.", "两步验证启用成功!": "Two-factor authentication enabled successfully!", "两步验证已禁用": "Two-factor authentication has been disabled", "两步验证设置": "Two-factor authentication settings", - "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。": "Two-factor authentication (2FA) provides additional security protection for your account. After enabling, you need to enter your password and the verification code generated by the authenticator application when logging in.", "个": "individual", "个GPU": " GPUs", "个人中心": "Personal center", @@ -372,6 +403,7 @@ "仅对自定义模型有效": "Only effective for custom models", "仅当前层": "Current level only", "仅当自动禁用开启时有效,关闭后不会自动禁用该渠道": "Only effective when automatic disabling is enabled, after closing, the channel will not be automatically disabled", + "仅提交已勾选": "", "仅支持": "Only supports", "仅支持 JSON 对象,必须包含 access_token 与 account_id": "Only JSON objects are supported, must include access_token and account_id", "仅支持 JSON 文件": "Only JSON files are supported", @@ -379,14 +411,17 @@ "仅支持 OpenAI 接口格式": "Only OpenAI interface format is supported", "仅显示已绑定": "Show bound only", "仅显示矛盾倍率": "Only show conflicting ratios", + "仅检测上游模型更新": "", "仅用于开发环境,生产环境应使用 HTTPS": "For development only, use HTTPS in production", "仅用于换算,实际保存的是额度": "For conversion only, quota is what gets saved", "仅用订阅": "Subscription only", "仅用钱包": "Wallet only", + "仅管理员可用。开启后,当系统定时检测全部渠道发现上游模型变更或检测异常时,将按你选择的通知方式发送汇总通知;渠道或模型过多时会自动省略部分明细。": "", "仅重置配置": "Reset configuration only", "今日关闭": "Close Today", "今日已签到": "Checked in today", "今日已签到,累计签到": "Checked in today, total check-ins", + "仍有未处理项": "", "从 0.5 降到 0.3": "reduced from 0.5 to 0.3", "从剪贴板粘贴配置": "Paste Config", "从官方模型库同步": "Sync from official model library", @@ -397,10 +432,11 @@ "代理设置": "Proxy Settings", "代码已复制到剪贴板": "Code copied to clipboard", "令牌": "Tokens", + "令牌不存在": "", "令牌分组": "Token Group", + "令牌分组,默认为用户的分组": "Token group, default is your group", "令牌分组设为 auto 时,按以下顺序依次尝试选择可用分组,排在前面的优先级更高": "When token group is set to auto, groups are selected in order of priority, with higher priority groups listed first", "令牌分组设为 auto 时,系统按优先级顺序自动选择一个可用分组。": "When token group is set to auto, the system automatically selects an available group by priority.", - "令牌分组,默认为用户的分组": "Token group, default is your group", "令牌创建成功,请在列表页面点击复制获取令牌!": "Token created successfully, please click copy on the list page to get the token!", "令牌名称": "Token Name", "令牌已重置并已复制到剪贴板": "Token has been reset and copied to clipboard", @@ -413,8 +449,11 @@ "以及": "and", "仪表盘设置": "Dashboard", "价格": "Pricing", + "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "Price: {{symbol}}{{price}} * {{ratioType}}: {{ratio}}", + "价格:${{price}} * {{ratioType}}:{{ratio}}": "Price: ${{price}} * {{ratioType}}: {{ratio}}", "价格摘要": "Price Summary", "价格暂时不可用,请稍后重试": "Price temporarily unavailable, please try again later", + "价格模式": "", "价格模式(默认)": "Price Mode (Default)", "价格计算中...": "Calculating price...", "价格计算失败": "Price calculation failed", @@ -423,8 +462,6 @@ "价格设置方式": "Pricing configuration method", "价格重新计算中...": "Recalculating price...", "价格预估": "Price Estimate", - "价格:${{price}} * {{ratioType}}:{{ratio}}": "Price: ${{price}} * {{ratioType}}: {{ratio}}", - "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "Price: {{symbol}}{{price}} * {{ratioType}}: {{ratio}}", "任一满足(OR)": "Any match (OR)", "任务 ID": "Task ID", "任务ID": "Task ID", @@ -441,7 +478,6 @@ "余额充值管理": "Balance recharge management", "作废": "Invalidate", "作废于": "Invalidated at", - "下一次重置": "Next reset", "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "After invalidation, the subscription becomes invalid immediately. History is not affected. Continue?", "作用域": "Scope", "作用域:包含分组": "Scope: Include Group", @@ -449,6 +485,9 @@ "作用域:包含规则名称": "Scope: Include Rule Name", "你似乎并没有修改什么": "You seem to have not modified anything", "你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "You can manually add them under “Custom model names”, click Fill and submit, or use the actions below to handle them automatically.", + "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?_one": "", + "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?_other": "", + "使用 {{name}} 继续": "Continue with {{name}}", "使用 Discord 继续": "Continue with Discord", "使用 GitHub 继续": "Continue with GitHub", "使用 JSON 对象格式,格式为:{\"组名\": [最多请求次数, 最多请求完成次数]}": "Use JSON object format, format: {\"group_name\": [max_requests, max_completions]}", @@ -457,43 +496,43 @@ "使用 Passkey 实现免密且更安全的登录体验": "Use Passkey for password-free and more secure login experience", "使用 Passkey 登录": "Sign in with Passkey", "使用 Passkey 验证": "Verify with Passkey", - "使用 {{name}} 继续": "Continue with {{name}}", "使用 微信 继续": "Continue with WeChat", "使用 用户名 注册": "Sign up with Username", "使用 邮箱或用户名 登录": "Sign in with Email or Username", "使用ID排序": "Sort by ID", "使用分组": "Using Group", + "使用原生额度输入": "Use raw quota input", "使用日志": "Usage Logs", "使用模式": "Usage mode", "使用统计": "Usage Statistics", - "使用认证器应用扫描二维码": "Scan QR code with authenticator app", "使用认证器应用(如 Google Authenticator、Microsoft Authenticator)扫描下方二维码:": "Use an authenticator app (such as Google Authenticator, Microsoft Authenticator) to scan the QR code below:", + "使用认证器应用扫描二维码": "Scan QR code with authenticator app", "使用说明": "Guide", "例如": "e.g.", "例如 /var/cache/new-api": "e.g. /var/cache/new-api", - "例如 https://docs.newapi.pro": "E.g., https://docs.newapi.pro", "例如 €, £, Rp, ₩, ₹...": "For example, €, £, Rp, ₩, ₹...", + "例如 https://docs.newapi.pro": "E.g., https://docs.newapi.pro", + "例如 https://example.com/api/waffo/webhook": "", + "例如 https://example.com/console/topup": "", + "例如:": "For example:", "例如: /bin/bash -c \"python app.py\"": "e.g.: /bin/bash -c \"python app.py\"", "例如: nginx:latest": "e.g.: nginx:latest", "例如: socks5://user:pass@host:port": "e.g.: socks5://user:pass@host:port", - "例如发卡网站的购买链接": "E.g., purchase link from card issuing website", - "例如(全渠道):": "Example (all channels):", - "例如(指定渠道):": "Example (specific channels):", - "例如:": "For example:", "例如:-c": "e.g.: -c", "例如:/bin/bash": "e.g.: /bin/bash", "例如:0001": "e.g.: 0001", "例如:1000": "e.g.: 1000", "例如:100000": "e.g.: 100000", - "例如:2000": "e.g.: 2000", "例如:2,就是最低充值2$": "e.g.: 2, means minimum top-up is $2", + "例如:2000": "e.g.: 2000", "例如:4.99": "e.g.: 4.99", "例如:401, 403, 429, 500-599": "e.g. 401,403,429,500-599", "例如:7,就是7元/美金": "e.g.: 7, means 7 yuan per USD", - "例如:GitHub Enterprise": "e.g.: GitHub Enterprise", + "例如:Credit Card": "", "例如:email": "e.g.: email", "例如:example.com": "e.g.: example.com", "例如:github / si:google / https://example.com/logo.png / 🐱": "e.g.: github / si:google / https://example.com/logo.png / 🐱", + "例如:GitHub Enterprise": "e.g.: GitHub Enterprise", "例如:github-enterprise": "e.g.: github-enterprise", "例如:gpt-4.1-nano,regex:^claude-.*$,regex:^sora-.*$": "Example: gpt-4.1-nano,regex:^claude-.*$,regex:^sora-.*$", "例如:https://example.com/.well-known/openid-configuration": "e.g.: https://example.com/.well-known/openid-configuration", @@ -506,9 +545,13 @@ "例如:prod_6I8rBerHpPxyoiU9WK4kot": "e.g.: prod_6I8rBerHpPxyoiU9WK4kot", "例如:sub、id、data.user.id": "e.g.: sub, id, data.user.id", "例如:基础套餐": "e.g.: Basic Package", + "例如:清理工具参数,避免上游校验错误": "", "例如:该请求不满足准入策略": "e.g.: This request does not meet the admission policy", "例如:适合轻度使用": "e.g.: Suitable for light usage", "例如:需要等级 {{required}},你当前等级 {{current}}": "e.g.: Required level {{required}}, your current level is {{current}}", + "例如(全渠道):": "Example (all channels):", + "例如(指定渠道):": "Example (specific channels):", + "例如发卡网站的购买链接": "E.g., purchase link from card issuing website", "供应商": "Provider", "供应商介绍": "Provider introduction", "供应商信息:": "Provider information:", @@ -519,6 +562,8 @@ "供应商更新成功!": "Provider updated successfully!", "侧边栏管理(全局控制)": "Sidebar Management (Global Control)", "侧边栏设置保存成功": "Sidebar settings saved successfully", + "保前缀": "", + "保后缀": "", "保存": "Save", "保存 Discord OAuth 设置": "Save Discord OAuth Settings", "保存 GitHub OAuth 设置": "Save GitHub OAuth Settings", @@ -534,8 +579,8 @@ "保存备用码": "Save backup codes", "保存备用码以备不时之需": "Save backup codes for emergencies", "保存失败": "Save failed", - "保存失败:": "Save failed:", "保存失败,请重试": "Save failed, please try again", + "保存失败:": "Save failed:", "保存屏蔽词过滤设置": "Save sensitive word filtering settings", "保存性能设置": "Save Performance Settings", "保存成功": "Saved successfully", @@ -553,11 +598,11 @@ "保存预览": "Save Preview", "保存额度设置": "Save Quota Settings", "保留": "kept", + "保留最近N个文件": "Retain last N files", + "保留最近N天": "Retain last N days", "保留原值(目标已有值时不覆盖)": "Keep original value (do not overwrite if target already has a value)", "保留天数": "Days to Retain", "保留文件数": "Files to Retain", - "保留最近N个文件": "Retain last N files", - "保留最近N天": "Retain last N days", "修复数据库一致性": "Fix database consistency", "修改为": "Modify to", "修改子渠道优先级": "Modify sub-channel priority", @@ -569,6 +614,7 @@ "倍率信息": "Ratio information", "倍率是为了方便换算不同价格的模型": "The magnification is to facilitate the conversion of models with different prices.", "倍率模式": "Ratio Mode", + "倍率模式(默认)": "", "倍率用于计费乘数,勾选「用户可选」后用户可在创建令牌时选择该分组": "Ratio is the billing multiplier. Check \"User Selectable\" to let users pick this group when creating tokens", "倍率类型": "Ratio type", "假设再加两个分组 default 和 vip,但不勾选用户可选:": "Now add two more groups default and vip, but without checking User Selectable:", @@ -578,12 +624,12 @@ "停用": "Deactivate", "允许 AccountFilter 参数": "Allow AccountFilter parameter", "允许 HTTP 协议图片请求(适用于自部署代理)": "Allow HTTP protocol image requests (for self-deployed proxies)", - "允许 Turnstile 用户校验": "Allow Turnstile user verification", "允许 inference_geo 透传": "Allow inference_geo Pass-through", "允许 safety_identifier 透传": "Allow safety_identifier Pass-through", "允许 service_tier 透传": "Allow service_tier Pass-through", "允许 speed 透传": "Allow speed Pass-through", "允许 stream_options.include_obfuscation 透传": "Allow stream_options.include_obfuscation Pass-through", + "允许 Turnstile 用户校验": "Allow Turnstile user verification", "允许不安全的 Origin(HTTP)": "Allow insecure Origin (HTTP)", "允许回调(会泄露服务器 IP 地址)": "Allow callback (will leak server IP address)", "允许在 Stripe 支付中输入促销码": "Allow entering promotion codes during Stripe checkout", @@ -604,13 +650,13 @@ "允许重试": "Allow Retry", "元": "CNY", "充值": "Top Up", - "充值价格显示": "Top Up price", "充值价格(x元/美金)": "Top Up price (x yuan/dollar)", + "充值价格显示": "Top Up price", "充值分组倍率": "Top Up group ratio", "充值分组倍率不是合法的 JSON 字符串": "Top Up group ratio is not a valid JSON string", "充值数量": "Top Up quantity", - "充值数量不能小于": "The top up amount cannot be less than", "充值数量,最低 ": "Top Up quantity, minimum", + "充值数量不能小于": "The top up amount cannot be less than", "充值方式设置": "Top Up method settings", "充值方式设置不是合法的 JSON 字符串": "Top Up method settings is not a valid JSON string", "充值确认": "Top Up confirmation", @@ -626,8 +672,8 @@ "兑换成功!": "Redemption successful!", "兑换码充值": "Redemption code recharge", "兑换码创建成功": "Redemption Code Created", - "兑换码创建成功!": "Redemption code created successfully!", "兑换码创建成功,是否下载兑换码?": "Redemption code created successfully. Do you want to download it?", + "兑换码创建成功!": "Redemption code created successfully!", "兑换码将以文本文件的形式下载,文件名为兑换码的名称。": "The redemption code will be downloaded as a text file, with the filename being the redemption code name.", "兑换码更新成功!": "Redemption code updated successfully!", "兑换码生成管理": "Redemption code generation management", @@ -636,6 +682,7 @@ "全局控制侧边栏区域和功能显示,管理员隐藏的功能用户无法启用": "Global control of sidebar areas and functions, users cannot enable functions hidden by administrators", "全局设置": "Global Settings", "全选": "Select all", + "全选当前列表模型": "", "全部": "All", "全部供应商": "All vendors", "全部分组": "All groups", @@ -674,8 +721,8 @@ "关闭": "Close", "关闭侧边栏": "Close sidebar", "关闭公告": "Close Notice", - "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "After closing, this notice will no longer be shown (only for this browser). Are you sure you want to close it?", "关闭后,此模型将不会被“同步官方”自动覆盖或创建": "After closing, this model will not be automatically overwritten or created by \"Sync Official\"", + "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "After closing, this notice will no longer be shown (only for this browser). Are you sure you want to close it?", "关闭弹窗,已停止批量测试": "Dialog closed, batch testing stopped", "关闭提示": "Close notice", "其他": "Other", @@ -693,11 +740,25 @@ "内置": "Built-in", "内置 Ollama 镜像": "Built-in Ollama Image", "再次输入部署名称": "Enter Deployment Name Again", + "最低": "lowest", + "最低充值数量": "", + "最低充值美元数量": "Minimum recharge dollar amount", + "最后使用时间": "Last used time", + "最后更新": "Last Updated", + "最后请求": "Last request", + "最大GPU数量": "Max Number of GPUs", + "最大可用": "Max Available", + "最大条目数": "Max Entries", + "最终抵扣": "Final Deduction", + "最近一次": "Last", + "最近事件": "Recent Events", + "最高优先级": "highest priority", "写": "Write", "准入策略": "Admission Policy", "准入策略 JSON(可选)": "Admission Policy JSON (optional)", "准备中...": "Preparing...", "准备完成初始化": "Ready to complete initialization", + "减少": "Subtract", "凭证已刷新": "Credentials Refreshed", "分类名称": "Category Name", "分组": "Group", @@ -750,17 +811,20 @@ "创建用户": "Create User", "初始化失败,请重试": "Initialization failed, please retry", "初始化系统": "Initialize system", + "删请求头": "", "删除": "Delete", "删除 Key 来源": "Delete Key Source", "删除会彻底移除该订阅记录(含权益明细)。是否继续?": "Deletion will permanently remove this subscription record (including benefit details). Continue?", "删除后无法恢复,确定要删除模型 \"{{name}}\" 吗?": "Cannot be recovered after deletion, are you sure you want to delete model \"{{name}}\"?", "删除失败": "Delete failed", "删除密钥失败": "Failed to delete key", + "删除已选 {{selected}} / {{total}}": "", "删除成功": "Delete successful", "删除所选": "Delete Selected", "删除所选令牌": "Delete selected token", "删除所选通道": "Delete selected channels", "删除条件": "Delete Condition", + "删除模型": "", "删除禁用密钥失败": "Failed to delete disabled keys", "删除禁用通道": "Delete disabled channels", "删除自动禁用密钥": "Delete auto disabled keys", @@ -777,19 +841,20 @@ "刷新缓存统计": "Refresh Cache Statistics", "刷新缓存统计失败": "Failed to refresh cache statistics", "刷新页面": "Reload Page", + "前:": "Before:", "前往 io.net API Keys": "Go to io.net API Keys", "前往设置": "Go to Settings", "前往设置页面": "Go to Settings Page", "前缀": "Prefix", - "前:": "Before:", + "前置": "", + "剪贴板中未检测到连接信息": "No connection info found in clipboard", + "副本数量": "Number of Replicas", "剩余": "Remaining", "剩余备用码:": "Remaining backup codes: ", "剩余时间": "Remaining Time", "剩余额度": "Remaining quota", - "剩余额度$": "Remaining quota $", "剩余额度/总额度": "Remaining/Total", - "剪贴板中未检测到连接信息": "No connection info found in clipboard", - "副本数量": "Number of Replicas", + "剩余额度$": "Remaining quota $", "功能特性": "Features", "加入渠道": "Join Channel", "加入预填组": "Join Pre-filled Group", @@ -821,6 +886,7 @@ "区域": "Region", "升级分组": "Upgrade Group", "单GPU小时费率": "Per GPU Hour Rate", + "单价 (USD)": "", "历史消耗": "Consumption", "原价": "Original price", "原价,和普通用户一样": "original price, same as regular users", @@ -828,8 +894,10 @@ "原密码": "Original Password", "原生格式": "Native format", "原生额度": "Raw quota", - "使用原生额度输入": "Use raw quota input", - "收起原生额度输入": "Hide raw quota input", + "去前缀": "", + "去后缀": "", + "去处理{{type}}": "", + "去空格": "", "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "Deduplication completed: {{before}} keys before deduplication, {{after}} keys after deduplication", "参与官方同步": "Participate in official sync", "参数": "parameter", @@ -837,11 +905,13 @@ "参数值": "Parameter value", "参数覆盖": "Parameters override", "参数覆盖 JSON 已复制": "Parameter override JSON copied", + "参数覆盖已复制": "", "参数覆盖必须是合法的 JSON 对象": "Parameter override must be a valid JSON object", "参数覆盖必须是合法的 JSON 格式!": "Parameter override must be in valid JSON format!", "参数覆盖模板": "Parameter Override Template", "参数覆盖模板 JSON 格式不正确": "Parameter override template JSON format is incorrect", "参数覆盖模板预览": "Parameter Override Template Preview", + "参数覆盖详情": "", "参数配置": "Parameter Configuration", "参数配置有误": "Invalid parameter configuration", "参数错误": "Parameter Error", @@ -855,6 +925,7 @@ "取消全选": "Deselect all", "取消选择": "Deselect", "变换": "Transform", + "变更": "Change", "变焦": "zoom", "变量值": "Variable Value", "变量名": "Variable Name", @@ -865,6 +936,7 @@ "可以根据用户分组增减令牌可选的分组范围。例如 vip 用户额外开放 premium 分组,或移除某个分组的选择权。": "Adjust the selectable group range based on user group. E.g., grant vip users access to premium, or remove access to a group.", "可以根据用户分组设置不同的计费倍率。例如 vip 用户使用 standard 令牌时倍率从 1.0 降为 0.8。": "Set different billing ratios based on user group. E.g., vip users using standard tokens get ratio 0.8 instead of 1.0.", "可信": "Reliable", + "可勾选需要执行的变更:新增会加入渠道模型列表,删除会从渠道模型列表移除。": "", "可在设置页面设置关于内容,支持 HTML & Markdown": "The About content can be set on the settings page, supporting HTML & Markdown", "可手动填写,多个 scope 用空格分隔": "Can be filled in manually, separate multiple scopes with spaces", "可用": "Available", @@ -880,14 +952,15 @@ "可视化": "Visualization", "可视化倍率设置": "Visual model ratio settings", "可视化编辑": "Visual editing", - "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。": "Optional. Match the incoming request's User-Agent; any line matched as a substring (case-insensitive) counts as a hit.", - "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。": "Optional. Validate the extracted affinity key with regex; leave empty to skip validation.", - "可选。对请求路径进行匹配;不填表示匹配所有路径。": "Optional. Match the request path; leave empty to match all paths.", - "可选值": "Optional value", + "可空": "", "可选,公告的补充说明": "Optional, additional information for the notice", "可选,用于复现结果": "Optional, for reproducibility", "可选:基于用户信息 JSON 做组合条件准入,条件不满足时返回自定义提示": "Optional: Admission based on combined conditions from user info JSON; returns custom message when conditions are not met", "可选:用于自动生成端点或 Discovery URL": "Optional: Used to auto-generate endpoints or Discovery URL", + "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。": "Optional. Match the incoming request's User-Agent; any line matched as a substring (case-insensitive) counts as a hit.", + "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。": "Optional. Validate the extracted affinity key with regex; leave empty to skip validation.", + "可选。对请求路径进行匹配;不填表示匹配所有路径。": "Optional. Match the request path; leave empty to match all paths.", + "可选值": "Optional value", "合计:{{total}}": "Total: {{total}}", "合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "Total: text {{textTotal}} + audio {{audioTotal}} = {{total}}", "同时重置消息": "Reset messages simultaneously", @@ -895,6 +968,7 @@ "同步到渠道": "Sync to Channel", "同步向导": "Sync Wizard", "同步失败": "Synchronization failed", + "同步字段": "", "同步成功": "Synchronization successful", "同步接口": "Synchronization interface", "同步渠道失败": "Failed to sync channel", @@ -904,10 +978,11 @@ "名称+密钥": "Name + key", "名称不能为空": "Name cannot be empty", "名称匹配类型": "Name matching type", + "后:": "After:", + "后端固定": "", "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "Backend fixed ratio: {{ratio}}. This field only displays the converted price.", "后端请求失败": "Backend request failed", "后缀": "Suffix", - "后:": "After:", "向右展开": "Expand right", "向左展开": "Expand left", "否": "No", @@ -920,17 +995,17 @@ "启动部署失败": "Failed to start deployment", "启动配置": "Startup Configuration", "启用": "Enable", - "启用 Prompt 检查": "Enable Prompt check", "启用 io.net 部署": "Enable io.net Deployment", "启用 io.net 部署开关": "Enable io.net Deployment Switch", "启用 io.net 部署时必须填写 API Key": "API Key is required when enabling io.net deployment", + "启用 Prompt 检查": "Enable Prompt check", + "启用 Waffo": "", "启用2FA失败": "Failed to enable Two-Factor Authentication", "启用Claude思考适配(-thinking后缀)": "Enable Claude thinking adaptation (-thinking suffix)", "启用FunctionCall思维签名填充": "Enable FunctionCall thoughtSignature fill", "启用Gemini思考后缀适配": "Enable Gemini thinking suffix adaptation", "启用Ping间隔": "Enable Ping interval", "启用SMTP SSL": "Enable SMTP SSL", - "强制使用 AUTH LOGIN": "Force AUTH LOGIN", "启用SSRF防护(推荐开启以保护服务器安全)": "Enable SSRF Protection (Recommended for server security)", "启用供应商": "Enable Provider", "启用全部": "Enable all", @@ -939,6 +1014,7 @@ "启用后套餐将在用户端展示。是否继续?": "After enabling, the plan will be shown to users. Continue?", "启用后将优先复用上一次成功的渠道(粘滞选路)。": "When enabled, the last successful channel will be reused preferentially (sticky routing).", "启用后将使用 Creem Test Mode": "Use Creem Test Mode after enabling", + "启用后将使用 Waffo 沙盒环境": "", "启用密钥失败": "Failed to enable key", "启用屏蔽词过滤功能": "Enable sensitive word filtering function", "启用性能监控": "Enable Performance Monitoring", @@ -968,10 +1044,14 @@ "响应缺少凭据": "Response missing credentials", "响应缺少授权链接": "Response missing authorization link", "商品价格 ID": "Product Price ID", + "商户 ID": "", "回答内容": "Answer Content", "回调 URL 填": "Callback URL Fill", "回调 URL 格式": "Callback URL format", "回调地址": "Callback address", + "回调支付方式": "Callback Payment Method", + "回调调用者IP": "Callback Caller IP", + "回调通知地址": "", "固定价格": "Fixed Price", "固定价格(每次)": "Fixed Price (per use)", "固定价格值": "Fixed Price Value", @@ -979,24 +1059,26 @@ "图标": "Icon", "图标使用 react-icons(Simple Icons)或 URL/emoji,例如:github、gitlab、si:google": "Icon uses react-icons (Simple Icons) or URL/emoji, e.g.: github, gitlab, si:google", "图标使用@lobehub/icons库,如:OpenAI、Claude.Color,支持链式参数:OpenAI.Avatar.type={'platform'}、OpenRouter.Avatar.shape={'square'},查询所有可用图标请 ": "The icon uses the @lobehub/icons library, such as: OpenAI, Claude.Color, supports chain parameters: OpenAI.Avatar.type={'platform'}, OpenRouter.Avatar.shape={'square'}, query all available icons please ", + "图标文件不能超过 100KB,请压缩后重新上传": "", "图混合": "Blend", "图片倍率 {{imageRatio}}": "Image ratio {{imageRatio}}", "图片功能在自定义请求体模式下不可用": "Image functionality is not available in custom request body mode", "图片地址": "Image URL", "图片已添加": "Image Added", - "图片生成调用:{{symbol}}{{price}} / 1次": "Image generation call: {{symbol}}{{price}} / 1 time", "图片生成:1 次 * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Image generation: 1 call * unit price {{price}} * {{ratioType}} {{ratio}} = {{amount}}", + "图片生成调用:{{symbol}}{{price}} / 1次": "Image generation call: {{symbol}}{{price}} / 1 time", "图片输入 {{price}} / 1M tokens": "Image input {{price}} / 1M tokens", "图片输入 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Image input {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "图片输入: {{imageRatio}}": "Image input: {{imageRatio}}", + "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Image input: {{tokens}} / 1M * model ratio {{modelRatio}} * image ratio {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "图片输入价格": "Image Input Price", "图片输入价格 {{symbol}}{{price}} / 1M tokens": "Image input price {{symbol}}{{price}} / 1M tokens", "图片输入价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (图片倍率: {{imageRatio}})": "Image input price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (Image ratio: {{imageRatio}})", "图片输入价格:{{symbol}}{{price}} / 1M tokens": "Image input price: {{symbol}}{{price}} / 1M tokens", "图片输入价格:{{symbol}}{{total}} / 1M tokens": "Image input price: {{symbol}}{{total}} / 1M tokens", + "图片输入倍率": "", "图片输入倍率(仅部分模型支持该计费)": "Image input ratio (only supported by some models for billing)", "图片输入相关的倍率设置,键为模型名称,值为倍率,仅部分模型支持该计费": "Ratio settings related to image input, key is model name, value is ratio, only supported by some models for billing", - "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Image input: {{tokens}} / 1M * model ratio {{modelRatio}} * image ratio {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "图生文": "Describe", "图生视频": "Image to Video", "在Gotify服务器创建应用后获得的令牌,用于发送通知": "Token obtained after creating an application on the Gotify server, used to send notifications", @@ -1027,12 +1109,12 @@ "填充新模板": "Fill New Template", "填充旧模板": "Fill Old Template", "填充模板": "Fill Template", - "填充模板(全渠道)": "Fill template (all channels)", - "填充模板(指定渠道)": "Fill template (selected channels)", "填充模板:等级+激活": "Fill Template: Level + Activation", "填充模板:等级提示": "Fill Template: Level Prompt", "填充模板:组织或角色": "Fill Template: Organization or Role", "填充模板:组织提示": "Fill Template: Organization Prompt", + "填充模板(全渠道)": "Fill template (all channels)", + "填充模板(指定渠道)": "Fill template (selected channels)", "填入": "Fill", "填入 CC Switch": "Fill in CC Switch", "填入所有模型": "Fill in all models", @@ -1051,7 +1133,9 @@ "填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议": "After filling in the user agreement content, users will be required to check that they have read the user agreement during registration", "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "After filling in the privacy policy content, users will be required to check that they have read the privacy policy during registration", "填写音频补全价格前,需要先填写音频输入价格。": "Fill in the audio input price before setting the audio completion price.", + "处理上游模型更新": "", "处理中": "Processing", + "处理全部渠道上游更新": "", "备份支持": "Backup support", "备份状态": "Backup state", "备注": "Remark", @@ -1061,6 +1145,7 @@ "复制": "Copy", "复制代码": "Copy code", "复制令牌": "Copy token", + "复制令牌失败": "", "复制全部": "Copy all", "复制名称": "Copy name", "复制失败": "Copy failed", @@ -1079,6 +1164,7 @@ "复制渠道的所有信息": "Copy all information for a channel", "复制版本号": "Copy Version", "复制生成的密钥并粘贴到此处": "Copy the generated key and paste it here", + "复制请求头": "", "复制连接信息": "Copy Connection String", "复制链接": "Copy link", "外接设备": "External device", @@ -1102,13 +1188,13 @@ "套餐标题": "Plan Title", "套餐标题不能为空": "Package title cannot be empty", "套餐的基本信息和定价": "Basic plan info and pricing", + "如:大带宽批量分析图片推荐": "e.g. Large bandwidth batch analysis of image recommendations", + "如:香港线路": "e.g. Hong Kong line", "如果亲和到的渠道失败,重试到其他渠道成功后,将亲和更新到成功的渠道。": "If the affinity channel fails, after a successful retry on another channel, the affinity will be updated to the successful channel.", "如果你对接的是上游One API或者New API等转发项目,请使用OpenAI类型,不要使用此类型,除非你知道你在做什么。": "If you are connecting to upstream One API or New API forwarding projects, please use OpenAI type. Do not use this type unless you know what you are doing.", "如果用户请求中包含系统提示词,则使用此设置拼接到用户的系统提示词前面": "If the user request contains a system prompt, this setting will be appended to the user's system prompt", "如果镜像为私有,请填写密码或Token": "If the image is private, please fill in the password or token", "如果镜像为私有,请填写用户名": "If the image is private, please fill in the username", - "如:大带宽批量分析图片推荐": "e.g. Large bandwidth batch analysis of image recommendations", - "如:香港线路": "e.g. Hong Kong line", "始终使用浅色主题": "Always use light theme", "始终使用深色主题": "Always use dark theme", "字段映射": "Field Mapping", @@ -1176,6 +1262,7 @@ "密钥": "Key", "密钥 JSON 必须包含 access_token": "Key JSON must include access_token", "密钥 JSON 必须包含 account_id": "Key JSON must include account_id", + "密钥(编辑模式下,保存的密钥不会显示)": "Key (in edit mode, saved keys will not be displayed)", "密钥去重": "Key deduplication", "密钥将以Bearer方式添加到请求头中,用于验证webhook请求的合法性": "The key will be added to the request header as Bearer to verify the legitimacy of the webhook request", "密钥已删除": "Key has been deleted", @@ -1193,7 +1280,6 @@ "密钥获取成功": "Key acquisition successful", "密钥输入方式": "Key input method", "密钥预览": "Key preview", - "密钥(编辑模式下,保存的密钥不会显示)": "Key (in edit mode, saved keys will not be displayed)", "对于官方渠道,new-api已经内置地址,除非是第三方代理站点或者Azure的特殊接入地址,否则不需要填写": "For official channels, the new-api has a built-in address. Unless it is a third-party proxy site or a special Azure access address, there is no need to fill it in", "对免费模型启用预消耗": "Enable pre-consumption for free models", "对域名启用 IP 过滤(推荐开启)": "Enable IP filtering for domains (recommended)", @@ -1218,6 +1304,8 @@ "将只保留最近 {{value}} 个日志文件,其余将被删除。": "Only the last {{value}} log files will be retained; the rest will be deleted.", "将大请求体临时存储到磁盘": "Store large request bodies temporarily on disk", "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "The pricing configuration of the currently edited model {{name}} will be applied to the {{count}} selected models.", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。_one": "", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。_other": "", "将清除所有保存的配置并恢复默认设置,此操作不可撤销。是否继续?": "This will clear all saved configurations and restore default settings, this operation cannot be undone. Continue?", "将清除选定时间之前的所有日志": "This will clear all logs before the selected time", "将追加 2 条规则到现有规则列表。": "2 rules will be appended to the existing rule list.", @@ -1232,9 +1320,9 @@ "展示价格": "Display Pricing", "嵌套映射:用户分组 → 使用分组 → 倍率": "Nested mapping: user group → using group → ratio", "左侧边栏个人设置": "Personal settings in left sidebar", - "已为 ${count} 个渠道设置标签!": "Set tags for ${count} channels!", "已为 {{count}} 个模型设置{{type}}_one": "Set {{type}} for {{count}} model", "已为 {{count}} 个模型设置{{type}}_other": "Set {{type}} for {{count}} models", + "已为 ${count} 个渠道设置标签!": "Set tags for ${count} channels!", "已从 Discovery 自动填充配置": "Configuration auto-filled from Discovery", "已从 Discovery 获取配置,可继续手动修改所有字段。": "Configuration retrieved from Discovery. You can continue to manually modify all fields.", "已作废": "Invalidated", @@ -1249,16 +1337,18 @@ "已切换至最优倍率视图,每个模型使用其最低倍率分组": "Switched to the optimal ratio view, each model uses its lowest ratio group", "已初始化": "Initialized", "已删除": "Deleted", - "已删除 ${data} 个通道!": "Deleted ${data} channels!", "已删除 {{count}} 个令牌!": "Deleted {{count}} tokens!", "已删除 {{count}} 个令牌!_one": "Deleted {{count}} token!", "已删除 {{count}} 个令牌!_other": "Deleted {{count}} tokens!", "已删除 {{count}} 条失效兑换码_one": "Deleted {{count}} expired redemption code", "已删除 {{count}} 条失效兑换码_other": "Deleted {{count}} expired redemption codes", + "已删除 ${data} 个通道!": "Deleted ${data} channels!", "已删除所有禁用渠道,共计 ${data} 个": "Deleted all disabled channels, total ${data}", "已删除消息及其回复": "Deleted message and its replies", "已勾选": "Selected", "已勾选 {{count}} 个模型": "{{count}} models selected", + "已勾选 {{count}} 个模型_one": "", + "已勾选 {{count}} 个模型_other": "", "已发起支付": "Payment initiated", "已发送到 Fluent": "Sent to Fluent", "已取消 Passkey 注册": "Passkey registration cancelled", @@ -1270,10 +1360,13 @@ "已填充提示模板": "Prompt template filled", "已填充模版": "Template filled", "已填充策略模板": "Policy template filled", + "已处理上游模型更新:加入 {{added}} 个,删除 {{removed}} 个,本次忽略 {{ignored}} 个,当前已忽略模型 {{totalIgnored}} 个": "", "已备份": "Backed up", "已复制": "Copied", "已复制 ${count} 个模型": "Copied ${count} models", "已复制 ID 到剪贴板": "ID copied to clipboard", + "已复制:": "Copied:", + "已复制:{{name}}": "Copied: {{name}}", "已复制全部数据": "All data copied", "已复制到剪切板": "Copied to clipboard", "已复制到剪贴板": "Copied to clipboard", @@ -1282,14 +1375,16 @@ "已复制模型名称": "Model name copied", "已复制版本号": "Version copied", "已复制自动生成的 API Key": "Auto-generated API Key copied", - "已复制:": "Copied:", - "已复制:{{name}}": "Copied: {{name}}", "已完成": "Completed", "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "Applied the pricing configuration of model {{name}} to {{count}} models in batch", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型_one": "", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型_other": "", "已开启全局请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "Global request pass-through is enabled. Built-in NewAPI features such as parameter overrides, model redirection, and channel adaptation will be disabled. This is not a best practice. If this causes issues, please do not submit an issue.", + "已忽略模型": "", "已成功开始测试所有已启用通道,请刷新页面查看结果。": "Successfully started testing all enabled channels. Please refresh page to view results.", "已打开授权页面": "Authorization page opened", "已打开支付页面": "Payment page opened", + "已批量处理上游模型更新:渠道 {{channels}} 个,加入 {{added}} 个,删除 {{removed}} 个,失败 {{fails}} 个": "", "已提交": "Submitted", "已支付金额": "Amount Paid", "已新增 {{count}} 个模型:{{list}}_one": "Added {{count}} model: {{list}}", @@ -1306,6 +1401,8 @@ "已添加 {{count}} 个模板_other": "Added {{count}} templates", "已添加到白名单": "Added to whitelist", "已清理 {{count}} 个日志文件,释放 {{size}}": "Cleaned up {{count}} log files, freed {{size}}", + "已清理 {{count}} 个日志文件,释放 {{size}}_one": "", + "已清理 {{count}} 个日志文件,释放 {{size}}_other": "", "已清空": "Cleared", "已清空测试结果": "Cleared test results", "已生成授权凭据": "Authorization credentials generated", @@ -1324,13 +1421,14 @@ "已达到购买上限": "Purchase limit reached", "已过期": "Expired", "已运行时间": "Uptime", - "已选择 ${count} 个渠道": "Selected ${count} channels", "已选择 {{count}} 个模型_one": "Selected {{count}} model", "已选择 {{count}} 个模型_other": "Selected {{count}} models", "已选择 {{selected}} / {{total}}": "Selected {{selected}} / {{total}}", + "已选择 ${count} 个渠道": "Selected ${count} channels", "已重置为默认配置": "Reset to default configuration", "已销毁": "Destroyed", "币种": "Currency", + "帐号信息": "", "常用上下文 Key(用于 context_*)": "Common Context Keys (for context_*)", "常见问答": "FAQ", "常见问答管理,为用户提供常见问题的答案(最多50个,前端显示最新20条)": "FAQ management, providing answers to common questions for users (maximum 50, display latest 20 on the front end)", @@ -1349,19 +1447,16 @@ "延长容器时长将会产生额外费用,请确认您有足够的账户余额。": "Extending container duration will incur additional charges, please ensure you have sufficient account balance.", "延长操作一旦确认无法撤销,费用将立即扣除。": "Once confirmed, the extension operation cannot be undone, and charges will be deducted immediately.", "延长时长": "Extension Duration", + "延长时长(小时)": "Extension Duration (hours)", "延长时长不能超过720小时(30天)": "Extension duration cannot exceed 720 hours (30 days)", "延长时长失败": "Failed to extend duration", "延长时长至少为1小时": "Extension duration must be at least 1 hour", - "延长时长(小时)": "Extension Duration (hours)", "建立连接时发生错误": "Error occurred while establishing connection", "建议在生产环境中使用 MySQL 或 PostgreSQL 数据库,或确保 SQLite 数据库文件已映射到宿主机的持久化存储。": "It is recommended to use MySQL or PostgreSQL databases in production environments, or ensure that the SQLite database file is mapped to the persistent storage of the host machine.", "开": "On", "开启「默认使用 auto 分组」后,新建令牌和初始令牌都会自动设为 auto。": "After enabling \"Default to auto group\", new tokens and initial tokens will be set to auto.", "开启之后会清除用户提示词中的": "After enabling, the user prompt will be cleared", "开启之后将上游地址替换为服务器地址": "After enabling, the upstream address will be replaced with the server address", - "开启后不限制:必须设置模型倍率": "After enabling, no limit: must set model ratio", - "开启后创建令牌默认选择auto分组,初始令牌也将设为auto": "When enabled, new tokens default to auto group, initial tokens are also set to auto", - "开启后未登录用户无法访问模型广场": "When enabled, unauthenticated users cannot access the model marketplace", "开启后,using_group 会参与 cache key(不同分组隔离)。": "When enabled, using_group will be part of the cache key (isolated by group).", "开启后,仅\"消费\"和\"错误\"日志将记录您的客户端IP地址": "After enabling, only \"consumption\" and \"error\" logs will record your client IP address", "开启后,对免费模型(倍率为0,或者价格为0)的模型也会预消耗额度": "After enabling, free models (ratio 0 or price 0) will also pre-consume quota", @@ -1373,6 +1468,11 @@ "开启后,规则名称会参与 cache key(不同规则隔离)。": "When enabled, the rule name will be part of the cache key (isolated by rule).", "开启后,该渠道请求 Claude 时将强制追加 ?beta=true(无需客户端手动传参)": "When enabled, requests to Claude through this channel will force append ?beta=true (no need for clients to pass this parameter manually)", "开启后,违规请求将额外扣费。": "When enabled, violation requests will incur additional charges.", + "开启后不限制:必须设置模型倍率": "After enabling, no limit: must set model ratio", + "开启后创建令牌默认选择auto分组,初始令牌也将设为auto": "When enabled, new tokens default to auto group, initial tokens are also set to auto", + "开启后未登录用户无法访问模型广场": "When enabled, unauthenticated users cannot access the model marketplace", + "开启后检测到新增模型会自动加入当前渠道模型列表": "", + "开启后由后端定时任务检测该渠道上游模型变化": "", "开启批量操作": "Enable batch selection", "开始": "Start", "开始同步": "Start sync", @@ -1382,6 +1482,7 @@ "异步任务退款": "Async Task Refund", "张图片": " images", "弱变换": "High Variation", + "强制使用 AUTH LOGIN": "Force AUTH LOGIN", "强制将响应格式化为 OpenAI 标准格式(只适用于OpenAI渠道类型)": "Force format responses to OpenAI standard format (Only for OpenAI channel types)", "强制格式化": "Force format", "强制要求": "Mandatory requirement", @@ -1419,6 +1520,7 @@ "当前设置类型: ": "Current setting type: ", "当前跟随系统": "Currently following system", "当前配置无法连接到 io.net。": "Unable to connect to io.net with current configuration.", + "当前额度": "Current quota", "当某个分组的用户使用另一个分组的令牌时,可设置特殊倍率覆盖基础倍率。例如:vip 分组的用户使用 default 分组时倍率为 0.5": "When a user in one group uses a token from another group, a special ratio can override the base ratio. E.g., vip users using default group get ratio 0.5", "当模型没有设置价格时仍接受调用,仅当您信任该网站时使用,可能会产生高额费用": "Accept calls even if the model has no price settings, use only when you trust the website, which may incur high costs", "当运行通道全部测试时,超过此时间将自动禁用通道": "When running all channel tests, the channel will be automatically disabled when this time is exceeded", @@ -1430,8 +1532,8 @@ "微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效)": "Scan WeChat QR code to follow official account, enter \"verification code\" to get code (valid for 3 minutes)", "微信扫码登录": "WeChat scan code to log in", "微信账户绑定成功!": "WeChat account bound successfully!", - "必填。对请求的 model 名称进行匹配,任意一条匹配即命中该规则。": "Required. Match the requested model name; any match triggers this rule.", "必填:请输入服务器地址以自动生成完整端点 URL": "Required: Enter server address to auto-generate full endpoint URLs", + "必填。对请求的 model 名称进行匹配,任意一条匹配即命中该规则。": "Required. Match the requested model name; any match triggers this rule.", "必须全部满足(AND)": "All must be met (AND)", "必须是有效的 JSON 字符串数组,例如:[\"g1\",\"g2\"]": "Must be a valid JSON string array, for example: [\"g1\",\"g2\"]", "忘记密码?": "Forgot password?", @@ -1500,10 +1602,13 @@ "批量删除失败": "Batch deletion failed", "批量删除成功": "Batch deletion successful", "批量删除模型": "Batch delete models", + "批量处理失败": "", "批量应用当前模型价格": "Batch Apply Current Model Pricing", "批量操作": "Batch Operations", "批量操作失败": "Batch operation failed", "批量操作完成: {{success}}个成功, {{failed}}个失败": "Batch operation completed: {{success}} succeeded, {{failed}} failed", + "批量检测失败": "", + "批量检测完成:渠道 {{channels}} 个,新增 {{add}} 个,删除 {{remove}} 个,失败 {{fails}} 个": "", "批量测试${count}个模型": "Batch test ${count} models", "批量测试完成!成功: ${success}, 失败: ${fail}, 总计: ${total}": "Batch testing completed! Success: ${success}, Fail: ${fail}, Total: ${total}", "批量测试已停止": "Batch testing stopped", @@ -1525,30 +1630,31 @@ "按倍率设置": "Set by ratio", "按次": "Per request", "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Per request {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "按次计费": "Pay per request", "按次:{{symbol}}{{price}}": "Per request: {{symbol}}{{price}}", "按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Per request: {{symbol}}{{price}} * {{ratioType}}: {{ratio}} = {{symbol}}{{total}}", + "按次计费": "Pay per request", "按照如下格式输入:AccessKey|SecretAccessKey|Region": "Enter in the format: AccessKey|SecretAccessKey|Region", "按量计费": "Pay as you go", "按量计费下需要先填写输入价格,才能保存其它价格项。": "For per-token billing, fill in the input price before saving other price fields.", "按顺序替换content中的变量占位符": "Replace variable placeholders in content in order", "换脸": "Face swap", + "授权,需在遵守": " and must be used in compliance with the ", "授权失败": "Authorization failed", "授权端点": "Authorization Endpoint", "授权范围 (Scopes)": "Scopes", - "授权,需在遵守": " and must be used in compliance with the ", "排序": "Sort Order", "排队中": "Queuing", "接受未设置价格模型": "Accept models without price settings", "接口凭证": "Interface credentials", "接口密钥已过期": "API key has expired", + "接收上游模型更新通知": "", "控制台": "Console", "控制台区域": "Console Area", "控制输出的随机性和创造性": "Controls randomness and creativity of output", "控制顶栏模块显示状态,全局生效": "Control header module display status, global effect", "推荐": "Recommended", - "推荐使用(用户可选)": "Recommended (user optional)", "推荐:用户可以选择是否使用指纹等验证": "Recommended: Users can choose whether to use fingerprint verification", + "推荐使用(用户可选)": "Recommended (user optional)", "描述": "Description", "提交": "Submit", "提交时间": "Submission time", @@ -1559,14 +1665,14 @@ "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "Prompt {{input}} tokens / 1M tokens * {{symbol}}{{price}}", "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Prompt {{input}} tokens / 1M tokens * {{symbol}}{{price}} + Completion {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Prompt {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + Cache {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + Cache creation {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + Completion {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "提示价格:{{symbol}}{{price}} / 1M tokens": "Prompt price: {{symbol}}{{price}} / 1M tokens", - "提示缓存倍率": "Prompt cache ratio", "提示:如需备份数据,只需复制上述目录即可": "Tip: To back up data, simply copy the directory above", "提示:此处配置仅用于控制「模型广场」对用户的展示效果,不会影响模型的实际调用与路由。若需配置真实调用行为,请前往「渠道管理」进行设置。": "Notice: This configuration only affects how models are displayed in the Model Marketplace and does not impact actual model invocation or routing. To configure real invocation behavior, please go to Channel Management.", "提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。": "Notice: Endpoint mapping is for Model Marketplace display only and does not affect real model invocation. To configure real invocation, please go to Channel Management.", "提示:该功能为测试版,未来配置结构与功能行为可能发生变更,请勿在生产环境使用。": "Notice: This feature is beta. The configuration structure and behavior may change in the future. Do not use in production.", "提示:语言偏好会同步到您登录的所有设备,并影响API返回的错误消息语言。": "Note: Language preference syncs across all your logged-in devices and affects the language of API error messages.", "提示:链接中的{key}将被替换为API密钥,{address}将被替换为服务器地址": "Tip: {key} in the link will be replaced with the API key, {address} will be replaced with the server address", + "提示价格:{{symbol}}{{price}} / 1M tokens": "Prompt price: {{symbol}}{{price}} / 1M tokens", + "提示缓存倍率": "Prompt cache ratio", "搜索供应商": "Search vendor", "搜索关键字": "Search keywords", "搜索失败": "Search failed", @@ -1580,6 +1686,7 @@ "搜索模型失败": "Search model failed", "搜索渠道名称或地址": "Search channel name or address", "搜索聊天应用名称": "Search chat app name", + "搜索规则(描述 / 类型 / 路径 / 来源 / 目标)": "", "搜索规则(类型 / 路径 / 来源 / 目标)": "Search rules (type / path / source / target)", "搜索部署名称": "Search deployment name", "操作": "Actions", @@ -1594,13 +1701,16 @@ "支付": "Pay", "支付地址": "Payment address", "支付失败": "Payment failed", + "支付完成后用户跳转的页面,留空则自动使用 服务器地址 + /console/topup": "", "支付宝": "Alipay", "支付方式": "Payment method", "支付方式名称": "Pay Method Name", + "支付方式名称不能为空": "", "支付方式类型": "Pay Method Type", "支付渠道": "Payment Channels", "支付设置": "Payment", "支付请求失败": "Payment request failed", + "支付返回地址": "", "支付金额": "Payment Amount", "支持 Ctrl+V 粘贴图片": "Supports Ctrl+V to paste images", "支持 JSONPath,如 email, data.user.email": "Supports JSONPath, e.g. email, data.user.email", @@ -1629,6 +1739,7 @@ "收起": "Collapse", "收起侧边栏": "Collapse sidebar", "收起内容": "Collapse content", + "收起原生额度输入": "Hide raw quota input", "放大": "Upscalers", "放大编辑": "Expand editor", "敏感信息不会发送到前端显示": "Sensitive information will not be displayed in the frontend", @@ -1646,9 +1757,9 @@ "数据管理和日志查看": "Data management and log viewing", "文件上传": "File upload", "文件搜索 {{count}} 次 * {{symbol}}{{price}} / 1K 次": "File search {{count}} calls * {{symbol}}{{price}} / 1K calls", + "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "File search: {{count}} / 1K * unit price {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "文件搜索价格:{{symbol}}{{price}} / 1K 次": "File search price: {{symbol}}{{price}} / 1K times", "文件搜索调用 {{fileSearchCallCount}} 次": "File search called {{fileSearchCallCount}} times", - "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "File search: {{count}} / 1K * unit price {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "文字价格 {{textPrice}} + 音频价格 {{audioPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Text price {{textPrice}} + Audio price {{audioPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "Text prompt {{input}} tokens / 1M tokens * {{symbol}}{{price}} + Text completion {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Text prompt {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + Text completion {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + Audio prompt {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + Audio completion {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", @@ -1664,8 +1775,11 @@ "新增 Key 来源": "Add Key Source", "新增供应商": "Add vendor", "新增失败": "Failed to add", + "新增已选 {{selected}} / {{total}}": "", "新增成功": "Added successfully", + "新增支付方式": "", "新增条件": "Add Condition", + "新增模型": "", "新增规则": "Add Rule", "新增订阅": "Add subscription", "新密码": "New Password", @@ -1676,9 +1790,9 @@ "新建容器部署": "Create Container Deployment", "新建数量": "New quantity", "新建组": "New group", - "新格式模板": "New format template", "新格式(支持条件判断与json自定义):": "New format (supports conditional judgment and JSON customization):", "新格式(规则 + 条件)": "New Format (Rules + Conditions)", + "新格式模板": "New format template", "新版本": "New Version", "新用户使用邀请码奖励额度": "New user invitation code bonus quota", "新用户初始额度": "Initial quota for new users", @@ -1715,10 +1829,10 @@ "日志类型": "Log type", "日志设置": "Log settings", "日志详情": "Log details", - "旧格式必须是 JSON 对象": "Legacy format must be a JSON object", - "旧格式模板": "Old format template", "旧格式(JSON 对象)": "Legacy Format (JSON Object)", "旧格式(直接覆盖):": "Old format (direct override):", + "旧格式必须是 JSON 对象": "Legacy format must be a JSON object", + "旧格式模板": "Old format template", "旧的备用码已失效,请保存新的备用码": "Old backup codes have been invalidated, please save the new backup codes", "早上好": "Good morning", "时间": "Time", @@ -1731,17 +1845,19 @@ "是否为企业账户": "Is it an enterprise account?", "是否同时重置对话消息?选择\"是\"将清空所有对话记录并恢复默认示例;选择\"否\"将保留当前对话记录。": "Reset conversation messages at the same time? Selecting \"Yes\" will clear all conversation records and restore default examples; selecting \"No\" will retain current conversation records.", "是否将该订单标记为成功并为用户入账?": "Mark this order as successful and credit the user?", + "是否检测上游模型更新": "", "是否确认充值?": "Confirm the recharge?", + "是否自动同步上游模型更新": "", "是否自动禁用": "Whether to automatically disable", "是否要求指纹/面容等生物识别": "Whether to require fingerprint/face recognition", "显示倍率": "Show ratio", + "显示最新20条": "Display latest 20", "显示名称": "Display Name", "显示名称字段": "Display Name Field", "显示名称字段(可选)": "Display Name Field (optional)", "显示完整内容": "Show full content", "显示操作项": "Show actions", "显示更多": "Show more", - "显示最新20条": "Display latest 20", "显示第": "Showing", "显示设置": "Display settings", "显示调试": "Show debug", @@ -1762,6 +1878,7 @@ "暂无公告": "No Notice", "暂无分组,点击下方按钮添加": "No groups yet. Click the button below to add one.", "暂无匹配模型": "No matching model", + "暂无参数覆盖记录": "", "暂无可复制 JSON": "No JSON available to copy", "暂无可复制的版本信息": "No version information to copy", "暂无可展示数据": "No data available to display", @@ -1775,6 +1892,7 @@ "暂无已绑定项": "No bound items", "暂无常见问答": "No FAQ", "暂无成功模型": "No successful models", + "暂无支付方式,点击上方按钮新增": "", "暂无数据": "No data", "暂无数据,点击下方按钮添加键值对": "No data, click the button below to add key-value pairs", "暂无日志": "No logs", @@ -1803,6 +1921,7 @@ "更新": "Update", "更新 Creem 设置": "Update Creem Settings", "更新 Stripe 设置": "Update Stripe settings", + "更新 Waffo 设置": "", "更新SSRF防护设置": "Update SSRF Protection Settings", "更新Worker设置": "Update Worker Settings", "更新令牌信息": "Update Token Information", @@ -1825,18 +1944,7 @@ "更新配置后,容器可能需要重启以应用新的设置。请确保您了解这些更改的影响。": "After updating the configuration, the container may need to restart to apply the new settings. Please ensure you understand the impact of these changes.", "更新配置失败": "Failed to update configuration", "更新预填组": "Update pre-filled group", - "最低": "lowest", - "最低充值美元数量": "Minimum recharge dollar amount", - "最后使用时间": "Last used time", - "最后更新": "Last Updated", - "最后请求": "Last request", - "最大GPU数量": "Max Number of GPUs", - "最大可用": "Max Available", - "最大条目数": "Max Entries", - "最终抵扣": "Final Deduction", - "最近一次": "Last", - "最近事件": "Recent Events", - "最高优先级": "highest priority", + "替换": "", "月": "month", "有 Reasoning": "Has Reasoning", "有序字符串数组": "Ordered string array", @@ -1846,6 +1954,7 @@ "有效期设置": "Validity Settings", "服务可用性": "Service Status", "服务商": "Service Provider", + "服务器IP": "Server IP", "服务器地址": "Server Address", "服务器日志功能未启用(未配置日志目录)": "Server logging is not enabled (log directory not configured)", "服务器日志管理": "Server Log Management", @@ -1892,8 +2001,8 @@ "本地数据存储": "Local data storage", "本地计费": "Local billing", "本月获得": "This month", - "本设备内置": "Built-in device", "本设备:手机指纹/面容,外接:USB安全密钥": "Built-in: phone fingerprint/face, External: USB security key", + "本设备内置": "Built-in device", "本项目根据": "This project is licensed under the ", "机密环境变量": "Secret Environment Variables", "机密环境变量将被加密存储,适用于存储密码、API密钥等敏感信息。": "Secret environment variables will be stored encrypted, suitable for storing passwords, API keys and other sensitive information.", @@ -1902,19 +2011,21 @@ "权限设置": "Permission Settings", "条": "items", "条 - 第": "to", + "条,共": "of", "条件取反": "Negate Condition", "条件数": "Conditions", "条件规则": "Condition Rules", "条件项设置": "Condition Item Settings", "条日志已清理!": "logs have been cleared!", "条规则": "rules", - "条,共": "of", "来源": "Source", "来源于 IO.NET 部署": "From IO.NET Deployment", "来源端点": "Source Endpoint", "来自模型重定向,尚未加入模型列表": "From model redirect, not yet added to the model list", "某些配置更改可能需要几分钟才能生效。": "Some configuration changes may take a few minutes to take effect.", "查看": "Check", + "查看 Codex 帐号信息与用量": "", + "查看 JSON 示例": "", "查看关联部署": "View Associated Deployment", "查看图片": "View pictures", "查看密钥": "View key", @@ -1947,11 +2058,14 @@ "格式示例:": "Format example:", "格式错误": "Format Error", "检查更新": "Check for updates", + "检测全部渠道上游更新": "", "检测到 FluentRead(流畅阅读)": "FluentRead (smooth reading) detected", "检测到以下高危状态码重定向规则": "Detected high-risk status-code redirect rules", "检测到剪贴板中的连接信息": "Connection info detected in clipboard", "检测到多个密钥,您可以单独复制每个密钥,或点击复制全部获取完整内容。": "Detected multiple keys, you can copy each key individually or click Copy All to get the complete content.", "检测到该消息后有AI回复,是否删除后续回复并重新生成?": "AI reply detected after this message, delete subsequent replies and regenerate?", + "检测失败": "", + "检测完成:新增 {{add}} 个,删除 {{remove}} 个": "", "检测必须等待绘图成功才能进行放大等操作": "Detection must wait for drawing to succeed before performing zooming and other operations", "概览": "Overview", "模型": "Model", @@ -1967,6 +2081,7 @@ "模型价格:{{symbol}}{{price}}": "Model price: {{symbol}}{{price}}", "模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Model price: {{symbol}}{{price}} * {{ratioType}}: {{ratio}} = {{symbol}}{{total}}", "模型价格:{{symbol}}{{price}} / 次": "Model price: {{symbol}}{{price}} / request", + "模型价格未配置": "Model Price Not Configured", "模型倍率": "Model ratio", "模型倍率 {{modelRatio}}": "Model ratio {{modelRatio}}", "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "Model ratio {{modelRatio}}, cache ratio {{cacheRatio}}, completion ratio {{completionRatio}}, {{ratioType}} {{ratio}}", @@ -1991,8 +2106,8 @@ "模型名称已存在": "Model name already exists", "模型固定价格": "Model price per call", "模型图标": "Model icon", - "模型定价设置": "Model Pricing", "模型定价,需要登录访问": "Model pricing, requires login to access", + "模型定价设置": "Model Pricing", "模型广场": "Model Marketplace", "模型拉取失败: {{error}}": "Failed to pull model: {{error}}", "模型排行": "Model ranking", @@ -2002,8 +2117,8 @@ "模型更新成功!": "Model updated successfully!", "模型未加入列表,可能无法调用": "Model not in the list; requests may fail", "模型正则": "Model Regex", - "模型正则不能为空": "Model regex cannot be empty", "模型正则(每行一个)": "Model Regex (one per line)", + "模型正则不能为空": "Model regex cannot be empty", "模型消耗分布": "Model consumption distribution", "模型消耗趋势": "Model consumption trend", "模型版本": "Model version", @@ -2034,18 +2149,24 @@ "次": "request", "欢迎使用,请完成以下设置以开始使用系统": "Welcome! Please complete the following settings to start using the system", "欧元": "EUR", + "正则替换": "", "正在加载可用部署位置...": "Loading available deployment locations...", "正在加载签到状态...": "Loading check-in status...", + "正在处理,请稍候": "", "正在处理大内容...": "Processing large content...", + "正在批量处理,请稍候": "", + "正在批量检测,请稍候": "", "正在提交": "Submitting", "正在构造请求体预览...": "Constructing request body preview...", "正在检查 io.net 连接...": "Checking io.net connection...", + "正在检测,请稍候": "", "正在测试第 ${current} - ${end} 个模型 (共 ${total} 个)": "Testing model ${current} - ${end} (total ${total})", "正在跟随最新日志": "Following latest logs", "正在跳转 GitHub...": "Redirecting to GitHub...", "正在跳转...": "Redirecting...", "正常": "Normal", "正常情况下,令牌的计费倍率由令牌所选的分组决定。特殊倍率可以根据「用户所在分组」进一步覆盖这个倍率。": "Normally, a token's billing ratio is determined by its selected group. Special ratios can override this based on the user's group.", + "正数为增加,负数为减少": "Positive to add, negative to subtract", "此代理仅用于图片请求转发,Webhook通知发送等,AI API请求仍然由服务器直接发出,可在渠道设置中单独配置代理": "This proxy is only used for image request forwarding, webhook notification sending, etc. AI API requests are still sent directly by the server, and proxy can be configured separately in channel settings", "此修改将不可逆": "This modification will be irreversible", "此操作不可恢复,请仔细确认时间后再操作!": "This operation cannot be recovered, please confirm the time carefully before proceeding!", @@ -2072,6 +2193,7 @@ "此项可选,用于覆盖请求参数。不支持覆盖 stream 参数": "This is optional, used to override request parameters. Overriding stream parameter is not supported.", "此项可选,用于覆盖请求头参数": "This is optional, used to override request header parameters.", "此项可选,用于通过自定义API地址来进行 API 调用,末尾不要带/v1和/": "Optional for API calls through custom API address, do not add /v1 and / at the end", + "每个充值单位对应的 USD 金额,默认 1.0": "", "每个分组代表一个价格档位。管理员创建分组后,可以选择哪些档位对用户开放自选。": "Each group represents a pricing tier. After creating groups, admins can choose which tiers are open for user self-selection.", "每个用户最多可创建的令牌数量,默认 1000,设置过大可能会影响性能": "Maximum number of tokens each user can create, default 1000. Setting too large may affect performance", "每周": "Weekly", @@ -2087,7 +2209,9 @@ "永久删除您的两步验证设置": "Permanently delete your two-factor authentication settings", "永久删除所有备用码(包括未使用的)": "Permanently delete all backup codes (including unused ones)", "汇率": "Exchange rate", + "沙盒模式": "", "沙盒环境 RSA 私钥 Base64 (PKCS#8 DER)": "Sandbox RSA private key Base64 (PKCS#8 DER)", + "沙盒环境 Waffo API 密钥": "", "沙盒环境 Waffo 公钥 Base64 (X.509 DER)": "Sandbox Waffo public key Base64 (X.509 DER)", "没有匹配的字段": "No matching fields", "没有匹配的日志条目": "No matching log entries", @@ -2104,8 +2228,8 @@ "注册": "Sign up", "注册 Passkey": "Register Passkey", "注意": "Note", - "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "Note: For non-Chat API, please make sure to enter the correct API address, otherwise it may not work", "注意:JSON中重复的键只会保留最后一个同名键的值": "Note: In JSON, duplicate keys will only keep the value of the last key with the same name", + "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "Note: For non-Chat API, please make sure to enter the correct API address, otherwise it may not work", "注销": "Logout", "注销成功!": "Logout successful!", "活跃文件": "Active Files", @@ -2171,14 +2295,6 @@ "添加键值对": "Add key-value pair", "添加问答": "Add FAQ", "添加额度": "Add quota", - "减少": "Subtract", - "覆盖": "Override", - "调整额度": "Adjust Quota", - "调整额度成功": "Quota adjusted successfully", - "当前额度": "Current quota", - "变更": "Change", - "预计结果": "Estimated result", - "正数为增加,负数为减少": "Positive to add, negative to subtract", "清理不活跃缓存": "Clean up inactive cache", "清理失败": "Cleanup failed", "清理方式": "Cleanup Mode", @@ -2187,6 +2303,7 @@ "清空全部缓存": "Clear All Cache", "清空该规则缓存": "Clear This Rule's Cache", "清空重定向": "Clear redirect", + "清除": "", "清除历史日志": "Clear historical logs", "清除失效兑换码": "Clear invalid redemption codes", "清除所有模型": "Clear all models", @@ -2194,8 +2311,8 @@ "渠道 ID": "Channel ID", "渠道ID,名称,密钥,API地址": "Channel ID, name, key, Base URL", "渠道亲和性": "Channel affinity", - "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "Channel affinity reuses the last successful channel based on keys extracted from the request context or JSON body.", "渠道亲和性:上游缓存命中": "Channel Affinity: Upstream Cache Hit", + "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "Channel affinity reuses the last successful channel based on keys extracted from the request context or JSON body.", "渠道优先级": "Channel Priority", "渠道信息": "Channel information", "渠道创建成功!": "Channel created successfully!", @@ -2223,6 +2340,8 @@ "点击\"确认延长\"后将立即扣除费用并延长容器运行时间": "After clicking \"Confirm Extension\", the fee will be deducted immediately and the container runtime will be extended", "点击上传文件或拖拽文件到这里": "Click to upload file or drag and drop file here", "点击下方按钮通过 Telegram 完成绑定": "Click the button below to complete binding via Telegram", + "点击处理删除模型": "", + "点击处理新增模型": "", "点击复制ID": "Click to copy ID", "点击复制模型名称": "Click to copy model name", "点击查看差异": "Click to view differences", @@ -2241,6 +2360,7 @@ "状态页面Slug": "Status Page Slug", "环境变量": "Environment Variables", "生产环境 RSA 私钥 Base64 (PKCS#8 DER)": "Production RSA private key Base64 (PKCS#8 DER)", + "生产环境 Waffo API 密钥": "", "生产环境 Waffo 公钥 Base64 (X.509 DER)": "Production Waffo public key Base64 (X.509 DER)", "生成令牌": "Generate Token", "生成并填入": "Generate and Fill", @@ -2274,6 +2394,7 @@ "用户信息更新成功!": "User information updated successfully!", "用户信息端点": "User Info Endpoint", "用户信息缺失": "User information missing", + "用户最大令牌数量": "Maximum Tokens per User", "用户分组": "User Group", "用户分组和额度管理": "User Group and Quota Management", "用户分组的联动作用": "How user group affects other features", @@ -2291,9 +2412,9 @@ "用户名字段(可选)": "Username Field (optional)", "用户名或邮箱": "Username or email", "用户名称": "User Name", + "用户在充值页面看到的支付方式名称,例如:Credit Card": "", "用户控制面板,管理账户": "User control panel for account management", "用户新建令牌时可选的分组,格式为 JSON 字符串,例如:{\"vip\": \"VIP 用户\", \"test\": \"测试\"},表示用户可以选择 vip 分组和 test 分组": "User selectable groups when creating tokens, in JSON string format, for example: {\"vip\": \"VIP User\", \"test\": \"Test\"}, indicating that users can choose vip group and test group", - "用户最大令牌数量": "Maximum Tokens per User", "用户每周期最多请求完成次数": "User max successful request times per period", "用户每周期最多请求次数": "User max request times per period", "用户注册时看到的网站名称,比如'我的网站'": "Website name users see during registration, e.g. 'My Website'", @@ -2314,6 +2435,7 @@ "留空则使用账号绑定的邮箱": "If left blank, the email address bound to the account will be used", "留空则使用默认端点;支持 {path, method}": "Leave blank to use the default endpoint; supports {path, method}", "留空则保持原有密钥": "Leave empty to keep existing key", + "留空则自动使用 服务器地址 + /api/waffo/webhook": "", "留空则默认使用服务器地址,注意不能携带http://或者https://": "If left blank, the server address will be used by default. Note that http:// or https:// should not be included", "登 录": "Log In", "登录": "Sign in", @@ -2340,6 +2462,7 @@ "硬件类型": "Hardware Type", "硬件配置": "Hardware Configuration", "确定": "OK", + "确定?": "Sure?", "确定删除此组?": "Confirm delete this group?", "确定导入": "Confirm import", "确定是否要修复数据库一致性?": "Are you sure you want to repair database consistency?", @@ -2352,6 +2475,7 @@ "确定是否要复制此渠道?": "Are you sure you want to copy this channel?", "确定是否要注销此用户?": "Are you sure you want to deactivate this user?", "确定清除所有失效兑换码?": "Are you sure you want to clear all invalid redemption codes?", + "确定要仅检测全部渠道上游模型更新吗?(不执行新增/删除)": "", "确定要修改所有子渠道优先级为 ": "Confirm to modify all sub-channel priorities to ", "确定要修改所有子渠道权重为 ": "Confirm to modify all sub-channel weights to ", "确定要充值 $": "Confirm to recharge $", @@ -2372,6 +2496,7 @@ "确定要删除选中的": "Are you sure you want to delete the selected", "确定要启用所有密钥吗?": "Are you sure you want to enable all keys?", "确定要启用此用户吗?": "Are you sure you want to enable this user?", + "确定要对全部渠道执行上游模型更新吗?": "", "确定要提升此用户吗?": "Are you sure you want to promote this user?", "确定要更新所有已启用通道余额吗?": "Are you sure you want to update the balance of all enabled channels?", "确定要测试所有未手动禁用渠道吗?": "Are you sure you want to test all channels except manually disabled ones?", @@ -2382,15 +2507,14 @@ "确定要降级此用户吗?": "Are you sure you want to demote this user?", "确定重置": "Confirm reset", "确定重置模型倍率吗?": "Confirm to reset model ratio?", - "确定?": "Sure?", "确认": "Confirm", "确认作废": "Confirm invalidation", "确认关闭提示": "Confirm close", "确认冲突项修改": "Confirm conflict item modification", "确认删除": "Confirm deletion", "确认删除模型": "Confirm Delete Model", - "确认删除该分组的所有规则?": "Delete all rules for this group?", "确认删除该分组?": "Confirm delete this group?", + "确认删除该分组的所有规则?": "Delete all rules for this group?", "确认删除该规则?": "Confirm delete this rule?", "确认取消密码登录": "Confirm cancel password login", "确认启用": "Confirm Enable", @@ -2418,13 +2542,16 @@ "磁盘使用率超过此值时拒绝请求": "Reject requests when disk usage exceeds this value", "磁盘可用空间小于缓存最大总量设置": "Disk free space is less than max cache size setting", "磁盘命中": "Disk Hits", + "磁盘缓存最大总量 (MB)": "Max Disk Cache Size (MB)", "磁盘缓存占用的最大空间": "Maximum space occupied by disk cache", "磁盘缓存已清理": "Disk cache cleared", - "磁盘缓存最大总量 (MB)": "Max Disk Cache Size (MB)", "磁盘缓存设置(磁盘换内存)": "Disk Cache Settings (Disk Swap Memory)", "磁盘缓存阈值 (MB)": "Disk Cache Threshold (MB)", "示例": "Example", "示例:{\"default\": [200, 100], \"vip\": [0, 1000]}。": "Example: {\"default\": [200, 100], \"vip\": [0, 1000]}.", + "视频": "Video", + "视频Remix": "Video remix", + "视频无法在当前浏览器中播放,这可能是由于:": "The video cannot be played in this browser, possibly because:", "禁用": "Disable", "禁用 store 透传": "Disable store Pass-through", "禁用2FA失败": "Failed to disable Two-Factor Authentication", @@ -2442,10 +2569,12 @@ "私有镜像仓库的密码": "Password for private image registry", "私有镜像仓库的用户名": "Username for private image registry", "秒": "Second", + "移动": "", + "移动请求头": "", "移除": "Remove", "移除 (-:)": "Remove (-:)", - "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "Removal of One API copyright mark must first be authorized. Project maintenance requires a lot of effort. If this project is meaningful to you, please actively support it.", "移除 functionResponse.id 字段": "Remove functionResponse.id Field", + "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "Removal of One API copyright mark must first be authorized. Project maintenance requires a lot of effort. If this project is meaningful to you, please actively support it.", "空": "Empty", "窗口处理": "window handling", "窗口等待": "window wait", @@ -2479,19 +2608,19 @@ "等待中": "Waiting", "等待获取邮箱信息...": "Waiting to get email information...", "筛选": "Filter", + "签到最大额度": "Maximum check-in quota", + "签到最小额度": "Minimum check-in quota", "签到功能允许用户每日签到获取随机额度奖励": "Check-in feature allows users to check in daily to receive random quota rewards", "签到失败": "Check-in failed", "签到奖励将直接添加到您的账户余额": "Check-in rewards will be directly added to your account balance", "签到奖励的最大额度": "Maximum quota for check-in rewards", "签到奖励的最小额度": "Minimum quota for check-in rewards", "签到成功!获得": "Check-in successful! Received", - "签到最大额度": "Maximum check-in quota", - "签到最小额度": "Minimum check-in quota", "签到设置": "Check-in Settings", "简单来说:同一个令牌分组,不同等级的用户可以享受不同的价格。": "In short: same token group, different user tiers can enjoy different prices.", "简洁": "Simple", - "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。": "Simple mode returns message only; status code and error type will use system defaults.", "简洁模式:按 type 全量清理对象,例如 redacted_thinking。": "Simple mode: Prune all objects by type, e.g. redacted_thinking.", + "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。": "Simple mode returns message only; status code and error type will use system defaults.", "管理": "Manage", "管理 Ollama 模型的拉取和删除": "Manage Ollama model pulling and deletion", "管理你的 LinuxDO OAuth App": "Manage your LinuxDO OAuth App", @@ -2502,8 +2631,8 @@ "管理员暂时未设置任何关于内容": "The administrator has not set any custom About content yet", "管理员未开启 Creem 充值!": "The administrator has not enabled Creem recharge!", "管理员未开启Stripe充值!": "Administrator has not enabled Stripe recharge!", - "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "The administrator has not enabled the online recharge function, please contact the administrator to enable it or recharge with a redemption code.", "管理员未开启在线充值!": "The administrator has not enabled online recharge!", + "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "The administrator has not enabled the online recharge function, please contact the administrator to enable it or recharge with a redemption code.", "管理员未开启在线支付功能,请联系管理员配置。": "Online payment is not enabled by the admin. Please contact the administrator.", "管理员未设置用户可选分组": "Administrator has not set user-selectable groups", "管理员给用户分配的分组(如 vip)不仅决定用户身份,还会影响后续两个功能:": "The group assigned to a user (e.g., vip) not only determines their identity, but also affects the following two features:", @@ -2543,14 +2672,16 @@ "系统数据统计": "System data statistics", "系统文档和帮助信息": "System documentation and help information", "系统消息": "System message", + "系统版本": "System Version", "系统管理功能": "System management functions", "系统设置": "System", "系统访问令牌": "System Access Token", + "约": "Approximately", "索引": "Index", "紧凑列表": "Compact list", + "纯字符串会直接覆盖整条请求头,或者点击“查看 JSON 示例”按 token 规则处理。": "", "累计签到": "Total check-ins", "累计获得": "Total received", - "约": "Approximately", "线路描述": "Route description", "组列表": "Group list", "组名": "Group name", @@ -2580,44 +2711,45 @@ "统计次数": "Statistical count", "统计额度": "Statistical quota", "继续": "Continue", - "缓存 Tokens": "Cache Tokens", "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Cache {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Cache {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio: {{ratio}})", + "缓存 Tokens": "Cache Tokens", "缓存: {{cacheRatio}}": "Cache: {{cacheRatio}}", "缓存价格:{{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "Cache price: {{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M tokens (Cache ratio: {{cacheRatio}})", "缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "Cache price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (Cache ratio: {{cacheRatio}})", "缓存倍率": "Cache ratio", "缓存倍率 {{cacheRatio}}": "Cache ratio {{cacheRatio}}", "缓存写": "Cache Write", - "缓存创建 Tokens": "Cache Creation Tokens", "缓存创建 {{price}} / 1M tokens": "Cache creation {{price}} / 1M tokens", "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio: {{ratio}})", + "缓存创建 Tokens": "Cache Creation Tokens", + "缓存创建: {{cacheCreationRatio}}": "Cache creation: {{cacheCreationRatio}}", "缓存创建: 1h {{cacheCreationRatio1h}}": "Cache creation: 1h {{cacheCreationRatio1h}}", "缓存创建: 5m {{cacheCreationRatio5m}}": "Cache creation: 5m {{cacheCreationRatio5m}}", "缓存创建: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Cache creation: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存创建: {{cacheCreationRatio}}": "Cache creation: {{cacheCreationRatio}}", + "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Cache creation: {{tokens}} / 1M * model ratio {{modelRatio}} * cache creation ratio {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存创建价格": "Input Cache Creation Price", "缓存创建价格 {{symbol}}{{price}} / 1M tokens": "Cache creation price {{symbol}}{{price}} / 1M tokens", - "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "Cache creation price total: 5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "Cache creation price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (Cache creation ratio: {{cacheCreationRatio}})", "缓存创建价格:{{symbol}}{{price}} / 1M tokens": "Cache creation price: {{symbol}}{{price}} / 1M tokens", + "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "Cache creation price total: 5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens", "缓存创建倍率": "Cache creation ratio", + "缓存创建倍率 {{cacheCreationRatio}}": "Cache creation ratio {{cacheCreationRatio}}", "缓存创建倍率 1h {{cacheCreationRatio1h}}": "Cache creation multiplier 1h {{cacheCreationRatio1h}}", "缓存创建倍率 5m {{cacheCreationRatio5m}}": "Cache creation multiplier 5m {{cacheCreationRatio5m}}", "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Cache creation ratio 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存创建倍率 {{cacheCreationRatio}}": "Cache creation ratio {{cacheCreationRatio}}", - "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Cache creation: {{tokens}} / 1M * model ratio {{modelRatio}} * cache creation ratio {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存条目数": "Cache Entries", "缓存目录": "Cache Directory", "缓存目录磁盘空间": "Cache Directory Disk Space", "缓存读": "Cache Read", "缓存读 {{price}} / 1M tokens": "Cache read {{price}} / 1M tokens", + "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Cache read: {{tokens}} / 1M * model ratio {{modelRatio}} * cache ratio {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存读取价格": "Input Cache Read Price", "缓存读取价格 {{symbol}}{{price}} / 1M tokens": "Cache read price {{symbol}}{{price}} / 1M tokens", "缓存读取价格:{{symbol}}{{price}} / 1M tokens": "Cache read price: {{symbol}}{{price}} / 1M tokens", "缓存读取价格:{{symbol}}{{total}} / 1M tokens": "Cache read price: {{symbol}}{{total}} / 1M tokens", - "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Cache read: {{tokens}} / 1M * model ratio {{modelRatio}} * cache ratio {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "缓存读取倍率": "", "缓存输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Cached input: {{tokens}} / 1M * model ratio {{modelRatio}} * cache ratio {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "编辑": "Edit", "编辑 OAuth 提供商": "Edit OAuth Provider", @@ -2629,6 +2761,7 @@ "编辑分类": "Edit Category", "编辑成功": "Edit Successful", "编辑提供商": "Edit Provider", + "编辑支付方式": "", "编辑方式": "Edit Mode", "编辑标签": "Edit Tag", "编辑模型": "Edit Model", @@ -2698,11 +2831,12 @@ "若你的 OIDC Provider 支持 Discovery Endpoint,你可以仅填写 OIDC Well-Known URL,系统会自动获取 OIDC 配置": "If your OIDC Provider supports Discovery Endpoint, you can only fill in the OIDC Well-Known URL, and the system will automatically obtain the OIDC configuration", "获取 Discovery 配置": "Fetch Discovery Configuration", "获取 Discovery 配置失败:": "Failed to fetch Discovery configuration: ", + "获取 io.net API Key": "Get io.net API Key", "获取 OIDC 配置失败,请检查网络状况和 Well-Known URL 是否正确": "Failed to get OIDC configuration, please check network status and whether the Well-Known URL is correct", "获取 OIDC 配置成功!": "OIDC configuration obtained successfully!", "获取 Ollama 版本失败": "Failed to get Ollama version", - "获取 io.net API Key": "Get io.net API Key", "获取2FA状态失败": "Failed to get Two-Factor Authentication status", + "获取令牌密钥失败": "", "获取充值配置失败": "Failed to get topup configuration", "获取充值配置异常": "Topup configuration error", "获取初始化状态失败": "Failed to get initialization status", @@ -2734,9 +2868,9 @@ "补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "Completion {{completion}} tokens * Output ratio {{completionRatio}}", "补全 {{completion}} tokens / 1M tokens * {{symbol}}{{price}}": "Completion {{completion}} tokens / 1M tokens * {{symbol}}{{price}}", "补全价格": "Completion Price", - "补全价格已锁定": "Completion price is locked", "补全价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "Completion price: {{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (Completion ratio: {{completionRatio}})", "补全价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens": "Completion price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens", + "补全价格已锁定": "Completion price is locked", "补全倍率": "Completion ratio", "补全倍率 {{completionRatio}}": "Completion ratio {{completionRatio}}", "补全倍率值": "Completion Ratio Value", @@ -2745,6 +2879,7 @@ "补单成功": "Order completed successfully", "表单引用错误,请刷新页面重试": "Form reference error, please refresh the page and try again", "表格视图": "Table view", + "覆盖": "Override", "覆盖模式:将完全替换现有的所有密钥": "Overwrite mode: completely replace all existing keys", "覆盖模板": "Override Template", "覆盖现有密钥": "Overwrite existing key", @@ -2755,10 +2890,8 @@ "规则为 JSON 数组;可视化与 JSON 模式共用同一份数据。": "Rules are a JSON array; visual and JSON modes share the same data.", "规则名称(可读性更好,也会出现在管理侧日志中)。": "Rule name (for better readability, also appears in admin logs).", "规则导航": "Rule Navigation", + "规则描述(可选)": "", "规则未找到,请刷新后重试": "Rule not found, please refresh and try again", - "视频": "Video", - "视频Remix": "Video remix", - "视频无法在当前浏览器中播放,这可能是由于:": "The video cannot be played in this browser, possibly because:", "角色": "Role", "解析响应数据时发生错误": "An error occurred while parsing response data", "解析密钥文件失败: {{msg}}": "Failed to parse key file: {{msg}}", @@ -2767,21 +2900,20 @@ "解绑 Passkey": "Remove Passkey", "解绑后将无法使用 Passkey 登录,确定要继续吗?": "After unbinding, you will not be able to login with Passkey. Are you sure you want to continue?", "解绑成功": "Unbind successful", - "警告": "Warning", - "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔": "Warning: After enabling keep-alive, if the channel fails after keep-alive data has been written, the system cannot retry. If you must enable it, it is recommended to set the Ping interval as large as possible", - "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "Warning: Disabling two-factor authentication will permanently delete your verification settings and all backup codes. This action is irreversible!", "计价币种": "Pricing Currency", "计算中": "Calculating", "计算成本": "Calculate Cost", "计算费用中...": "Calculating fees...", "计费乘数,倍率越低费用越低。例如倍率 0.5 表示半价。": "Billing multiplier — the lower the ratio, the lower the cost. E.g., ratio 0.5 means half price.", "计费开始": "Billing Start", + "计费摘要": "", "计费方式": "Billing Mode", "计费显示模式": "Billing Display Mode", "计费模式": "Billing mode", "计费类型": "Billing type", "计费过程": "Billing process", "订单号": "Order No.", + "订单支付方式": "Order Payment Method", "订阅": "Subscription", "订阅剩余": "Subscription Remaining", "订阅套餐": "Subscription Plans", @@ -2796,6 +2928,7 @@ "记录请求与错误日志IP": "Record request and error log IP", "设备": "Device", "设备类型偏好": "Device Type Preference", + "设置": "", "设置 Logo": "Set Logo", "设置2FA失败": "Failed to set up Two-Factor Authentication", "设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}": "Set discounts for different recharge amounts, where the key is the recharge amount and the value is the discount rate, for example: {\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}", @@ -2825,6 +2958,7 @@ "设置默认地区和特定模型的专用地区": "Set default region and dedicated regions for specific models", "设计与开发由": "Designed & Developed by", "设计版本": "b80c3466cb6feafeb3990c7820e10e50", + "设请求头": "", "访问 io.net 控制台的 API Keys 页面": "Visit the API Keys page of the io.net console", "访问容器": "Access Container", "访问模型部署功能需要先启用 io.net 部署服务": "Accessing model deployment features requires enabling the io.net deployment service first", @@ -2840,6 +2974,8 @@ "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "This model's completion ratio is fixed to {{ratio}} by the backend. The completion price cannot be changed here.", "该渠道已开启请求透传,参数覆写、模型重定向等 NewAPI 内置功能将失效,非最佳实践。": "Request pass-through is enabled for this channel; built-in NewAPI features such as parameter overrides and model redirection will be disabled. This is not a best practice.", "该渠道已开启请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "Request pass-through is enabled for this channel. Built-in NewAPI features such as parameter overrides, model redirection, and channel adaptation will be disabled. This is not a best practice. If this causes issues, please do not submit an issue.", + "该渠道暂无可处理的上游模型更新": "", + "该渠道未开启上游模型更新检测": "", "该规则未启用“作用域:包含规则名称”,无法按规则清空缓存。": "This rule has not enabled \"Scope: Include Rule Name\", cannot clear cache by rule.", "该规则未设置参数覆盖模板": "This rule has no parameter override template set", "该规则的缓存保留时长;0 表示使用默认 TTL:": "Cache retention duration for this rule; 0 means using default TTL: ", @@ -2851,10 +2987,10 @@ "语音输入": "Voice input", "语音输出": "Voice output", "说明": "Description", - "说明信息": "Description", "说明:": "Instructions:", "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。": "Note: Tests on this page use non-streaming requests. If a channel only supports streaming responses, tests may fail. Please rely on actual usage.", "说明:生成结果是可直接粘贴到渠道密钥里的 JSON(包含 access_token / refresh_token / account_id)。": "Note: The generated result is a JSON that can be pasted directly into the channel key (includes access_token / refresh_token / account_id).", + "说明信息": "Description", "请上传密钥文件": "Please upload the key file", "请上传密钥文件!": "Please upload the key file!", "请为渠道命名": "Please name the channel", @@ -2868,6 +3004,7 @@ "请先填写服务器地址": "Please fill in the server address first", "请先填写服务器地址,以自动生成完整的端点 URL": "Please enter the server address first to auto-generate full endpoint URLs", "请先开启并填写音频输入价格。": "Enable and fill in the audio input price first.", + "请先新增模型或从左侧列表选择一个模型": "", "请先粘贴回调 URL": "Please paste the callback URL first", "请先输入密钥": "Please enter the key first", "请先选择一个作为模板的模型": "Please select a model to use as the template first", @@ -2883,6 +3020,7 @@ "请再次输入新密码": "Please enter the new password again", "请前往个人设置 → 安全设置进行配置。": "Please go to Personal Settings → Security Settings to configure.", "请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "Do not over-trust this feature, IP can be spoofed, please use it in conjunction with gateways such as nginx and CDN", + "请在 Waffo 后台获取 API 密钥、商户 ID 以及 RSA 密钥对,并配置回调地址。": "", "请在系统设置页面编辑分组倍率以添加新的分组:": "Please edit Group ratios in system settings to add new groups:", "请填写完整的产品信息": "Please fill in complete product information", "请填写完整的管理员账号信息": "Please fill in the complete administrator account information", @@ -2901,7 +3039,6 @@ "请求参数无效": "Invalid request parameters", "请求发生错误": "An error occurred with the request", "请求发生错误: ": "An error occurred with the request: ", - "模型价格未配置": "Model Price Not Configured", "请求后端接口失败:": "Failed to request the backend interface: ", "请求失败": "Request failed", "请求头覆盖": "Request header override", @@ -2932,18 +3069,18 @@ "请至少选择一个渠道": "Please select at least one channel", "请输入 API Key,一行一个,格式:APIKey|Region": "Enter API Key, one per line, format: APIKey|Region", "请输入 API Key,格式:APIKey|Region": "Enter API Key, format: APIKey|Region", - "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com": "Please enter AZURE_OPENAI_ENDPOINT, e.g.: https://docs-test-001.openai.azure.com", "请输入 Authorization Endpoint": "Please enter Authorization Endpoint", + "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com": "Please enter AZURE_OPENAI_ENDPOINT, e.g.: https://docs-test-001.openai.azure.com", "请输入 Client ID": "Please enter Client ID", "请输入 Client Secret": "Please enter Client Secret", + "请输入 io.net API Key": "Please enter io.net API Key", + "请输入 io.net API Key(敏感信息不显示)": "Please enter io.net API Key (sensitive information not displayed)", "请输入 JSON 格式的 OAuth 凭据,例如:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}": "Please enter OAuth credentials in JSON format, e.g.:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}", "请输入 JSON 格式的密钥内容,例如:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}": "Please enter the key content in JSON format, for example:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}", "请输入 OIDC 的 Well-Known URL": "Please enter the Well-Known URL for OIDC", "请输入 Slug": "Please enter Slug", "请输入 Token Endpoint": "Please enter Token Endpoint", "请输入 User Info Endpoint": "Please enter User Info Endpoint", - "请输入 io.net API Key": "Please enter io.net API Key", - "请输入 io.net API Key(敏感信息不显示)": "Please enter io.net API Key (sensitive information not displayed)", "请输入6位验证码或8位备用码": "Please enter a 6-digit verification code or 8-digit backup code", "请输入API地址": "Please enter the API address", "请输入API地址!": "Please enter the API address!", @@ -2953,9 +3090,9 @@ "请输入Gotify服务器地址": "Please enter Gotify server address", "请输入Gotify服务器地址,例如: https://gotify.example.com": "Please enter Gotify server address, e.g.: https://gotify.example.com", "请输入JSON数组,如 [\"model-a\",\"model-b\"]": "Enter a JSON array, e.g. [\"model-a\",\"model-b\"]", - "请输入URL链接": "Please enter the URL link", "请输入Uptime Kuma地址": "Please enter the Uptime Kuma address", "请输入Uptime Kuma服务地址,如:https://status.example.com": "Please enter the Uptime Kuma service address, such as: https://status.example.com", + "请输入URL链接": "Please enter the URL link", "请输入Webhook地址": "Please enter the Webhook address", "请输入Webhook地址,例如: https://example.com/webhook": "Please enter the Webhook URL, e.g.: https://example.com/webhook", "请输入你的账户名以确认删除!": "Please enter your account name to confirm deletion!", @@ -2980,14 +3117,14 @@ "请输入备注(仅管理员可见)": "Please enter a remark (only visible to administrators)", "请输入套餐标题": "Please enter plan title", "请输入完整的 JSON 格式密钥内容": "Please enter the complete JSON format key content", - "请输入完整的URL链接": "Please enter the complete URL link", "请输入完整的URL,例如:https://api.openai.com/v1/chat/completions": "Please enter complete URL, e.g.: https://api.openai.com/v1/chat/completions", + "请输入完整的URL链接": "Please enter the complete URL link", "请输入容器名称": "Please enter container name", "请输入密码": "Please enter password", "请输入密钥": "Please enter the key", - "请输入密钥!": "Please enter the key!", "请输入密钥,一行一个": "Please enter the key, one per line", "请输入密钥,一行一个,格式:AccessKey|SecretAccessKey|Region": "Enter keys one per line, format: AccessKey|SecretAccessKey|Region", + "请输入密钥!": "Please enter the key!", "请输入延长时长": "Please enter extension duration", "请输入总额度": "Please enter total quota", "请输入您的密码": "Please enter your password", @@ -3040,9 +3177,9 @@ "请输入认证器验证码或备用码": "Please enter authenticator verification code or backup code", "请输入说明": "Please enter the description", "请输入运行时长": "Please enter runtime duration", + "请输入邮箱!": "Please enter your email!", "请输入邮箱地址": "Please enter the email address", "请输入邮箱验证码!": "Please enter the email verification code!", - "请输入邮箱!": "Please enter your email!", "请输入部署名称": "Please enter deployment name", "请输入部署名称以完成二次确认": "Enter deployment name to complete secondary confirmation", "请输入部署地区,例如:us-central1\n支持使用模型映射格式\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}": "Please enter the deployment region, for example: us-central1\nSupports using model mapping format\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}", @@ -3085,12 +3222,17 @@ "请选择该渠道所支持的模型,留空则不更改": "Please select the models supported by the channel, leaving blank will not change", "请选择过期时间": "Please select expiration time", "请选择通知方式": "Please select notification method", + "调整额度": "Adjust Quota", + "调整额度成功": "Quota adjusted successfully", "调用次数": "Call Count", "调用次数分布": "Models call distribution", "调用次数排行": "Models call ranking", "调用趋势": "Call trend", "调试信息": "Debug information", "谨慎": "Cautious", + "警告": "Warning", + "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔": "Warning: After enabling keep-alive, if the channel fails after keep-alive data has been written, the system cannot retry. If you must enable it, it is recommended to set the Ping interval as large as possible", + "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "Warning: Disabling two-factor authentication will permanently delete your verification settings and all backup codes. This action is irreversible!", "豆包": "Doubao", "账单": "Bills", "账户充值": "Account recharge", @@ -3125,18 +3267,21 @@ "路径正则": "Path Regex", "路径正则(每行一个)": "Path Regex (one per line)", "跳转": "Jump", + "转大写": "", + "转小写": "", "转换": "Convert", "轮询": "Polling", "轮询模式": "Polling mode", "轮询模式必须搭配Redis和内存缓存功能使用,否则性能将大幅降低,并且无法实现轮询功能": "Polling mode must be used with Redis and memory cache functions, otherwise the performance will be significantly reduced and the polling function will not be implemented", "软错误": "soft errors", "输入": "Input", + "输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "Input {{input}} tokens / 1M tokens * {{symbol}}{{price}}", + "输入 {{price}} / 1M tokens": "Input {{price}} / 1M tokens", + "输入 $/1M tokens": "", "输入 OIDC 的 Authorization Endpoint": "Enter OIDC Authorization Endpoint", "输入 OIDC 的 Client ID": "Enter OIDC Client ID", "输入 OIDC 的 Token Endpoint": "Enter OIDC Token Endpoint", "输入 OIDC 的 Userinfo Endpoint": "Enter OIDC Userinfo Endpoint", - "输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "Input {{input}} tokens / 1M tokens * {{symbol}}{{price}}", - "输入 {{price}} / 1M tokens": "Input {{price}} / 1M tokens", "输入IP地址后回车,如:8.8.8.8": "Enter IP address and press Enter, e.g.: 8.8.8.8", "输入JSON对象": "Enter JSON Object", "输入与缓存价格合计 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Input and cache pricing subtotal * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", @@ -3146,6 +3291,7 @@ "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "Input Price: {{symbol}}{{price}} / 1M tokens{{audioPrice}}", "输入你注册的 LinuxDO OAuth APP 的 ID": "Enter the ID of your registered LinuxDO OAuth APP", "输入你的账户名{{username}}以确认删除": "Enter your account name{{username}} to confirm deletion", + "输入倍率": "", "输入域名后回车": "Enter domain and press Enter", "输入域名后回车,如:example.com": "Enter domain and press Enter, e.g.: example.com", "输入基础 URL": "Enter base URL", @@ -3155,6 +3301,7 @@ "输入模型倍率": "Enter model ratio", "输入模型名称,例如 gpt-4.1": "Enter a model name, for example gpt-4.1", "输入每次价格": "Enter per-use price", + "输入每次调用价格": "", "输入端口后回车,如:80 或 8000-8999": "Enter port and press Enter, e.g.: 80 or 8000-8999", "输入系统提示词,用户的系统提示词将优先于此设置": "Enter system prompt, user's system prompt will take priority over this setting", "输入自定义模型名称": "Enter Custom Model Name", @@ -3170,14 +3317,14 @@ "输入验证码完成设置": "Enter verification code to complete setup", "输出": "Output", "输出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}": "Output {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}}", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Output: {{tokens}} / 1M * model ratio {{modelRatio}} * completion ratio {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Output: {{tokens}} / 1M * model ratio {{modelRatio}} * output ratio {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "输出价格": "Output Price", "输出价格 {{symbol}}{{price}} / 1M tokens": "Output Price {{symbol}}{{price}} / 1M tokens", "输出价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "Output price: {{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (Completion ratio: {{completionRatio}})", "输出价格:{{symbol}}{{price}} / 1M tokens": "Output Price: {{symbol}}{{price}} / 1M tokens", "输出价格:{{symbol}}{{total}} / 1M tokens": "Output Price: {{symbol}}{{total}} / 1M tokens", "输出倍率 {{completionRatio}}": "Output ratio {{completionRatio}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Output: {{tokens}} / 1M * model ratio {{modelRatio}} * completion ratio {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Output: {{tokens}} / 1M * model ratio {{modelRatio}} * output ratio {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "边栏设置": "Sidebar Settings", "过期于": "Expires at", "过期时间": "Expiration time", @@ -3191,6 +3338,7 @@ "运行时长(小时)": "Runtime Duration (hours)", "返回修改": "Go back and edit", "返回登录": "Return to Login", + "返回错误": "", "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "This editor uses prices by default and converts them back into the ratio JSON required by the backend when saved.", "这些价格都是可选项,不填也可以。": "All of these prices are optional and can be left empty.", "这将删除超过 10 分钟未使用的临时缓存文件": "This will delete temporary cache files that have not been used for more than 10 minutes", @@ -3252,6 +3400,7 @@ "选择预设...": "Select preset...", "选择预设模板(可选)": "Select Preset Template (optional)", "透传请求体": "Pass through body", + "透传请求头": "", "递归": "Recursive", "递归策略": "Recursion Strategy", "通义千问": "Qwen", @@ -3322,7 +3471,9 @@ "配置 SMTP": "Configure SMTP", "配置 Telegram 登录": "Configure Telegram Login", "配置 Turnstile": "Configure Turnstile", + "配置 Waffo 充值时可用的支付方式,保存后在充值页面展示给用户。": "", "配置 WeChat Server": "Configure WeChat Server", + "配置:": "Config:", "配置后的效果:": "After configuration:", "配置和消息已全部重置": "Configuration and messages have been completely reset", "配置套餐的有效时长": "Configure the plan validity duration", @@ -3341,13 +3492,13 @@ "配置自定义 OAuth 提供商,支持 GitHub Enterprise、GitLab、Gitea、Nextcloud、Keycloak、ORY 等兼容 OAuth 2.0 协议的身份提供商": "Configure custom OAuth providers, supports GitHub Enterprise, GitLab, Gitea, Nextcloud, Keycloak, ORY and other OAuth 2.0 compatible identity providers", "配置说明": "Configuration instructions", "配置邮箱域名白名单": "Configure email domain whitelist", - "配置:": "Config:", "重启部署失败": "Failed to restart deployment", "重命名部署": "Rename Deployment", "重复提交": "Duplicate submission", "重复的键名": "Duplicate key name", "重复的键名,此值将被后面的同名键覆盖": "Duplicate key name, this value will be overridden by the subsequent key with the same name", "重定向 URL 填": "Redirect URL fill", + "重新上传": "", "重新发送": "Resend", "重新生成": "Regenerate", "重新生成备用码": "Regenerate backup codes", @@ -3401,15 +3552,15 @@ "限制周期统一使用上方配置的“限制周期”值。": "The limit period uniformly uses the \"limit period\" value configured above.", "限流": "Rate Limiting", "限购": "Limit", - "随机": "Random", - "随机模式": "Random mode", - "随机种子 (留空为随机)": "Random Seed (leave blank for random)", "隐私政策": "Privacy Policy", "隐私政策已更新": "Privacy policy updated", "隐私政策更新失败": "Privacy policy update failed", "隐私设置": "Privacy settings", "隐藏操作项": "Hide actions", "隐藏调试": "Hide debug", + "随机": "Random", + "随机模式": "Random mode", + "随机种子 (留空为随机)": "Random Seed (leave blank for random)", "零一万物": "Yi", "需要安全验证": "Security verification required", "需要添加的额度(支持负数)": "Need to add quota (supports negative numbers)", @@ -3427,13 +3578,15 @@ "音频补全价格": "Audio Completion Price", "音频补全价格:{{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (音频补全倍率: {{audioCompRatio}})": "Audio completion price: {{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (Audio completion ratio: {{audioCompRatio}})", "音频补全价格:{{symbol}}{{price}} / 1M tokens": "Audio completion price: {{symbol}}{{price}} / 1M tokens", + "音频补全倍率": "", "音频补全倍率(仅部分模型支持该计费)": "Audio completion ratio (only supported by some models for this billing)", + "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Audio input: {{tokens}} / 1M * model ratio {{modelRatio}} * audio ratio {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "音频输入价格": "Audio Input Price", "音频输入价格:{{symbol}}{{price}} / 1M tokens": "Audio input price: {{symbol}}{{price}} / 1M tokens", + "音频输入倍率": "", "音频输入相关的倍率设置,键为模型名称,值为倍率": "Audio input related ratio settings, key is model name, value is ratio", - "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Audio input: {{tokens}} / 1M * model ratio {{modelRatio}} * audio ratio {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "Audio output completion related ratio settings, key is model name, value is ratio", "音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Audio output: {{tokens}} / 1M * model ratio {{modelRatio}} * audio ratio {{audioRatio}} * audio completion ratio {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "Audio output completion related ratio settings, key is model name, value is ratio", "页脚": "Footer", "页面未找到,请检查您的浏览器地址是否正确": "Page not found, please check if your browser address is correct", "页面渲染出错,请刷新页面重试": "An error occurred while rendering the page. Please refresh and try again.", @@ -3451,9 +3604,10 @@ "预览更新": "Preview update", "预览模板": "Preview Template", "预览请求体": "Preview request body", - "预警阈值必须为正数": "Warning threshold must be a positive number", "预计结束": "Estimated End", + "预计结果": "Estimated result", "预设模板": "Preset Template", + "预警阈值必须为正数": "Warning threshold must be a positive number", "频率惩罚,减少重复词汇的出现": "Frequency penalty, reduces repeated vocabulary", "频率限制的周期(分钟)": "Rate limit period (minutes)", "颜色": "Color", @@ -3511,10 +3665,6 @@ "默认折叠侧边栏": "Default collapse sidebar", "默认测试模型": "Default Test Model", "默认用户消息": "Default User Message", - "默认补全倍率": "Default completion ratio", - "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(Currently only supports Epay interface, the default callback address is the server address above!)", - ",当前无生效订阅,将自动使用钱包": ", no active subscription. Wallet will be used automatically.", - ",时间:": ",time:", - ",点击更新": ", click Update" + "默认补全倍率": "Default completion ratio" } } diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json index 61c6c370..ef92f79d 100644 --- a/web/src/i18n/locales/fr.json +++ b/web/src/i18n/locales/fr.json @@ -1,38 +1,62 @@ { "translation": { - " + Web搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_many": " + Recherche Web {{count}} fois / 1K fois * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " + Web搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_one": " + Recherche Web {{count}} fois / 1K fois * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", + " + Web搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_many": " + Recherche Web {{count}} fois / 1K fois * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " + Web搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_other": " + Recherche Web {{count}} fois / 1K fois * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " + 图片生成调用 {{symbol}}{{price}} / 1次 * {{ratioType}} {{ratio}}": " + Appel de génération d'image {{symbol}}{{price}} / 1 fois * {{ratioType}} {{ratio}}", - " + 文件搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_many": " + Recherche de fichiers {{count}} fois / 1K fois * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " + 文件搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_one": " + Recherche de fichiers {{count}} fois / 1K fois * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", + " + 文件搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_many": " + Recherche de fichiers {{count}} fois / 1K fois * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " + 文件搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_other": " + Recherche de fichiers {{count}} fois / 1K fois * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " 个模型设置相同的值": " modèles avec la même valeur", " 吗?": " ?", " 秒": "s", " 秒。": " secondes.", - "$/1M tokens": "$/1M tokens", - "(筛选后显示 {{count}} 条)_many": "(Affichage de {{count}} éléments après filtrage)", + ",当前无生效订阅,将自动使用钱包": ", aucun abonnement actif, le portefeuille sera utilisé automatiquement.", + ",时间:": ", time:", + ",点击更新": ", cliquez sur Mettre à jour", + "(共 {{total}} 个,省略 {{omit}} 个)": "", + "(共 {{total}} 个)": "", + "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(Actuellement, seule l'interface Epay est prise en charge, l'adresse du serveur ci-dessus est utilisée par défaut comme adresse de rappel !)", "(筛选后显示 {{count}} 条)_one": "(Showing {{count}} item after filtering)", + "(筛选后显示 {{count}} 条)_many": "(Affichage de {{count}} éléments après filtrage)", "(筛选后显示 {{count}} 条)_other": "(Showing {{count}} items after filtering)", "(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(Entrée {{input}} tokens / 1M tokens * {{symbol}}{{price}}", "(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(Entrée {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + Entrée audio {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}", "(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(Entrée {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + Cache {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}", "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(Entrée {{nonImageInput}} tokens + Entrée image {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}", + "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "La valeur maximale de [Nombre maximal de requêtes] et [Nombre maximal d'achèvements de requêtes] est 2147483647.", + "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[Nombre maximal de requêtes] doit être supérieur ou égal à 0, [Nombre maximal d'achèvements de requêtes] doit être supérieur ou égal à 1.", + "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", + "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", + "{{count}} 项操作_one": "", + "{{count}} 项操作_many": "", + "{{count}} 项操作_other": "", + "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", + "{{name}} ID": "{{name}} ID", + "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", + "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", + "「用户可选」决定用户创建令牌时能否自主选择该分组。未勾选的分组只能由管理员在后台分配给用户,不会出现在用户的令牌创建页面中。": "\"User Selectable\" controls whether users can choose this group when creating tokens. Unchecked groups can only be assigned by admin and won't appear in the token creation page.", + "• 视频服务商的跨域限制": "• Des restrictions cross-origin imposées par le fournisseur vidéo", + "• 防盗链保护机制": "• Un mécanisme de protection anti-hotlink", + "• 需要特定的请求头或认证": "• Des en-têtes ou une authentification spécifiques sont requis", + "© {{currentYear}}": "© {{currentYear}}", + "| 基于": " | Basé sur ", + "$/1M tokens": "$/1M tokens", + "$/次": "", "0 - 最低": "0 - La plus basse", "0 表示不限": "0 signifie illimité", "0.002-1之间的小数": "Décimal entre 0,002-1", "0.1以上的小数": "Décimal supérieur à 0,1", - "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。": "1) Cliquez sur « Ouvrir la page d'autorisation » pour vous connecter ; 2) Le navigateur redirigera vers localhost (ce n'est pas grave si la page ne s'ouvre pas) ; 3) Copiez l'URL complète de la barre d'adresse et collez-la ci-dessous ; 4) Cliquez sur « Générer et remplir ».", "1. 管理员在此创建分组并设置倍率": "1. Admin creates groups and sets ratios here", + "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。": "1) Cliquez sur « Ouvrir la page d'autorisation » pour vous connecter ; 2) Le navigateur redirigera vers localhost (ce n'est pas grave si la page ne s'ouvre pas) ; 3) Copiez l'URL complète de la barre d'adresse et collez-la ci-dessous ; 4) Cliquez sur « Générer et remplir ».", "10 - 最高": "10 - La plus haute", "1h缓存创建 {{price}} / 1M tokens": "Création de cache 1h {{price}} / 1M tokens", "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Création de cache 1h {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Création du cache 1h {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio : {{ratio}})", + "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "Création du cache 1h : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de création du cache 1h {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", "1h缓存创建价格 {{symbol}}{{price}} / 1M tokens": "Prix de création du cache 1h {{symbol}}{{price}} / 1M tokens", "1h缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h缓存创建倍率: {{cacheCreationRatio1h}})": "Prix de création de cache 1h : {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (ratio de création 1h : {{cacheCreationRatio1h}})", "1h缓存创建价格:{{symbol}}{{price}} / 1M tokens": "Prix de création du cache 1h : {{symbol}}{{price}} / 1M tokens", - "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "Création du cache 1h : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de création du cache 1h {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", "2 - 低": "2 - Basse", "2. 管理员在用户管理中将用户分配到对应分组(如 vip)": "2. Admin assigns users to groups in User Management (e.g., vip)", "2025年5月10日后添加的渠道,不需要再在部署的时候移除模型名称中的\".\"": "Après le 10 mai 2025, les canaux ajoutés n'ont plus besoin de supprimer le point dans le nom du modèle lors du déploiement", @@ -43,22 +67,25 @@ "5m缓存创建 {{price}} / 1M tokens": "Création de cache 5m {{price}} / 1M tokens", "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Création de cache 5m {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Création du cache 5m {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio : {{ratio}})", + "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "Création du cache 5m : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de création du cache 5m {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", "5m缓存创建价格 {{symbol}}{{price}} / 1M tokens": "Prix de création du cache 5m {{symbol}}{{price}} / 1M tokens", "5m缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m缓存创建倍率: {{cacheCreationRatio5m}})": "Prix de création de cache 5m : {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (ratio de création 5m : {{cacheCreationRatio5m}})", "5m缓存创建价格:{{symbol}}{{price}} / 1M tokens": "Prix de création du cache 5m : {{symbol}}{{price}} / 1M tokens", - "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "Création du cache 5m : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de création du cache 5m {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", "8 - 高": "8 - Haute", "AGPL v3.0协议": "Licence AGPL v3.0", "AI 对话": "Conversation IA", "AI模型测试环境": "Environnement de test de modèle d'IA", "AI模型配置": "Configuration du modèle d'IA", "AK/SK 模式:使用 AccessKey 和 SecretAccessKey;API Key 模式:使用 API Key": "Mode AK/SK : utiliser AccessKey et SecretAccessKey ; mode API Key : utiliser API Key", + "anthropic-beta JSON 示例": "", "API Key": "API Key", "API Key 模式下不支持批量创建": "Création en lot non prise en charge en mode clé API", "API Key 验证失败": "API Key verification failed", "API Key 验证成功!连接到 io.net 服务正常": "API Key verification successful! Connection to io.net service is normal", "API 地址和相关配置": "URL de l'API et configuration associée", "API 密钥": "Clé API", + "API 密钥 (沙盒)": "", + "API 密钥 (生产)": "", "API 文档": "Docs API", "API 配置": "Config. API", "API令牌管理": "Jetons API", @@ -68,14 +95,13 @@ "API地址": "URL de base", "API渠道配置": "Configuration du canal de l'API", "API端点": "Points de terminaison de l'API", - "Authorization Endpoint": "Point de terminaison d'autorisation", "Authorization callback URL 填": "Remplir l'URL de rappel d'autorisation", + "Authorization Endpoint": "Point de terminaison d'autorisation", + "auto分组调用链路": "Chaîne d'appels de groupe auto", "Bark推送URL": "URL de notification Bark", "Bark推送URL必须以http://或https://开头": "L'URL de notification Bark doit commencer par http:// ou https://", "Bark通知": "Notification Bark", "Basic Auth 头": "En-tête Basic Auth", - "CPU 使用率超过此值时拒绝请求": "Rejeter les requêtes lorsque l'utilisation du CPU dépasse cette valeur", - "CPU 阈值 (%)": "Seuil CPU (%)", "Cached tokens": "Cached tokens", "Cached tokens 占比口径由后端返回:Claude 语义按 cached/(prompt+cached),其余按 cached/prompt。": "Le ratio de cached tokens est renvoyé par le backend : la sémantique Claude calcule cached/(prompt+cached), les autres calculent cached/prompt.", "Changing batch type to:": "Changement du type de lot en :", @@ -91,13 +117,20 @@ "Client Secret": "Secret client", "Codex 授权": "Autorisation Codex", "Codex 渠道不支持批量创建": "Le canal Codex ne prend pas en charge la création par lot", + "common.changeLanguage": "Changer de langue", "Completion tokens": "Completion tokens", "Configuration": "Configuration", + "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。": "context_int/context_string lit depuis le contexte de la requête ; gjson lit depuis le body JSON de la requête d'entrée via le chemin gjson.", + "CPU 使用率超过此值时拒绝请求": "Rejeter les requêtes lorsque l'utilisation du CPU dépasse cette valeur", + "CPU 阈值 (%)": "Seuil CPU (%)", "Creem API 密钥,敏感信息不显示": "Clé API Creem, les informations sensibles ne sont pas affichées", "Creem Setting Tips": "Creem ne prend en charge que des produits à montant fixe préconfigurés. Ces produits et leurs prix doivent être créés et configurés à l'avance sur le site Creem, les recharges à montant dynamique ne sont donc pas prises en charge. Configurez le nom et le prix du produit sur Creem, récupérez l'identifiant du produit, puis remplissez-le ci-dessous. Définissez enfin le montant et le prix affiché dans new-api.", "Creem 介绍": "Présentation de Creem", "Creem 充值": "Recharge Creem", "Creem 设置": "Paramètres Creem", + "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。": "default and vip can only be assigned to users by admin in \"User Management\". Suitable for tiered pricing, internal testing, or other scenarios where user self-selection is not desired.", + "default为默认设置,可单独设置每个分类的安全等级": "\"default\" est le paramètre par défaut, et chaque catégorie peut être définie séparément", + "default为默认设置,可单独设置每个模型的版本": "\"default\" est le paramètre par défaut, et chaque modèle peut être défini séparément", "Dify渠道只适配chatflow和agent,并且agent不支持图片!": "Le canal Dify ne prend en charge que chatflow et agent, et l'agent ne prend pas en charge les images !", "Discord": "Discord", "Discord Client ID": "ID client Discord", @@ -107,11 +140,10 @@ "Discovery scopes": "Discovery scopes", "Discovery 建议 scopes:": "Scopes Discovery recommandés :", "EUR (欧元)": "EUR (Euro)", + "false": "faux", "GC 已执行": "GC exécuté", "GC 执行失败": "Échec de l'exécution du GC", "GC 次数": "Nombre de GC", - "GPU/容器": "GPU/Container", - "GPU数量": "Number of GPUs", "Gemini安全设置": "Paramètres de sécurité Gemini", "Gemini思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Adaptation de la pensée Gemini BudgetTokens = MaxTokens * BudgetTokens pourcentage", "Gemini思考适配设置": "Paramètres d'adaptation de la pensée Gemini", @@ -126,10 +158,14 @@ "Gotify服务器地址": "Adresse du serveur Gotify", "Gotify服务器地址必须以http://或https://开头": "L'adresse du serveur Gotify doit commencer par http:// ou https://", "Gotify通知": "Notification Gotify", + "GPU/容器": "GPU/Container", + "GPU数量": "Number of GPUs", "Grok设置": "Paramètres Grok", "Haiku 模型": "Modèle Haiku", "Homepage URL 填": "Remplir l'URL de la page d'accueil", "ID": "ID", + "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护": "include_obfuscation contrôle les champs d'obfuscation dans le flux Responses. Désactivé par défaut pour éviter que les clients ne désactivent cette protection de sécurité", + "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "Le champ inference_geo contrôle la région de résidence des données d'inférence de Claude. Désactivé par défaut pour éviter la transmission non autorisée d'informations géographiques", "IP": "IP", "IP白名单": "IP Whitelist", "IP白名单(支持CIDR表达式)": "Liste blanche d'adresses IP (prise en charge des expressions CIDR)", @@ -157,8 +193,8 @@ "LinuxDO": "LinuxDO", "LinuxDO ID": "ID LinuxDO", "Logo 图片地址": "Adresse de l'image du logo", - "MIT许可证": "Licence MIT", "Midjourney 任务记录": "Tâches Midjourney", + "MIT许可证": "Licence MIT", "New API项目仓库地址:": "Adresse du référentiel du projet New API : ", "NewAPI 默认不会将入口请求的 User-Agent 透传到上游渠道;该条件仅用于识别访问本站点的客户端。": "NewAPI ne transmet pas le User-Agent de la requête entrante aux canaux en amont par défaut ; cette condition sert uniquement à identifier les clients accédant à ce site.", "OAuth Client ID": "OAuth Client ID", @@ -169,7 +205,6 @@ "Ollama 模型管理": "Ollama Model Management", "Ollama 版本信息": "Ollama Version Info", "Opus 模型": "Modèle Opus", - "POST 参数": "Paramètres POST", "Passkey": "Passkey", "Passkey 已解绑": "Passkey délié", "Passkey 已重置": "Le Passkey a été réinitialisé", @@ -177,50 +212,71 @@ "Passkey 注册失败,请重试": "L'enregistrement du Passkey a échoué. Veuillez réessayer.", "Passkey 注册成功": "Enregistrement du Passkey réussi", "Passkey 登录": "Connexion avec Passkey", + "Pay Method Name": "", + "Pay Method Type": "", "Ping间隔(秒)": "Intervalle de ping (secondes)", + "POST 参数": "Paramètres POST", + "price_xxx 的商品价格 ID,新建产品后可获得": "ID de prix du produit price_xxx, peut être obtenu après la création d'un nouveau produit", "Prompt cache hit tokens": "Prompt cache hit tokens", "Prompt tokens": "Prompt tokens", "Reasoning Effort": "Effort de raisonnement", "Request ID": "Request ID", + "RSA 私钥 (沙盒)": "", + "RSA 私钥 (生产)": "", + "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "Le champ safety_identifier aide OpenAI à identifier les utilisateurs d'applications susceptibles de violer les politiques d'utilisation. Désactivé par défaut pour protéger la confidentialité des utilisateurs", + "Scopes(可选)": "Scopes (optionnel)", + "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "Le champ service_tier est utilisé pour spécifier le niveau de service. Permettre le passage peut entraîner une facturation plus élevée que prévu. Désactivé par défaut pour éviter des frais supplémentaires", + "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "Clé secrète Stripe sk_xxx ou rk_xxx, les informations sensibles ne sont pas affichées", "SMTP 发送者邮箱": "Adresse e-mail de l'expéditeur SMTP", "SMTP 服务器地址": "Adresse du serveur SMTP", "SMTP 端口": "Port SMTP", "SMTP 访问凭证": "Informations d'identification d'accès SMTP", "SMTP 账户": "Compte SMTP", + "Sonnet 模型": "Modèle Sonnet", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "Le champ speed contrôle le mode de vitesse d'inférence de Claude. Désactivé par défaut pour éviter un passage involontaire au mode fast", "SSE 事件": "Événement SSE", "SSE数据流": "Flux de données SSE", "SSRF防护开关详细说明": "L'interrupteur principal contrôle si la protection SSRF est activée. Lorsqu'elle est désactivée, toutes les vérifications SSRF sont contournées, autorisant l'accès à n'importe quelle URL. ⚠️ Ne désactivez cette fonctionnalité que dans des environnements entièrement fiables.", "SSRF防护设置": "Protection SSRF", "SSRF防护详细说明": "La protection SSRF empêche les utilisateurs malveillants d'utiliser votre serveur pour accéder aux ressources du réseau interne. Configurez des listes blanches pour les domaines/IP de confiance et limitez les ports autorisés. S'applique aux téléchargements de fichiers, aux webhooks et aux notifications.", - "Scopes(可选)": "Scopes (optionnel)", - "Sonnet 模型": "Modèle Sonnet", + "standard 已被移除,vip 用户看不到": "standard has been removed, vip users cannot see it", + "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "Le champ store autorise OpenAI à stocker les données de requête pour l'évaluation et l'optimisation du produit. Désactivé par défaut. L'activation peut causer un dysfonctionnement de Codex", "Stripe 设置": "Paramètres Stripe", "Stripe/Creem 商品ID(可选)": "ID produit Stripe/Creem (optionnel)", "Stripe/Creem 需在第三方平台创建商品并填入 ID": "Les produits Stripe/Creem doivent être créés sur la plateforme tierce et l'ID doit être renseigné", - "TTL(秒)": "TTL (secondes)", - "TTL(秒,0 表示默认)": "TTL (secondes, 0 pour la valeur par défaut)", "Telegram": "Telegram", "Telegram Bot Token": "Jeton du bot Telegram", "Telegram Bot 名称": "Nom du bot Telegram", "Telegram ID": "ID Telegram", "Token Endpoint": "Point de terminaison du jeton", + "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。": "Les tokens sont convertis en quota/nombre d'utilisations selon le ratio. Après la requête, la différence est réglée (déduction supplémentaire/remboursement).", "Total tokens": "Total tokens", + "true": "vrai", + "TTL(秒,0 表示默认)": "TTL (secondes, 0 pour la valeur par défaut)", + "TTL(秒)": "TTL (secondes)", "Turnstile Secret Key": "Clé secrète Turnstile", "Turnstile Site Key": "Clé du site Turnstile", - "URL 标识,只能包含小写字母、数字和连字符": "Identifiant URL, uniquement lettres minuscules, chiffres et tirets autorisés", - "URL链接": "Lien URL", - "USD (美元)": "USD (Dollar US)", "Unix时间戳": "Horodatage Unix", "Uptime Kuma地址": "Adresse Uptime Kuma", "Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)": "Catégories de surveillance Uptime Kuma, vous pouvez configurer plusieurs catégories de surveillance pour l'affichage de l'état du service (maximum 20)", + "URL 标识,只能包含小写字母、数字和连字符": "Identifiant URL, uniquement lettres minuscules, chiffres et tirets autorisés", + "URL链接": "Lien URL", + "USD (美元)": "USD (Dollar US)", "User Info Endpoint": "Point de terminaison des informations utilisateur", "User-Agent include(每行一个,可不写)": "User-Agent include (un par ligne, optionnel)", "Value 正则": "Regex de valeur", "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AI ne prend pas en charge le champ functionResponse.id. Lorsqu'il est activé, ce champ sera automatiquement supprimé", - "WeChat Server 服务器地址": "Adresse du serveur WeChat Server", - "WeChat Server 访问凭证": "Informations d'identification d'accès au serveur WeChat", - "Web 搜索调用 {{webSearchCallCount}} 次": "Recherche Web appelée {{webSearchCallCount}} fois", + "Waffo API 参数,可空,例如:CREDITCARD,DEBITCARD(最多64位)": "", + "Waffo API 参数,可空(最多64位)": "", + "Waffo 充值": "", + "Waffo 充值的最低数量,默认 1": "", + "Waffo 公钥 (沙盒)": "", + "Waffo 公钥 (生产)": "", + "Waffo 商户 ID": "", + "Waffo 是一个支付聚合平台,支持多种支付方式。": "", + "Waffo 设置": "", "Web 搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Recherche Web : {{count}} / 1K * prix unitaire {{price}} * {{ratioType}} {{ratio}} = {{amount}}", + "Web 搜索调用 {{webSearchCallCount}} 次": "Recherche Web appelée {{webSearchCallCount}} fois", "Webhook 密钥": "Clé Webhook", "Webhook 签名密钥": "Clé de signature Webhook", "Webhook地址": "URL du Webhook", @@ -228,62 +284,38 @@ "Webhook请求结构说明": "Description de la structure de la requête Webhook", "Webhook通知": "Notification par Webhook", "Web搜索价格:{{symbol}}{{price}} / 1K 次": "Prix de recherche Web : {{symbol}}{{price}} / 1K fois", + "WeChat Server 服务器地址": "Adresse du serveur WeChat Server", + "WeChat Server 访问凭证": "Informations d'identification d'accès au serveur WeChat", "Well-Known URL": "URL bien connue", "Well-Known URL 必须以 http:// 或 https:// 开头": "L'URL bien connue doit commencer par http:// ou https://", + "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示": "Clé de signature Webhook whsec_xxx, les informations sensibles ne sont pas affichées", "Worker地址": "Adresse du Worker", "Worker密钥": "Clé du Worker", - "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "La valeur maximale de [Nombre maximal de requêtes] et [Nombre maximal d'achèvements de requêtes] est 2147483647.", - "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[Nombre maximal de requêtes] doit être supérieur ou égal à 0, [Nombre maximal d'achèvements de requêtes] doit être supérieur ou égal à 1.", - "auto分组调用链路": "Chaîne d'appels de groupe auto", - "common.changeLanguage": "Changer de langue", - "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。": "context_int/context_string lit depuis le contexte de la requête ; gjson lit depuis le body JSON de la requête d'entrée via le chemin gjson.", - "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。": "default and vip can only be assigned to users by admin in \"User Management\". Suitable for tiered pricing, internal testing, or other scenarios where user self-selection is not desired.", - "default为默认设置,可单独设置每个分类的安全等级": "\"default\" est le paramètre par défaut, et chaque catégorie peut être définie séparément", - "default为默认设置,可单独设置每个模型的版本": "\"default\" est le paramètre par défaut, et chaque modèle peut être défini séparément", - "false": "faux", - "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护": "include_obfuscation contrôle les champs d'obfuscation dans le flux Responses. Désactivé par défaut pour éviter que les clients ne désactivent cette protection de sécurité", - "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "Le champ inference_geo contrôle la région de résidence des données d'inférence de Claude. Désactivé par défaut pour éviter la transmission non autorisée d'informations géographiques", - "price_xxx 的商品价格 ID,新建产品后可获得": "ID de prix du produit price_xxx, peut être obtenu après la création d'un nouveau produit", - "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "Le champ safety_identifier aide OpenAI à identifier les utilisateurs d'applications susceptibles de violer les politiques d'utilisation. Désactivé par défaut pour protéger la confidentialité des utilisateurs", - "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "Le champ service_tier est utilisé pour spécifier le niveau de service. Permettre le passage peut entraîner une facturation plus élevée que prévu. Désactivé par défaut pour éviter des frais supplémentaires", - "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "Le champ speed contrôle le mode de vitesse d'inférence de Claude. Désactivé par défaut pour éviter un passage involontaire au mode fast", - "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "Clé secrète Stripe sk_xxx ou rk_xxx, les informations sensibles ne sont pas affichées", - "standard 已被移除,vip 用户看不到": "standard has been removed, vip users cannot see it", - "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "Le champ store autorise OpenAI à stocker les données de requête pour l'évaluation et l'optimisation du produit. Désactivé par défaut. L'activation peut causer un dysfonctionnement de Codex", - "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。": "Les tokens sont convertis en quota/nombre d'utilisations selon le ratio. Après la requête, la différence est réglée (déduction supplémentaire/remboursement).", - "true": "vrai", - "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示": "Clé de signature Webhook whsec_xxx, les informations sensibles ne sont pas affichées", - "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", - "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", - "{{name}} ID": "{{name}} ID", - "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", - "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", - "| 基于": " | Basé sur ", - "© {{currentYear}}": "© {{currentYear}}", - "• 视频服务商的跨域限制": "• Des restrictions cross-origin imposées par le fournisseur vidéo", - "• 防盗链保护机制": "• Un mécanisme de protection anti-hotlink", - "• 需要特定的请求头或认证": "• Des en-têtes ou une authentification spécifiques sont requis", - "「用户可选」决定用户创建令牌时能否自主选择该分组。未勾选的分组只能由管理员在后台分配给用户,不会出现在用户的令牌创建页面中。": "\"User Selectable\" controls whether users can choose this group when creating tokens. Unchecked groups can only be assigned by admin and won't appear in the token creation page.", "一个月": "Un mois", "一天": "Un jour", "一小时": "Une heure", "一次调用消耗多少刀,优先级大于模型倍率": "Combien de USD coûte un appel, priorité sur le ratio de modèle", - "一行一个屏蔽词,不需要符号分割": "Un mot sensible par ligne, aucun symbole n'est requis", "一行一个,不区分大小写": "Un mot-clé par ligne, insensible à la casse", + "一行一个屏蔽词,不需要符号分割": "Un mot sensible par ligne, aucun symbole n'est requis", "一键填充到 FluentRead": "Remplissage en un clic vers FluentRead", "三种操作的区别:": "Differences between the three operations:", "上一个表单块": "Bloc de formulaire précédent", "上一步": "Précédent", + "上传 PNG/JPG/SVG 图片,建议尺寸 ≤ 128×128px": "", + "上传图片": "", "上次保存: ": "Dernier enregistrement : ", + "上次检测到可加入模型": "", + "上次检测时间": "", "上游倍率同步": "Synchronisation du ratio en amont", "上游模型管理": "Gestion des modèles amont", "上游返回": "Réponse amont", "下一个表单块": "Bloc de formulaire suivant", + "下一次重置": "Prochaine réinitialisation", "下一步": "Suivant", "下午好": "Bon après-midi", "下载日志": "Download Logs", "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "Les champs backend écrits après l'enregistrement sont affichés ci-dessous afin de rester cohérents avec les éditeurs JSON bruts.", + "下面是带注释的示例,仅用于参考;实际保存时请删除注释。": "", "不会出现": "will not appear", "不再提醒": "Ne plus rappeler", "不勾选用户可选": "Not user-selectable", @@ -308,10 +340,10 @@ "两次输入的密码不一致": "Les deux mots de passe saisis ne correspondent pas", "两次输入的密码不一致!": "Les mots de passe saisis deux fois sont incohérents !", "两步验证": "Authentification à deux facteurs", + "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。": "L'authentification à deux facteurs (2FA) offre une protection de sécurité supplémentaire à votre compte. Après l'avoir activée, vous devez saisir votre mot de passe et le code de vérification généré par l'application d'authentification lorsque vous vous connectez.", "两步验证启用成功!": "Authentification à deux facteurs activée avec succès !", "两步验证已禁用": "L'authentification à deux facteurs a été désactivée", "两步验证设置": "Paramètres d'authentification à deux facteurs", - "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。": "L'authentification à deux facteurs (2FA) offre une protection de sécurité supplémentaire à votre compte. Après l'avoir activée, vous devez saisir votre mot de passe et le code de vérification généré par l'application d'authentification lorsque vous vous connectez.", "个": " individuel", "个GPU": " GPUs", "个人中心": "Centre personnel", @@ -368,6 +400,7 @@ "仅对自定义模型有效": "Uniquement efficace pour les modèles personnalisés", "仅当前层": "Niveau actuel uniquement", "仅当自动禁用开启时有效,关闭后不会自动禁用该渠道": "Efficace uniquement lorsque la désactivation automatique est activée, après la fermeture, le canal ne sera pas automatiquement désactivé", + "仅提交已勾选": "", "仅支持": "Seulement prend en charge", "仅支持 JSON 对象,必须包含 access_token 与 account_id": "Seuls les objets JSON sont pris en charge, doivent inclure access_token et account_id", "仅支持 JSON 文件": "Seuls les fichiers JSON sont pris en charge", @@ -375,14 +408,17 @@ "仅支持 OpenAI 接口格式": "Seul le format d'interface OpenAI est pris en charge", "仅显示已绑定": "Afficher uniquement les liés", "仅显示矛盾倍率": "Afficher uniquement les ratios contradictoires", + "仅检测上游模型更新": "", "仅用于开发环境,生产环境应使用 HTTPS": "Pour le développement uniquement, utilisez HTTPS en production", "仅用于换算,实际保存的是额度": "Uniquement pour la conversion, c'est le quota qui est enregistré", "仅用订阅": "Abonnement uniquement", "仅用钱包": "Portefeuille uniquement", + "仅管理员可用。开启后,当系统定时检测全部渠道发现上游模型变更或检测异常时,将按你选择的通知方式发送汇总通知;渠道或模型过多时会自动省略部分明细。": "", "仅重置配置": "Réinitialiser uniquement la configuration", "今日关闭": "Fermer aujourd'hui", "今日已签到": "Enregistré aujourd'hui", "今日已签到,累计签到": "Enregistré aujourd'hui, total des enregistrements", + "仍有未处理项": "", "从 0.5 降到 0.3": "reduced from 0.5 to 0.3", "从剪贴板粘贴配置": "Coller la config", "从官方模型库同步": "Synchroniser depuis la bibliothèque de modèles officielle", @@ -393,10 +429,11 @@ "代理设置": "Paramètres du proxy", "代码已复制到剪贴板": "Le code a été copié dans le presse-papiers", "令牌": "Jeton", + "令牌不存在": "", "令牌分组": "Regroupement de jetons", + "令牌分组,默认为用户的分组": "Groupe de jetons, par défaut le groupe de l'utilisateur", "令牌分组设为 auto 时,按以下顺序依次尝试选择可用分组,排在前面的优先级更高": "When token group is set to auto, groups are selected in order of priority, with higher priority groups listed first", "令牌分组设为 auto 时,系统按优先级顺序自动选择一个可用分组。": "When token group is set to auto, the system automatically selects an available group by priority.", - "令牌分组,默认为用户的分组": "Groupe de jetons, par défaut le groupe de l'utilisateur", "令牌创建成功,请在列表页面点击复制获取令牌!": "Jeton créé avec succès, veuillez cliquer sur copier sur la page de liste pour obtenir le jeton !", "令牌名称": "Nom du jeton", "令牌已重置并已复制到剪贴板": "Le jeton a été réinitialisé et copié dans le presse-papiers", @@ -408,8 +445,11 @@ "以及": "et", "仪表盘设置": "Tableau de bord", "价格": "Tarifs", + "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "Price: {{symbol}}{{price}} * {{ratioType}}: {{ratio}}", + "价格:${{price}} * {{ratioType}}:{{ratio}}": "Prix : ${{price}} * {{ratioType}} : {{ratio}}", "价格摘要": "Résumé des prix", "价格暂时不可用,请稍后重试": "Price temporarily unavailable, please try again later", + "价格模式": "", "价格模式(默认)": "Mode prix (par défaut)", "价格计算中...": "Calculating price...", "价格计算失败": "Price calculation failed", @@ -418,8 +458,6 @@ "价格设置方式": "Méthode de configuration des prix", "价格重新计算中...": "Recalculating price...", "价格预估": "Price Estimate", - "价格:${{price}} * {{ratioType}}:{{ratio}}": "Prix : ${{price}} * {{ratioType}} : {{ratio}}", - "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "Price: {{symbol}}{{price}} * {{ratioType}}: {{ratio}}", "任一满足(OR)": "Au moins un (OR)", "任务 ID": "ID de la tâche", "任务ID": "ID de tâche", @@ -436,7 +474,6 @@ "余额充值管理": "Recharge du solde", "作废": "Invalider", "作废于": "Invalidé le", - "下一次重置": "Prochaine réinitialisation", "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "Après invalidation, l'abonnement devient immédiatement invalide. L'historique n'est pas affecté. Continuer ?", "作用域": "Portée", "作用域:包含分组": "Portée : inclure le groupe", @@ -444,6 +481,10 @@ "作用域:包含规则名称": "Portée : inclure le nom de la règle", "你似乎并没有修改什么": "Vous ne semblez rien avoir modifié", "你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "Vous pouvez les ajouter manuellement dans « Noms de modèles personnalisés », cliquer sur Remplir puis soumettre, ou utiliser directement les actions ci-dessous pour les traiter automatiquement.", + "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?_one": "", + "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?_many": "", + "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?_other": "", + "使用 {{name}} 继续": "Continuer avec {{name}}", "使用 Discord 继续": "Continuer avec Discord", "使用 GitHub 继续": "Continuer avec GitHub", "使用 JSON 对象格式,格式为:{\"组名\": [最多请求次数, 最多请求完成次数]}": "Utiliser le format d'objet JSON, au format : {\"nom du groupe\": [nombre maximal de requêtes, nombre maximal d'achèvements de requêtes]}", @@ -452,43 +493,44 @@ "使用 Passkey 实现免密且更安全的登录体验": "Utilisez Passkey pour une expérience de connexion sans mot de passe et plus sécurisée.", "使用 Passkey 登录": "Se connecter avec Passkey", "使用 Passkey 验证": "Vérifier avec Passkey", - "使用 {{name}} 继续": "Continuer avec {{name}}", "使用 微信 继续": "Continuer avec WeChat", "使用 用户名 注册": "S'inscrire avec un nom d'utilisateur", "使用 邮箱或用户名 登录": "Connectez-vous avec votre e-mail ou votre nom d'utilisateur", "使用ID排序": "Trier par ID", "使用分组": "Using Group", + "使用原生额度输入": "Saisir le quota brut", "使用日志": "Journaux", "使用模式": "Mode d'utilisation", "使用统计": "Statistiques d'utilisation", - "使用认证器应用扫描二维码": "Scanner le code QR avec l'application d'authentification", "使用认证器应用(如 Google Authenticator、Microsoft Authenticator)扫描下方二维码:": "Utilisez une application d'authentification (telle que Google Authenticator, Microsoft Authenticator) pour scanner le code QR ci-dessous :", + "使用认证器应用扫描二维码": "Scanner le code QR avec l'application d'authentification", "使用说明": "Guide", "例如 /var/cache/new-api": "ex. : /var/cache/new-api", - "例如 https://docs.newapi.pro": "Par exemple, https://docs.newapi.pro", "例如 €, £, Rp, ₩, ₹...": "Par exemple, €, £, Rp, ₩, ₹...", + "例如 https://docs.newapi.pro": "Par exemple, https://docs.newapi.pro", + "例如 https://example.com/api/waffo/webhook": "", + "例如 https://example.com/console/topup": "", + "例如:": "Par exemple :", "例如: /bin/bash -c \"python app.py\"": "e.g.: /bin/bash -c \"python app.py\"", "例如: nginx:latest": "e.g.: nginx:latest", "例如: socks5://user:pass@host:port": "par exemple : socks5://user:pass@host:port", - "例如发卡网站的购买链接": "Par exemple, lien d'achat sur un site d'émission de cartes", - "例如(全渠道):": "Exemple (tous les canaux) :", - "例如(指定渠道):": "Exemple (canaux spécifiques) :", - "例如:": "Par exemple :", "例如:-c": "e.g.: -c", "例如:/bin/bash": "e.g.: /bin/bash", "例如:0001": "Par exemple : 0001", "例如:1000": "Par exemple : 1000", "例如:100000": "Ex. : 100000", - "例如:2000": "Par exemple : 2000", "例如:2,就是最低充值2$": "Par exemple : 2, c'est-à-dire un minimum de 2$ de recharge", + "例如:2000": "Par exemple : 2000", "例如:4.99": "Ex. : 4.99", "例如:401, 403, 429, 500-599": "ex. : 401, 403, 429, 500-599", "例如:7,就是7元/美金": "Par exemple : 7, c'est-à-dire 7 yuans/dollar", - "例如:GitHub Enterprise": "ex. : GitHub Enterprise", + "例如:Credit Card": "", "例如:email": "ex. : email", "例如:example.com": "ex: example.com", "例如:github / si:google / https://example.com/logo.png / 🐱": "ex. : github / si:google / https://example.com/logo.png / 🐱", + "例如:GitHub Enterprise": "ex. : GitHub Enterprise", "例如:github-enterprise": "ex. : github-enterprise", + "例如:gpt-4.1-nano,regex:^claude-.*$,regex:^sora-.*$": "", "例如:https://example.com/.well-known/openid-configuration": "ex. : https://example.com/.well-known/openid-configuration", "例如:https://gitea.example.com": "ex. : https://gitea.example.com", "例如:https://yourdomain.com": "Par exemple : https://yourdomain.com", @@ -499,9 +541,13 @@ "例如:prod_6I8rBerHpPxyoiU9WK4kot": "Ex. : prod_6I8rBerHpPxyoiU9WK4kot", "例如:sub、id、data.user.id": "ex. : sub, id, data.user.id", "例如:基础套餐": "Ex. : forfait de base", + "例如:清理工具参数,避免上游校验错误": "", "例如:该请求不满足准入策略": "ex. : Cette requête ne satisfait pas la politique d'admission", "例如:适合轻度使用": "Ex. : Convient à un usage léger", "例如:需要等级 {{required}},你当前等级 {{current}}": "ex. : Niveau requis {{required}}, votre niveau actuel est {{current}}", + "例如(全渠道):": "Exemple (tous les canaux) :", + "例如(指定渠道):": "Exemple (canaux spécifiques) :", + "例如发卡网站的购买链接": "Par exemple, lien d'achat sur un site d'émission de cartes", "供应商": "Fournisseur", "供应商介绍": "Présentation du fournisseur", "供应商信息:": "Informations sur le fournisseur :", @@ -512,6 +558,8 @@ "供应商更新成功!": "Fournisseur mis à jour avec succès !", "侧边栏管理(全局控制)": "Barre latérale (Global)", "侧边栏设置保存成功": "Paramètres de la barre latérale enregistrés avec succès", + "保前缀": "", + "保后缀": "", "保存": "Enregistrer", "保存 Discord OAuth 设置": "Enregistrer les paramètres OAuth Discord", "保存 GitHub OAuth 设置": "Enregistrer les paramètres GitHub OAuth", @@ -527,8 +575,8 @@ "保存备用码": "Enregistrer les codes de sauvegarde", "保存备用码以备不时之需": "Enregistrez les codes de sauvegarde pour les urgences", "保存失败": "Échec de l'enregistrement", - "保存失败:": "Échec de l'enregistrement :", "保存失败,请重试": "Échec de l'enregistrement, veuillez réessayer", + "保存失败:": "Échec de l'enregistrement :", "保存屏蔽词过滤设置": "Enregistrer les paramètres de filtrage des mots sensibles", "保存性能设置": "Enregistrer les paramètres de performance", "保存成功": "Enregistré avec succès", @@ -546,11 +594,11 @@ "保存预览": "Aperçu avant enregistrement", "保存额度设置": "Enregistrer les paramètres de quota", "保留": "kept", + "保留最近N个文件": "Conserver les N derniers fichiers", + "保留最近N天": "Conserver les N derniers jours", "保留原值(目标已有值时不覆盖)": "Conserver la valeur originale (ne pas écraser si la cible a déjà une valeur)", "保留天数": "Jours à conserver", "保留文件数": "Fichiers à conserver", - "保留最近N个文件": "Conserver les N derniers fichiers", - "保留最近N天": "Conserver les N derniers jours", "修复数据库一致性": "Réparer la cohérence de la base de données", "修改为": "Modifier en", "修改子渠道优先级": "Modifier la priorité du sous-canal", @@ -562,6 +610,7 @@ "倍率信息": "Informations sur le ratio", "倍率是为了方便换算不同价格的模型": "Le ratio sert à faciliter la conversion de modèles à des prix différents.", "倍率模式": "Mode de ratio", + "倍率模式(默认)": "", "倍率用于计费乘数,勾选「用户可选」后用户可在创建令牌时选择该分组": "Ratio is the billing multiplier. Check \"User Selectable\" to let users pick this group when creating tokens", "倍率类型": "Type de ratio", "假设再加两个分组 default 和 vip,但不勾选用户可选:": "Now add two more groups default and vip, but without checking User Selectable:", @@ -571,12 +620,12 @@ "停用": "Désactiver", "允许 AccountFilter 参数": "Autoriser le paramètre AccountFilter", "允许 HTTP 协议图片请求(适用于自部署代理)": "Autoriser les requêtes d'images via le protocole HTTP (applicable aux proxies auto-déployés)", - "允许 Turnstile 用户校验": "Autoriser la vérification des utilisateurs Turnstile", "允许 inference_geo 透传": "Autoriser la transmission de inference_geo", "允许 safety_identifier 透传": "Autoriser le passage de safety_identifier", "允许 service_tier 透传": "Autoriser le passage de service_tier", "允许 speed 透传": "Autoriser la transmission de speed", "允许 stream_options.include_obfuscation 透传": "Autoriser la transmission de stream_options.include_obfuscation", + "允许 Turnstile 用户校验": "Autoriser la vérification des utilisateurs Turnstile", "允许不安全的 Origin(HTTP)": "Autoriser une origine non sécurisée (HTTP)", "允许回调(会泄露服务器 IP 地址)": "Autoriser le rappel (divulguera l'adresse IP du serveur)", "允许在 Stripe 支付中输入促销码": "Autoriser la saisie de codes promotionnels lors du paiement Stripe", @@ -597,13 +646,13 @@ "允许重试": "Autoriser les tentatives", "元": "CNY", "充值": "Recharger", - "充值价格显示": "Prix de recharge", "充值价格(x元/美金)": "Prix de recharge (x yuans/dollar)", + "充值价格显示": "Prix de recharge", "充值分组倍率": "Ratio de groupe de recharge", "充值分组倍率不是合法的 JSON 字符串": "Le ratio de groupe de recharge n'est pas une chaîne JSON valide", "充值数量": "Quantité de recharge", - "充值数量不能小于": "Le montant de la recharge ne peut pas être inférieur à", "充值数量,最低 ": "Quantité de recharge, minimum ", + "充值数量不能小于": "Le montant de la recharge ne peut pas être inférieur à", "充值方式设置": "Méthodes recharge", "充值方式设置不是合法的 JSON 字符串": "Les paramètres de la méthode de recharge ne sont pas une chaîne JSON valide", "充值确认": "Confirmation de la recharge", @@ -619,8 +668,8 @@ "兑换成功!": "Échange réussi !", "兑换码充值": "Recharge par code d'échange", "兑换码创建成功": "Code d'échange créé", - "兑换码创建成功!": "Code d'échange créé avec succès !", "兑换码创建成功,是否下载兑换码?": "Code d'échange créé avec succès. Voulez-vous le télécharger ?", + "兑换码创建成功!": "Code d'échange créé avec succès !", "兑换码将以文本文件的形式下载,文件名为兑换码的名称。": "Le code d'échange sera téléchargé sous forme de fichier texte, le nom de fichier étant le nom du code d'échange.", "兑换码更新成功!": "Code d'échange mis à jour avec succès !", "兑换码生成管理": "Génération de codes", @@ -629,6 +678,7 @@ "全局控制侧边栏区域和功能显示,管理员隐藏的功能用户无法启用": "Contrôle global des zones et des fonctions de la barre latérale, les utilisateurs ne peuvent pas activer les fonctions masquées par les administrateurs", "全局设置": "Paramètres globaux", "全选": "Tout sélectionner", + "全选当前列表模型": "", "全部": "Tous", "全部供应商": "Tous les fournisseurs", "全部分组": "Tous les groupes", @@ -650,15 +700,15 @@ "公告更新失败": "Échec de la mise à jour de l'avis", "公告类型": "Type d'avis", "共": "Total", - "共 {{count}} 个密钥_many": "{{count}} clés au total", "共 {{count}} 个密钥_one": "{{count}} clé au total", + "共 {{count}} 个密钥_many": "{{count}} clés au total", "共 {{count}} 个密钥_other": "{{count}} clés au total", "共 {{count}} 个模型": "{{count}} modèles", - "共 {{count}} 个模型_many": "{{count}} modèles", "共 {{count}} 个模型_one": "{{count}} modèle", + "共 {{count}} 个模型_many": "{{count}} modèles", "共 {{count}} 个模型_other": "{{count}} modèles", - "共 {{count}} 条日志_many": "{{count}} entrées de journal", "共 {{count}} 条日志_one": "{{count}} log entry", + "共 {{count}} 条日志_many": "{{count}} entrées de journal", "共 {{count}} 条日志_other": "{{count}} log entries", "共 {{total}} 项,当前显示 {{start}}-{{end}} 项": "Total {{total}} éléments, affichage actuel {{start}}-{{end}} éléments", "关": "Fermer", @@ -670,8 +720,8 @@ "关闭": "Fermer", "关闭侧边栏": "Fermer la barre latérale", "关闭公告": "Fermer l'avis", - "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "Après fermeture, cet avertissement ne sera plus affiché (uniquement pour ce navigateur). Voulez-vous vraiment le fermer ?", "关闭后,此模型将不会被“同步官方”自动覆盖或创建": "Après fermeture, ce modèle ne sera pas automatiquement remplacé ou créé par \"Synchroniser depuis la bibliothèque de modèles officielle\"", + "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "Après fermeture, cet avertissement ne sera plus affiché (uniquement pour ce navigateur). Voulez-vous vraiment le fermer ?", "关闭弹窗,已停止批量测试": "Fermer la fenêtre popup, le test par lots a été arrêté", "关闭提示": "Fermer l’avertissement", "其他": "Autre", @@ -689,11 +739,25 @@ "内置": "Intégré", "内置 Ollama 镜像": "Built-in Ollama Image", "再次输入部署名称": "Enter Deployment Name Again", + "最低": "Le plus bas", + "最低充值数量": "", + "最低充值美元数量": "Montant minimum de recharge en dollars", + "最后使用时间": "Dernière utilisation", + "最后更新": "Last Updated", + "最后请求": "Dernière requête", + "最大GPU数量": "Max Number of GPUs", + "最大可用": "Max Available", + "最大条目数": "Nombre max. d'entrées", + "最终抵扣": "Déduction finale", + "最近一次": "Dernière", + "最近事件": "Recent Events", + "最高优先级": "highest priority", "写": "Écriture", "准入策略": "Politique d'admission", "准入策略 JSON(可选)": "Politique d'admission JSON (optionnel)", "准备中...": "Preparing...", "准备完成初始化": "Prêt à terminer l'initialisation", + "减少": "Soustraire", "凭证已刷新": "Identifiants actualisés", "分类名称": "Nom de la catégorie", "分组": "Groupe", @@ -746,17 +810,20 @@ "创建用户": "Créer un utilisateur", "初始化失败,请重试": "Échec de l'initialisation, veuillez réessayer", "初始化系统": "Initialiser le système", + "删请求头": "", "删除": "Supprimer", "删除 Key 来源": "Supprimer la source de clé", "删除会彻底移除该订阅记录(含权益明细)。是否继续?": "La suppression retirera définitivement cet enregistrement d'abonnement (y compris les détails des avantages). Continuer ?", "删除后无法恢复,确定要删除模型 \"{{name}}\" 吗?": "Cannot be recovered after deletion, are you sure you want to delete model \"{{name}}\"?", "删除失败": "Échec de la suppression", "删除密钥失败": "Échec de la suppression de la clé", + "删除已选 {{selected}} / {{total}}": "", "删除成功": "Supprimé avec succès", "删除所选": "Supprimer la sélection", "删除所选令牌": "Supprimer le jeton sélectionné", "删除所选通道": "Supprimer les canaux sélectionnés", "删除条件": "Supprimer la condition", + "删除模型": "", "删除禁用密钥失败": "Échec de la suppression des clés désactivées", "删除禁用通道": "Supprimer les canaux désactivés", "删除自动禁用密钥": "Supprimer les clés désactivées automatiquement", @@ -773,19 +840,20 @@ "刷新缓存统计": "Actualiser les statistiques du cache", "刷新缓存统计失败": "Échec de l'actualisation des statistiques du cache", "刷新页面": "Recharger la page", + "前:": "Avant :", "前往 io.net API Keys": "Go to io.net API Keys", "前往设置": "Go to Settings", "前往设置页面": "Go to Settings Page", "前缀": "Préfixe", - "前:": "Avant :", + "前置": "", + "剪贴板中未检测到连接信息": "Aucune info de connexion trouvée dans le presse-papiers", + "副本数量": "Number of Replicas", "剩余": "Remaining", "剩余备用码:": "Codes de sauvegarde restants : ", "剩余时间": "Remaining Time", "剩余额度": "Quota restant", - "剩余额度$": "Quota restant $", "剩余额度/总额度": "Restant/Total", - "剪贴板中未检测到连接信息": "Aucune info de connexion trouvée dans le presse-papiers", - "副本数量": "Number of Replicas", + "剩余额度$": "Quota restant $", "功能特性": "Fonctionnalités", "加入渠道": "Join Channel", "加入预填组": "Rejoindre un groupe pré-rempli", @@ -817,6 +885,7 @@ "区域": "Région", "升级分组": "Groupe de mise à niveau", "单GPU小时费率": "Per GPU Hour Rate", + "单价 (USD)": "", "历史消耗": "Consommation historique", "原价": "Prix original", "原价,和普通用户一样": "original price, same as regular users", @@ -824,19 +893,23 @@ "原密码": "Mot de passe original", "原生格式": "Format natif", "原生额度": "Quota brut", - "使用原生额度输入": "Saisir le quota brut", - "收起原生额度输入": "Masquer la saisie du quota brut", + "去前缀": "", + "去后缀": "", + "去处理{{type}}": "", + "去空格": "", "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "Doublons supprimés : {{before}} clés avant, {{after}} clés après", "参与官方同步": "Participer à la synchronisation officielle", "参数": "paramètre", "参数值": "Valeur du paramètre", "参数覆盖": "Remplacement des paramètres", "参数覆盖 JSON 已复制": "JSON de remplacement des paramètres copié", + "参数覆盖已复制": "", "参数覆盖必须是合法的 JSON 对象": "Le remplacement des paramètres doit être un objet JSON valide", "参数覆盖必须是合法的 JSON 格式!": "Le remplacement des paramètres doit être au format JSON valide !", "参数覆盖模板": "Modèle de remplacement des paramètres", "参数覆盖模板 JSON 格式不正确": "Le format JSON du modèle de remplacement des paramètres est incorrect", "参数覆盖模板预览": "Aperçu du modèle de remplacement des paramètres", + "参数覆盖详情": "", "参数配置": "Configuration des paramètres", "参数配置有误": "Configuration des paramètres invalide", "参数错误": "Erreur de paramètre", @@ -850,6 +923,7 @@ "取消全选": "Annuler la sélection", "取消选择": "Deselect", "变换": "Variation", + "变更": "Modification", "变焦": "Zoom", "变量值": "Variable Value", "变量名": "Variable Name", @@ -860,6 +934,7 @@ "可以根据用户分组增减令牌可选的分组范围。例如 vip 用户额外开放 premium 分组,或移除某个分组的选择权。": "Adjust the selectable group range based on user group. E.g., grant vip users access to premium, or remove access to a group.", "可以根据用户分组设置不同的计费倍率。例如 vip 用户使用 standard 令牌时倍率从 1.0 降为 0.8。": "Set different billing ratios based on user group. E.g., vip users using standard tokens get ratio 0.8 instead of 1.0.", "可信": "Fiable", + "可勾选需要执行的变更:新增会加入渠道模型列表,删除会从渠道模型列表移除。": "", "可在设置页面设置关于内容,支持 HTML & Markdown": "Le contenu \"À propos\" peut être défini sur la page des paramètres, prenant en charge HTML & Markdown", "可手动填写,多个 scope 用空格分隔": "Peut être rempli manuellement, séparer les scopes par des espaces", "可用": "Disponible", @@ -875,14 +950,15 @@ "可视化": "Visualisation", "可视化倍率设置": "Ratio visuel", "可视化编辑": "Édition visuelle", - "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。": "Optionnel. Correspondance du User-Agent de la requête entrante ; toute ligne correspondant comme sous-chaîne (insensible à la casse) est considérée comme un hit.", - "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。": "Optionnel. Validation regex de la clé d'affinité extraite ; laisser vide pour ignorer la validation.", - "可选。对请求路径进行匹配;不填表示匹配所有路径。": "Optionnel. Correspondance du chemin de la requête ; laisser vide pour correspondre à tous les chemins.", - "可选值": "Valeur facultative", + "可空": "", "可选,公告的补充说明": "Facultatif, informations supplémentaires pour l'avis", "可选,用于复现结果": "Optionnel, pour des résultats reproductibles", "可选:基于用户信息 JSON 做组合条件准入,条件不满足时返回自定义提示": "Optionnel : Admission basée sur des conditions combinées à partir du JSON des informations utilisateur ; renvoie un message personnalisé lorsque les conditions ne sont pas remplies", "可选:用于自动生成端点或 Discovery URL": "Optionnel : Utilisé pour générer automatiquement les points de terminaison ou l'URL de découverte", + "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。": "Optionnel. Correspondance du User-Agent de la requête entrante ; toute ligne correspondant comme sous-chaîne (insensible à la casse) est considérée comme un hit.", + "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。": "Optionnel. Validation regex de la clé d'affinité extraite ; laisser vide pour ignorer la validation.", + "可选。对请求路径进行匹配;不填表示匹配所有路径。": "Optionnel. Correspondance du chemin de la requête ; laisser vide pour correspondre à tous les chemins.", + "可选值": "Valeur facultative", "合计:{{total}}": "Total : {{total}}", "合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "Total : partie texte {{textTotal}} + partie audio {{audioTotal}} = {{total}}", "同时重置消息": "Réinitialiser également les messages", @@ -890,6 +966,7 @@ "同步到渠道": "Sync to Channel", "同步向导": "Assistant de synchronisation", "同步失败": "Échec de la synchronisation", + "同步字段": "", "同步成功": "Synchronisation réussie", "同步接口": "Interface de synchronisation", "同步渠道失败": "Failed to sync channel", @@ -899,10 +976,11 @@ "名称+密钥": "Nom + clé", "名称不能为空": "Le nom ne peut pas être vide", "名称匹配类型": "Type de correspondance de nom", + "后:": "Apres :", + "后端固定": "", "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "Ratio fixé par le backend : {{ratio}}. Ce champ affiche uniquement le prix converti.", "后端请求失败": "Échec de la requête du backend", "后缀": "Suffixe", - "后:": "Apres :", "向右展开": "Développer à droite", "向左展开": "Développer à gauche", "否": "Non", @@ -915,17 +993,17 @@ "启动部署失败": "Failed to start deployment", "启动配置": "Startup Configuration", "启用": "Activer", - "启用 Prompt 检查": "Activer la vérification de l'invite", "启用 io.net 部署": "Enable io.net Deployment", "启用 io.net 部署开关": "Enable io.net Deployment Switch", "启用 io.net 部署时必须填写 API Key": "API Key is required when enabling io.net deployment", + "启用 Prompt 检查": "Activer la vérification de l'invite", + "启用 Waffo": "", "启用2FA失败": "Échec de l'activation de 2FA", "启用Claude思考适配(-thinking后缀)": "Activer l'adaptation de la pensée Claude (suffixe -thinking)", "启用FunctionCall思维签名填充": "Activer le remplissage de thoughtSignature pour FunctionCall", "启用Gemini思考后缀适配": "Activer l'adaptation du suffixe de la pensée Gemini", "启用Ping间隔": "Activer l'intervalle de ping", "启用SMTP SSL": "Activer SMTP SSL", - "强制使用 AUTH LOGIN": "Forcer AUTH LOGIN", "启用SSRF防护(推荐开启以保护服务器安全)": "Activer la protection SSRF (recommandé pour la sécurité du serveur)", "启用供应商": "Activer le fournisseur", "启用全部": "Activer tout", @@ -934,6 +1012,7 @@ "启用后套餐将在用户端展示。是否继续?": "Après activation, le plan sera affiché côté utilisateur. Continuer ?", "启用后将优先复用上一次成功的渠道(粘滞选路)。": "Une fois activé, le dernier canal réussi sera réutilisé en priorité (routage persistant).", "启用后将使用 Creem Test Mode": "Après activation, le mode test Creem sera utilisé", + "启用后将使用 Waffo 沙盒环境": "", "启用密钥失败": "Échec de l'activation de la clé", "启用屏蔽词过滤功能": "Activer la fonction de filtrage des mots sensibles", "启用性能监控": "Activer la surveillance des performances", @@ -963,10 +1042,14 @@ "响应缺少凭据": "Identifiants manquants dans la réponse", "响应缺少授权链接": "Lien d'autorisation manquant dans la réponse", "商品价格 ID": "ID du prix du produit", + "商户 ID": "", "回答内容": "Contenu de la réponse", "回调 URL 填": "Remplir l'URL de rappel", "回调 URL 格式": "Format de l'URL de rappel", "回调地址": "Adresse de rappel", + "回调支付方式": "Méthode de paiement du rappel", + "回调调用者IP": "IP de l'appelant du rappel", + "回调通知地址": "", "固定价格": "Prix fixe", "固定价格(每次)": "Prix fixe (par utilisation)", "固定价格值": "Valeur de prix fixe", @@ -974,23 +1057,25 @@ "图标": "Icône", "图标使用 react-icons(Simple Icons)或 URL/emoji,例如:github、gitlab、si:google": "L'icône utilise react-icons (Simple Icons) ou URL/emoji, ex. : github, gitlab, si:google", "图标使用@lobehub/icons库,如:OpenAI、Claude.Color,支持链式参数:OpenAI.Avatar.type={'platform'}、OpenRouter.Avatar.shape={'square'},查询所有可用图标请 ": "L'icône utilise la bibliothèque @lobehub/icons, telle que : OpenAI, Claude.Color, prend en charge les paramètres de chaîne : OpenAI.Avatar.type={'platform'}, OpenRouter.Avatar.shape={'square'}, interroger toutes les icônes disponibles s'il vous plaît ", + "图标文件不能超过 100KB,请压缩后重新上传": "", "图混合": "Mélanger", "图片倍率 {{imageRatio}}": "Ratio d'image {{imageRatio}}", "图片功能在自定义请求体模式下不可用": "La fonction image n'est pas disponible en mode requête personnalisée", "图片地址": "URL de l'image", "图片已添加": "Image ajoutée", - "图片生成调用:{{symbol}}{{price}} / 1次": "Appel de génération d'image : {{symbol}}{{price}} / 1 fois", "图片生成:1 次 * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Génération d'image : 1 appel * prix unitaire {{price}} * {{ratioType}} {{ratio}} = {{amount}}", + "图片生成调用:{{symbol}}{{price}} / 1次": "Appel de génération d'image : {{symbol}}{{price}} / 1 fois", "图片输入 {{price}} / 1M tokens": "Entrée image {{price}} / 1M tokens", "图片输入: {{imageRatio}}": "Entrée d'image : {{imageRatio}}", + "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Entrée d'image : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio d'image {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "图片输入价格": "Prix d'entrée image", "图片输入价格 {{symbol}}{{price}} / 1M tokens": "Prix d'entrée image {{symbol}}{{price}} / 1M tokens", "图片输入价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (图片倍率: {{imageRatio}})": "Prix d'entrée d'image : {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (ratio d'image : {{imageRatio}})", "图片输入价格:{{symbol}}{{price}} / 1M tokens": "Prix d'entrée image : {{symbol}}{{price}} / 1M tokens", "图片输入价格:{{symbol}}{{total}} / 1M tokens": "Prix d'entrée image : {{symbol}}{{total}} / 1M tokens", + "图片输入倍率": "", "图片输入倍率(仅部分模型支持该计费)": "Ratio d'entrée d'image (seulement certains modèles prennent en charge cette facturation)", "图片输入相关的倍率设置,键为模型名称,值为倍率,仅部分模型支持该计费": "Paramètres de ratio liés à l'entrée d'image, la clé est le nom du modèle, la valeur est le ratio, seulement certains modèles prennent en charge cette facturation", - "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Entrée d'image : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio d'image {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "图生文": "Décrire", "图生视频": "Générer une vidéo à partir d'une image", "在Gotify服务器创建应用后获得的令牌,用于发送通知": "Jeton obtenu après la création d'une application sur le serveur Gotify, utilisé pour envoyer des notifications", @@ -1021,12 +1106,12 @@ "填充新模板": "Remplir le nouveau modèle", "填充旧模板": "Remplir l'ancien modèle", "填充模板": "Remplir le modèle", - "填充模板(全渠道)": "Remplir le modèle (tous les canaux)", - "填充模板(指定渠道)": "Remplir le modèle (canaux sélectionnés)", "填充模板:等级+激活": "Remplir le modèle : Niveau + Activation", "填充模板:等级提示": "Remplir le modèle : Invite de niveau", "填充模板:组织或角色": "Remplir le modèle : Organisation ou rôle", "填充模板:组织提示": "Remplir le modèle : Invite d'organisation", + "填充模板(全渠道)": "Remplir le modèle (tous les canaux)", + "填充模板(指定渠道)": "Remplir le modèle (canaux sélectionnés)", "填入": "Remplir", "填入 CC Switch": "Remplir CC Switch", "填入所有模型": "Remplir tous les modèles", @@ -1044,7 +1129,9 @@ "填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议": "Après avoir rempli le contenu de l'accord utilisateur, les utilisateurs devront cocher avoir lu l'accord utilisateur lors de l'inscription", "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "Après avoir rempli le contenu de la politique de confidentialité, les utilisateurs devront cocher avoir lu la politique de confidentialité lors de l'inscription", "填写音频补全价格前,需要先填写音频输入价格。": "Renseignez d'abord le prix d'entrée audio avant de définir le prix de complétion audio.", + "处理上游模型更新": "", "处理中": "Processing", + "处理全部渠道上游更新": "", "备份支持": "Prise en charge de la sauvegarde", "备份状态": "État de la sauvegarde", "备注": "Remarque", @@ -1054,6 +1141,7 @@ "复制": "Copier", "复制代码": "Copier le code", "复制令牌": "Copier le Jeton", + "复制令牌失败": "", "复制全部": "Tout copier", "复制名称": "Copier le nom", "复制失败": "Échec de la copie", @@ -1072,6 +1160,7 @@ "复制渠道的所有信息": "Copier toutes les informations d'un canal", "复制版本号": "Copy Version", "复制生成的密钥并粘贴到此处": "Copy the generated key and paste it here", + "复制请求头": "", "复制连接信息": "Copier les infos de connexion", "复制链接": "Copier le lien", "外接设备": "Périphériques externes", @@ -1095,13 +1184,13 @@ "套餐标题": "Titre du plan", "套餐标题不能为空": "Le titre du forfait ne peut pas être vide", "套餐的基本信息和定价": "Informations de base et tarification du plan", + "如:大带宽批量分析图片推荐": "par exemple, Recommandations d'analyse d'images par lots à large bande passante", + "如:香港线路": "par exemple, Ligne de Hong Kong", "如果亲和到的渠道失败,重试到其他渠道成功后,将亲和更新到成功的渠道。": "Si le canal d'affinité échoue, après une nouvelle tentative réussie sur un autre canal, l'affinité sera mise à jour vers le canal réussi.", "如果你对接的是上游One API或者New API等转发项目,请使用OpenAI类型,不要使用此类型,除非你知道你在做什么。": "Si vous vous connectez à des projets de redirection One API ou New API en amont, veuillez utiliser le type OpenAI. N'utilisez pas ce type, sauf si vous savez ce que vous faites.", "如果用户请求中包含系统提示词,则使用此设置拼接到用户的系统提示词前面": "Si la requête de l'utilisateur contient un prompt système, utilisez ce paramètre pour le concaténer avant le prompt système de l'utilisateur", "如果镜像为私有,请填写密码或Token": "If the image is private, please fill in the password or token", "如果镜像为私有,请填写用户名": "If the image is private, please fill in the username", - "如:大带宽批量分析图片推荐": "par exemple, Recommandations d'analyse d'images par lots à large bande passante", - "如:香港线路": "par exemple, Ligne de Hong Kong", "始终使用浅色主题": "Toujours utiliser le thème clair", "始终使用深色主题": "Toujours utiliser le thème sombre", "字段映射": "Mapping des champs", @@ -1169,6 +1258,7 @@ "密钥": "Clé API", "密钥 JSON 必须包含 access_token": "Le JSON de la clé doit inclure access_token", "密钥 JSON 必须包含 account_id": "Le JSON de la clé doit inclure account_id", + "密钥(编辑模式下,保存的密钥不会显示)": "Clé (en mode édition, les clés enregistrées ne sont pas affichées)", "密钥去重": "Suppression des doublons de clés", "密钥将以Bearer方式添加到请求头中,用于验证webhook请求的合法性": "La clé sera ajoutée à l'en-tête de la requête en tant que Bearer pour vérifier la légitimité de la requête webhook", "密钥已删除": "La clé a été supprimée", @@ -1186,7 +1276,6 @@ "密钥获取成功": "Acquisition de la clé réussie", "密钥输入方式": "Méthode de saisie de la clé", "密钥预览": "Aperçu de la clé", - "密钥(编辑模式下,保存的密钥不会显示)": "Clé (en mode édition, les clés enregistrées ne sont pas affichées)", "对于官方渠道,new-api已经内置地址,除非是第三方代理站点或者Azure的特殊接入地址,否则不需要填写": "Pour les canaux officiels, le new-api a une adresse intégrée. Sauf s'il s'agit d'un site proxy tiers ou d'une adresse d'accès Azure spéciale, il n'est pas nécessaire de la remplir", "对免费模型启用预消耗": "Activer la préconsommation pour les modèles gratuits", "对域名启用 IP 过滤(推荐开启)": "Activer le filtrage IP pour les domaines (recommandé)", @@ -1211,6 +1300,9 @@ "将只保留最近 {{value}} 个日志文件,其余将被删除。": "Seuls les {{value}} derniers fichiers journaux seront conservés ; le reste sera supprimé.", "将大请求体临时存储到磁盘": "Stocker temporairement les grands corps de requête sur le disque", "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "La configuration tarifaire du modèle actuellement édité {{name}} sera appliquée aux {{count}} modèles sélectionnés.", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。_one": "", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。_many": "", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。_other": "", "将清除所有保存的配置并恢复默认设置,此操作不可撤销。是否继续?": "Effacera toutes les configurations enregistrées et rétablira les paramètres par défaut. Cette opération ne peut pas être annulée. Continuer ?", "将清除选定时间之前的所有日志": "Effacera tous les journaux avant l'heure sélectionnée", "将追加 2 条规则到现有规则列表。": "2 règles seront ajoutées à la liste de règles existante.", @@ -1225,10 +1317,10 @@ "展示价格": "Prix affiché", "嵌套映射:用户分组 → 使用分组 → 倍率": "Nested mapping: user group → using group → ratio", "左侧边栏个人设置": "Paramètres personnels de la barre latérale gauche", - "已为 ${count} 个渠道设置标签!": "Étiquettes définies pour ${count} canaux !", - "已为 {{count}} 个模型设置{{type}}_many": "{{type}} défini pour {{count}} modèles", "已为 {{count}} 个模型设置{{type}}_one": "{{type}} défini pour {{count}} modèle", + "已为 {{count}} 个模型设置{{type}}_many": "{{type}} défini pour {{count}} modèles", "已为 {{count}} 个模型设置{{type}}_other": "{{type}} défini pour {{count}} modèles", + "已为 ${count} 个渠道设置标签!": "Étiquettes définies pour ${count} canaux !", "已从 Discovery 自动填充配置": "Configuration remplie automatiquement depuis Discovery", "已从 Discovery 获取配置,可继续手动修改所有字段。": "Configuration récupérée depuis Discovery. Vous pouvez continuer à modifier manuellement tous les champs.", "已作废": "Invalidé", @@ -1243,18 +1335,21 @@ "已切换至最优倍率视图,每个模型使用其最低倍率分组": "Passé à la vue de ratio optimal, chaque modèle utilise son groupe de ratio le plus bas", "已初始化": "Initialisé", "已删除": "Supprimé", - "已删除 ${data} 个通道!": "${data} canaux supprimés !", "已删除 {{count}} 个令牌!": "Supprimé {{count}} jetons !", - "已删除 {{count}} 个令牌!_many": "Supprimé {{count}} jetons !", "已删除 {{count}} 个令牌!_one": "Supprimé {{count}} jeton !", + "已删除 {{count}} 个令牌!_many": "Supprimé {{count}} jetons !", "已删除 {{count}} 个令牌!_other": "Supprimé {{count}} jetons !", - "已删除 {{count}} 条失效兑换码_many": "{{count}} codes d'échange invalides supprimés", "已删除 {{count}} 条失效兑换码_one": "{{count}} code d'échange invalide supprimé", + "已删除 {{count}} 条失效兑换码_many": "{{count}} codes d'échange invalides supprimés", "已删除 {{count}} 条失效兑换码_other": "{{count}} codes d'échange invalides supprimés", + "已删除 ${data} 个通道!": "${data} canaux supprimés !", "已删除所有禁用渠道,共计 ${data} 个": "Tous les canaux désactivés ont été supprimés, au total ${data}", "已删除消息及其回复": "Message et ses réponses supprimés", "已勾选": "Sélectionné", "已勾选 {{count}} 个模型": "{{count}} modèles sélectionnés", + "已勾选 {{count}} 个模型_one": "", + "已勾选 {{count}} 个模型_many": "", + "已勾选 {{count}} 个模型_other": "", "已发起支付": "Paiement initié", "已发送到 Fluent": "Envoyé à Fluent", "已取消 Passkey 注册": "Enregistrement du Passkey annulé", @@ -1266,10 +1361,13 @@ "已填充提示模板": "Modèle d'invite rempli", "已填充模版": "Modèle rempli", "已填充策略模板": "Modèle de politique rempli", + "已处理上游模型更新:加入 {{added}} 个,删除 {{removed}} 个,本次忽略 {{ignored}} 个,当前已忽略模型 {{totalIgnored}} 个": "", "已备份": "Sauvegardé", "已复制": "Copié", "已复制 ${count} 个模型": "${count} modèles copiés", "已复制 ID 到剪贴板": "ID copied to clipboard", + "已复制:": "Copié :", + "已复制:{{name}}": "Copié : {{name}}", "已复制全部数据": "Toutes les données copiées", "已复制到剪切板": "Copié dans le presse-papiers", "已复制到剪贴板": "Copié dans le presse-papiers", @@ -1278,18 +1376,21 @@ "已复制模型名称": "Nom du modèle copié", "已复制版本号": "Version copied", "已复制自动生成的 API Key": "Auto-generated API Key copied", - "已复制:": "Copié :", - "已复制:{{name}}": "Copié : {{name}}", "已完成": "Completed", "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "La configuration tarifaire du modèle {{name}} a été appliquée à {{count}} modèles en lot", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型_one": "", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型_many": "", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型_other": "", "已开启全局请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "La transmission globale des requêtes est activée. Les fonctionnalités intégrées de NewAPI (surcharge des paramètres, redirection de modèle, adaptation du canal, etc.) seront désactivées. Ce n'est pas une bonne pratique. Si cela cause des problèmes, merci de ne pas ouvrir d'issue.", + "已忽略模型": "", "已成功开始测试所有已启用通道,请刷新页面查看结果。": "Le test de tous les canaux activés a démarré avec succès. Veuillez actualiser la page pour voir les résultats.", "已打开授权页面": "Page d'autorisation ouverte", "已打开支付页面": "Page de paiement ouverte", + "已批量处理上游模型更新:渠道 {{channels}} 个,加入 {{added}} 个,删除 {{removed}} 个,失败 {{fails}} 个": "", "已提交": "Soumis", "已支付金额": "Amount Paid", - "已新增 {{count}} 个模型:{{list}}_many": "{{count}} nouveaux modèles ajoutés : {{list}}", "已新增 {{count}} 个模型:{{list}}_one": "{{count}} nouveau modèle ajouté : {{list}}", + "已新增 {{count}} 个模型:{{list}}_many": "{{count}} nouveaux modèles ajoutés : {{list}}", "已新增 {{count}} 个模型:{{list}}_other": "{{count}} nouveaux modèles ajoutés : {{list}}", "已更新完毕所有已启用通道余额!": "Le quota de tous les canaux activés a été mis à jour !", "已有保存的配置": "Configuration enregistrée existante", @@ -1299,11 +1400,14 @@ "已服务": "Served", "已注销": "Déconnecté", "已添加": "Ajouté", - "已添加 {{count}} 个模板_many": "{{count}} modèles ajoutés", "已添加 {{count}} 个模板_one": "{{count}} modèle ajouté", + "已添加 {{count}} 个模板_many": "{{count}} modèles ajoutés", "已添加 {{count}} 个模板_other": "{{count}} modèles ajoutés", "已添加到白名单": "Ajouté à la liste blanche", "已清理 {{count}} 个日志文件,释放 {{size}}": "{{count}} fichiers journaux nettoyés, {{size}} libérés", + "已清理 {{count}} 个日志文件,释放 {{size}}_one": "", + "已清理 {{count}} 个日志文件,释放 {{size}}_many": "", + "已清理 {{count}} 个日志文件,释放 {{size}}_other": "", "已清空": "Vidé", "已清空测试结果": "Résultats de test effacés", "已生成授权凭据": "Identifiants d'autorisation générés", @@ -1322,14 +1426,15 @@ "已达到购买上限": "Limite d'achat atteinte", "已过期": "Expiré", "已运行时间": "Uptime", - "已选择 ${count} 个渠道": "${count} canaux sélectionnés", - "已选择 {{count}} 个模型_many": "{{count}} modèles sélectionnés", "已选择 {{count}} 个模型_one": "{{count}} modèle sélectionné", + "已选择 {{count}} 个模型_many": "{{count}} modèles sélectionnés", "已选择 {{count}} 个模型_other": "{{count}} modèles sélectionnés", "已选择 {{selected}} / {{total}}": "{{selected}} / {{total}} sélectionnés", + "已选择 ${count} 个渠道": "${count} canaux sélectionnés", "已重置为默认配置": "Réinitialisé à la configuration par défaut", "已销毁": "Destroyed", "币种": "Devise", + "帐号信息": "", "常用上下文 Key(用于 context_*)": "Clés de contexte courantes (pour context_*)", "常见问答": "FAQ", "常见问答管理,为用户提供常见问题的答案(最多50个,前端显示最新20条)": "Gestion de la FAQ, fournissant des réponses aux questions courantes des utilisateurs (maximum 50, afficher les 20 dernières sur le front-end)", @@ -1348,19 +1453,16 @@ "延长容器时长将会产生额外费用,请确认您有足够的账户余额。": "Extending container duration will incur additional charges, please ensure you have sufficient account balance.", "延长操作一旦确认无法撤销,费用将立即扣除。": "Once confirmed, the extension operation cannot be undone, and charges will be deducted immediately.", "延长时长": "Extension Duration", + "延长时长(小时)": "Extension Duration (hours)", "延长时长不能超过720小时(30天)": "Extension duration cannot exceed 720 hours (30 days)", "延长时长失败": "Failed to extend duration", "延长时长至少为1小时": "Extension duration must be at least 1 hour", - "延长时长(小时)": "Extension Duration (hours)", "建立连接时发生错误": "Erreur lors de l'établissement de la connexion", "建议在生产环境中使用 MySQL 或 PostgreSQL 数据库,或确保 SQLite 数据库文件已映射到宿主机的持久化存储。": "Il est recommandé d'utiliser les bases de données MySQL ou PostgreSQL dans les environnements de production, ou de s'assurer que le fichier de base de données SQLite est mappé sur le stockage persistant de la machine hôte.", "开": "Ouvert", "开启「默认使用 auto 分组」后,新建令牌和初始令牌都会自动设为 auto。": "After enabling \"Default to auto group\", new tokens and initial tokens will be set to auto.", "开启之后会清除用户提示词中的": "Après l'activation, l'invite de l'utilisateur sera effacée", "开启之后将上游地址替换为服务器地址": "Après l'activation, l'adresse en amont sera remplacée par l'adresse du serveur", - "开启后不限制:必须设置模型倍率": "Après l'activation, aucune limite : le ratio de modèle doit être défini", - "开启后创建令牌默认选择auto分组,初始令牌也将设为auto": "When enabled, new tokens default to auto group, initial tokens are also set to auto", - "开启后未登录用户无法访问模型广场": "Lorsqu'il est activé, les utilisateurs non authentifiés ne peuvent pas accéder à la place du marché des modèles", "开启后,using_group 会参与 cache key(不同分组隔离)。": "Une fois activé, using_group fera partie de la clé de cache (isolation par groupe).", "开启后,仅\"消费\"和\"错误\"日志将记录您的客户端IP地址": "Après l'activation, seuls les journaux \"consommation\" et \"erreur\" enregistreront votre adresse IP client", "开启后,对免费模型(倍率为0,或者价格为0)的模型也会预消耗额度": "Après activation, les modèles gratuits (ratio 0 ou prix 0) préconsommeront également du quota", @@ -1372,14 +1474,21 @@ "开启后,规则名称会参与 cache key(不同规则隔离)。": "Une fois activé, le nom de la règle fera partie de la clé de cache (isolation par règle).", "开启后,该渠道请求 Claude 时将强制追加 ?beta=true(无需客户端手动传参)": "Une fois activé, les requêtes à Claude via ce canal ajouteront automatiquement ?beta=true (pas besoin de le passer manuellement côté client)", "开启后,违规请求将额外扣费。": "Lorsqu'il est activé, les requêtes en violation entraîneront des frais supplémentaires.", + "开启后不限制:必须设置模型倍率": "Après l'activation, aucune limite : le ratio de modèle doit être défini", + "开启后创建令牌默认选择auto分组,初始令牌也将设为auto": "When enabled, new tokens default to auto group, initial tokens are also set to auto", + "开启后未登录用户无法访问模型广场": "Lorsqu'il est activé, les utilisateurs non authentifiés ne peuvent pas accéder à la place du marché des modèles", + "开启后检测到新增模型会自动加入当前渠道模型列表": "", + "开启后由后端定时任务检测该渠道上游模型变化": "", "开启批量操作": "Activer la sélection par lots", "开始": "Début", "开始同步": "Démarrer la synchronisation", "开始批量测试 ${count} 个模型,已清空上次结果...": "Démarrage du test par lots de ${count} modèles, résultats précédents effacés...", "开始时间": "heure de début", + "异常": "", "异步任务退款": "Remboursement de tâche asynchrone", "张图片": "images", "弱变换": "Faible variation", + "强制使用 AUTH LOGIN": "Forcer AUTH LOGIN", "强制将响应格式化为 OpenAI 标准格式(只适用于OpenAI渠道类型)": "Forcer le formatage des réponses au format standard OpenAI (uniquement pour les types de canaux OpenAI)", "强制格式化": "Forcer le format", "强制要求": "Exigence obligatoire", @@ -1417,6 +1526,7 @@ "当前设置类型: ": "Type de paramètre actuel : ", "当前跟随系统": "Suit actuellement le système", "当前配置无法连接到 io.net。": "Unable to connect to io.net with current configuration.", + "当前额度": "Quota actuel", "当某个分组的用户使用另一个分组的令牌时,可设置特殊倍率覆盖基础倍率。例如:vip 分组的用户使用 default 分组时倍率为 0.5": "When a user in one group uses a token from another group, a special ratio can override the base ratio. E.g., vip users using default group get ratio 0.5", "当模型没有设置价格时仍接受调用,仅当您信任该网站时使用,可能会产生高额费用": "Acceptez les appels même si le modèle n'a pas de prix défini, utilisez uniquement lorsque vous faites confiance au site Web, ce qui peut entraîner des coûts élevés", "当运行通道全部测试时,超过此时间将自动禁用通道": "Lors de l'exécution de tous les tests de canaux, le canal sera automatiquement désactivé lorsque ce temps sera dépassé", @@ -1496,10 +1606,13 @@ "批量删除失败": "Échec de la suppression par lots", "批量删除成功": "Batch deletion successful", "批量删除模型": "Supprimer les modèles par lots", + "批量处理失败": "", "批量应用当前模型价格": "Appliquer en lot le prix du modèle actuel", "批量操作": "Opérations par lots", "批量操作失败": "Batch operation failed", "批量操作完成: {{success}}个成功, {{failed}}个失败": "Batch operation completed: {{success}} succeeded, {{failed}} failed", + "批量检测失败": "", + "批量检测完成:渠道 {{channels}} 个,新增 {{add}} 个,删除 {{remove}} 个,失败 {{fails}} 个": "", "批量测试${count}个模型": "Tester par lots ${count} modèles", "批量测试完成!成功: ${success}, 失败: ${fail}, 总计: ${total}": "Test par lots terminé ! Succès : ${success}, Échec : ${fail}, Total : ${total}", "批量测试已停止": "Le test par lots a été arrêté", @@ -1521,28 +1634,29 @@ "按倍率设置": "Définir par ratio", "按次": "Par requête", "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Par requête {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "按次计费": "Paiement par requête", "按次:{{symbol}}{{price}}": "Par requête : {{symbol}}{{price}}", "按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Par requête : {{symbol}}{{price}} * {{ratioType}} : {{ratio}} = {{symbol}}{{total}}", + "按次计费": "Paiement par requête", "按照如下格式输入:AccessKey|SecretAccessKey|Region": "Entrez au format : AccessKey|SecretAccessKey|Region", "按量计费": "Paiement à l'utilisation", "按量计费下需要先填写输入价格,才能保存其它价格项。": "En facturation au volume, il faut d'abord renseigner le prix d'entrée avant d'enregistrer les autres prix.", "按顺序替换content中的变量占位符": "Remplacer les espaces réservés de variable dans le contenu dans l'ordre", "换脸": "Remplacement de visage", - "授权失败": "Échec de l'autorisation", "授权,需在遵守": " et doit être utilisé conformément au ", + "授权失败": "Échec de l'autorisation", "排序": "Ordre", "排队中": "En file d'attente", "接受未设置价格模型": "Accepter les modèles sans prix défini", "接口凭证": "Informations d'identification de l'interface", "接口密钥已过期": "API key has expired", + "接收上游模型更新通知": "", "控制台": "Console", "控制台区域": "Zone de la console", "控制输出的随机性和创造性": "Contrôle l'aléatoire et la créativité de la sortie", "控制顶栏模块显示状态,全局生效": "Contrôler l'état d'affichage du module d'en-tête, effet global", "推荐": "Recommandé", - "推荐使用(用户可选)": "Recommandé (optionnel pour l'utilisateur)", "推荐:用户可以选择是否使用指纹等验证": "Recommandé : les utilisateurs peuvent choisir d'utiliser ou non la vérification par empreinte digitale", + "推荐使用(用户可选)": "Recommandé (optionnel pour l'utilisateur)", "描述": "Description", "提交": "Soumettre", "提交时间": "Heure de soumission", @@ -1552,14 +1666,14 @@ "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "Invite {{input}} tokens / 1M tokens * {{symbol}}{{price}}", "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Invite {{input}} tokens / 1M tokens * {{symbol}}{{price}} + Complétion {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Invite {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + Cache {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + Création de cache {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + Complétion {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "提示价格:{{symbol}}{{price}} / 1M tokens": "Prix d'invite : {{symbol}}{{price}} / 1M tokens", - "提示缓存倍率": "Ratio de cache d'invite", "提示:如需备份数据,只需复制上述目录即可": "Astuce : pour sauvegarder les données, il suffit de copier le répertoire ci-dessus", "提示:此处配置仅用于控制「模型广场」对用户的展示效果,不会影响模型的实际调用与路由。若需配置真实调用行为,请前往「渠道管理」进行设置。": "Remarque : cette configuration n'affecte que l'affichage des modèles dans la place de marché des modèles et n'a aucun impact sur l'invocation ou le routage réels. Pour configurer le comportement réel des appels, veuillez aller dans « Gestion des canaux ».", "提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。": "Remarque : la correspondance des endpoints sert uniquement à l'affichage dans la place de marché des modèles et n'affecte pas l'invocation réelle. Pour configurer l'invocation réelle, veuillez aller dans « Gestion des canaux ».", "提示:该功能为测试版,未来配置结构与功能行为可能发生变更,请勿在生产环境使用。": "Remarque : cette fonctionnalité est en version bêta. La structure de configuration et le comportement peuvent changer à l’avenir. Ne l’utilisez pas en production.", "提示:语言偏好会同步到您登录的所有设备,并影响API返回的错误消息语言。": "Conseil : La préférence linguistique sera synchronisée sur tous vos appareils connectés et affectera la langue des messages d'erreur renvoyés par l'API.", "提示:链接中的{key}将被替换为API密钥,{address}将被替换为服务器地址": "Astuce : {key} dans le lien sera remplacé par la clé API, {address} sera remplacé par l'adresse du serveur", + "提示价格:{{symbol}}{{price}} / 1M tokens": "Prix d'invite : {{symbol}}{{price}} / 1M tokens", + "提示缓存倍率": "Ratio de cache d'invite", "搜索供应商": "Rechercher un fournisseur", "搜索关键字": "Rechercher des mots-clés", "搜索失败": "Search failed", @@ -1573,6 +1687,7 @@ "搜索模型失败": "Échec de la recherche de modèles", "搜索渠道名称或地址": "Rechercher un nom ou une adresse de canal", "搜索聊天应用名称": "Rechercher le nom de l'application de chat", + "搜索规则(描述 / 类型 / 路径 / 来源 / 目标)": "", "搜索规则(类型 / 路径 / 来源 / 目标)": "Rechercher des règles (type / chemin / source / cible)", "搜索部署名称": "Search deployment name", "操作": "Actions", @@ -1586,13 +1701,16 @@ "支付": "Payer", "支付地址": "Adresse de paiement", "支付失败": "Paiement échoué", + "支付完成后用户跳转的页面,留空则自动使用 服务器地址 + /console/topup": "", "支付宝": "Alipay", "支付方式": "Mode de paiement", "支付方式名称": "Nom de méthode de paiement", + "支付方式名称不能为空": "", "支付方式类型": "Type de méthode de paiement", "支付渠道": "Canaux de paiement", "支付设置": "Paiement", "支付请求失败": "Échec de la demande de paiement", + "支付返回地址": "", "支付金额": "Montant payé", "支持 Ctrl+V 粘贴图片": "Supporte Ctrl+V pour coller l'image", "支持6位TOTP验证码或8位备用码,可到`个人设置-安全设置-两步验证设置`配置或查看。": "Prend en charge le code de vérification TOTP à 6 chiffres ou le code de sauvegarde à 8 chiffres, peut être configuré ou consulté dans `Paramètres personnels - Paramètres de sécurité - Paramètres d'authentification à deux facteurs`.", @@ -1609,6 +1727,7 @@ "支持拉取 Ollama 官方模型库中的所有模型,拉取过程可能需要几分钟时间": "Supports pulling all models from the Ollama official model library, the pulling process may take a few minutes", "支持搜索用户的 ID、用户名、显示名称和邮箱地址": "Prise en charge de la recherche par ID utilisateur, nom d'utilisateur, nom d'affichage et adresse e-mail", "支持的图像模型": "Modèles d'image pris en charge", + "支持精确匹配;使用 regex: 开头可按正则匹配。": "", "支持通配符格式,如:example.com, *.api.example.com": "Prend en charge le format générique, par exemple : example.com, *.api.example.com", "支持逻辑 and/or 与嵌套 groups;操作符支持 eq/ne/gt/gte/lt/lte/in/not_in/contains/exists": "Prend en charge la logique and/or avec des groupes imbriqués ; opérateurs : eq/ne/gt/gte/lt/lte/in/not_in/contains/exists", "收益": "Gains", @@ -1616,6 +1735,7 @@ "收起": "Réduire", "收起侧边栏": "Réduire la barre latérale", "收起内容": "Réduire le contenu", + "收起原生额度输入": "Masquer la saisie du quota brut", "放大": "Upscalers", "放大编辑": "Développer l'éditeur", "敏感信息不会发送到前端显示": "Les informations sensibles ne seront pas affichées dans le frontend", @@ -1632,9 +1752,9 @@ "数据看板默认时间粒度": "Granularité temporelle par défaut du tableau de bord des données", "数据管理和日志查看": "Données et journaux", "文件上传": "Téléchargement de fichier", + "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Recherche de fichier : {{count}} / 1K * prix unitaire {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "文件搜索价格:{{symbol}}{{price}} / 1K 次": "Prix de recherche de fichier : {{symbol}}{{price}} / 1K fois", "文件搜索调用 {{fileSearchCallCount}} 次": "Recherche de fichier appelée {{fileSearchCallCount}} fois", - "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Recherche de fichier : {{count}} / 1K * prix unitaire {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "Invite texte {{input}} tokens / 1M tokens * {{symbol}}{{price}} + Complétion texte {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Prompt texte {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + Complétion texte {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + Prompt audio {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + Complétion audio {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "文字提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "Invite texte {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + Cache {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + Complétion texte {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", @@ -1649,8 +1769,11 @@ "新增 Key 来源": "Ajouter une source de clé", "新增供应商": "Ajouter un fournisseur", "新增失败": "Échec de l'ajout", + "新增已选 {{selected}} / {{total}}": "", "新增成功": "Ajouté avec succès", + "新增支付方式": "", "新增条件": "Ajouter une condition", + "新增模型": "", "新增规则": "Ajouter une règle", "新增订阅": "Ajouter un abonnement", "新密码": "Nouveau mot de passe", @@ -1661,9 +1784,9 @@ "新建容器部署": "Create Container Deployment", "新建数量": "Nouvelle quantité", "新建组": "Nouveau groupe", - "新格式模板": "Modèle de nouveau format", "新格式(支持条件判断与json自定义):": "Nouveau format (prend en charge les conditions et la personnalisation JSON) :", "新格式(规则 + 条件)": "Nouveau format (Règles + Conditions)", + "新格式模板": "Modèle de nouveau format", "新版本": "Nouvelle version", "新用户使用邀请码奖励额度": "Quota de bonus de code d'invitation pour nouvel utilisateur", "新用户初始额度": "Quota initial pour les nouveaux utilisateurs", @@ -1700,10 +1823,10 @@ "日志类型": "Type de journal", "日志设置": "Config. journaux", "日志详情": "Détails du journal", - "旧格式必须是 JSON 对象": "L'ancien format doit être un objet JSON", - "旧格式模板": "Modèle d'ancien format", "旧格式(JSON 对象)": "Ancien format (Objet JSON)", "旧格式(直接覆盖):": "Ancien format (remplacement direct) :", + "旧格式必须是 JSON 对象": "L'ancien format doit être un objet JSON", + "旧格式模板": "Modèle d'ancien format", "旧的备用码已失效,请保存新的备用码": "Les anciens codes de sauvegarde ont été invalidés, veuillez enregistrer les nouveaux codes de sauvegarde", "早上好": "Bonjour", "时间": "Heure", @@ -1716,16 +1839,18 @@ "是否为企业账户": "Est-ce un compte d'entreprise ?", "是否同时重置对话消息?选择\"是\"将清空所有对话记录并恢复默认示例;选择\"否\"将保留当前对话记录。": "Voulez-vous également réinitialiser les messages de conversation ? Choisir \"Oui\" effacera tous les enregistrements de conversation et restaurera les exemples par défaut ; choisir \"Non\" conservera les enregistrements de conversation actuels.", "是否将该订单标记为成功并为用户入账?": "Marquer cette commande comme réussie et créditer l'utilisateur ?", + "是否检测上游模型更新": "", "是否确认充值?": "Confirmer la recharge ?", + "是否自动同步上游模型更新": "", "是否自动禁用": "Désactiver automatiquement", "是否要求指纹/面容等生物识别": "Exiger une reconnaissance biométrique par empreinte digitale/faciale", "显示倍率": "Afficher le ratio", + "显示最新20条": "Afficher les 20 dernières", "显示名称": "Nom d'affichage", "显示名称字段(可选)": "Champ du nom d'affichage (optionnel)", "显示完整内容": "Afficher le contenu complet", "显示操作项": "Afficher les actions", "显示更多": "Afficher plus", - "显示最新20条": "Afficher les 20 dernières", "显示第": "Affichage de", "显示设置": "Paramètres d'affichage", "显示调试": "Afficher le débogage", @@ -1746,6 +1871,7 @@ "暂无公告": "Pas d'avis", "暂无分组,点击下方按钮添加": "No groups yet. Click the button below to add one.", "暂无匹配模型": "Aucun modèle correspondant", + "暂无参数覆盖记录": "", "暂无可复制 JSON": "Aucun JSON à copier", "暂无可复制的版本信息": "No version information to copy", "暂无可展示数据": "Aucune donnée à afficher", @@ -1759,6 +1885,7 @@ "暂无已绑定项": "Aucun élément lié", "暂无常见问答": "Pas de FAQ", "暂无成功模型": "Aucun modèle réussi", + "暂无支付方式,点击上方按钮新增": "", "暂无数据": "Aucune donnée", "暂无数据,点击下方按钮添加键值对": "Aucune donnée, cliquez sur le bouton ci-dessous pour ajouter des paires clé-valeur", "暂无日志": "No logs", @@ -1787,6 +1914,7 @@ "更新": "Mettre à jour", "更新 Creem 设置": "Mettre à jour les paramètres Creem", "更新 Stripe 设置": "Mettre à jour les paramètres Stripe", + "更新 Waffo 设置": "", "更新SSRF防护设置": "Mettre à jour les paramètres de protection SSRF", "更新Worker设置": "Mettre à jour les paramètres du worker", "更新令牌信息": "Mettre à jour les informations du jeton", @@ -1809,18 +1937,7 @@ "更新配置后,容器可能需要重启以应用新的设置。请确保您了解这些更改的影响。": "After updating the configuration, the container may need to restart to apply the new settings. Please ensure you understand the impact of these changes.", "更新配置失败": "Failed to update configuration", "更新预填组": "Mettre à jour le groupe pré-rempli", - "最低": "Le plus bas", - "最低充值美元数量": "Montant minimum de recharge en dollars", - "最后使用时间": "Dernière utilisation", - "最后更新": "Last Updated", - "最后请求": "Dernière requête", - "最大GPU数量": "Max Number of GPUs", - "最大可用": "Max Available", - "最大条目数": "Nombre max. d'entrées", - "最终抵扣": "Déduction finale", - "最近一次": "Dernière", - "最近事件": "Recent Events", - "最高优先级": "highest priority", + "替换": "", "月": "mois", "有 Reasoning": "A un raisonnement", "有序字符串数组": "Ordered string array", @@ -1830,6 +1947,7 @@ "有效期设置": "Paramètres de validité", "服务可用性": "État du service", "服务商": "Service Provider", + "服务器IP": "IP du serveur", "服务器地址": "Adresse du serveur", "服务器日志功能未启用(未配置日志目录)": "La journalisation du serveur n'est pas activée (répertoire de journaux non configuré)", "服务器日志管理": "Gestion des journaux du serveur", @@ -1876,8 +1994,8 @@ "本地数据存储": "Stockage de données locales", "本地计费": "Facturation locale", "本月获得": "Ce mois-ci", - "本设备内置": "Intégré à cet appareil", "本设备:手机指纹/面容,外接:USB安全密钥": "Intégré : empreinte digitale/visage du téléphone, Externe : clé de sécurité USB", + "本设备内置": "Intégré à cet appareil", "本项目根据": "Ce projet est sous licence ", "机密环境变量": "Secret Environment Variables", "机密环境变量将被加密存储,适用于存储密码、API密钥等敏感信息。": "Secret environment variables will be stored encrypted, suitable for storing passwords, API keys and other sensitive information.", @@ -1886,19 +2004,21 @@ "权限设置": "Paramètres d'autorisation", "条": "éléments", "条 - 第": "à", + "条,共": "sur", "条件取反": "Inverser la condition", "条件数": "Conditions", "条件规则": "Règles de condition", "条件项设置": "Paramètres des éléments de condition", "条日志已清理!": "les journaux ont été effacés !", "条规则": "rules", - "条,共": "sur", "来源": "Source", "来源于 IO.NET 部署": "From IO.NET Deployment", "来源端点": "Point de terminaison source", "来自模型重定向,尚未加入模型列表": "Issu d'une redirection de modèle, pas encore ajouté à la liste des modèles", "某些配置更改可能需要几分钟才能生效。": "Some configuration changes may take a few minutes to take effect.", "查看": "Voir", + "查看 Codex 帐号信息与用量": "", + "查看 JSON 示例": "", "查看关联部署": "View Associated Deployment", "查看图片": "Voir les images", "查看密钥": "Afficher la clé", @@ -1930,10 +2050,13 @@ "格式示例:": "Exemple de format :", "格式错误": "Format invalide", "检查更新": "Vérifier les mises à jour", + "检测全部渠道上游更新": "", "检测到 FluentRead(流畅阅读)": "FluentRead détecté", "检测到剪贴板中的连接信息": "Informations de connexion détectées dans le presse-papiers", "检测到多个密钥,您可以单独复制每个密钥,或点击复制全部获取完整内容。": "Plusieurs clés détectées, vous pouvez copier chaque clé individuellement ou cliquer sur Tout copier pour obtenir le contenu complet.", "检测到该消息后有AI回复,是否删除后续回复并重新生成?": "Une réponse IA a été détectée après ce message, voulez-vous supprimer les réponses suivantes et régénérer ?", + "检测失败": "", + "检测完成:新增 {{add}} 个,删除 {{remove}} 个": "", "检测必须等待绘图成功才能进行放大等操作": "La détection doit attendre que le dessin réussisse avant d'effectuer un zoom et d'autres opérations", "概览": "Overview", "模型": "Modèle", @@ -1949,6 +2072,7 @@ "模型价格:{{symbol}}{{price}}": "Prix du modèle : {{symbol}}{{price}}", "模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Prix du modèle : {{symbol}}{{price}} * {{ratioType}} : {{ratio}} = {{symbol}}{{total}}", "模型价格:{{symbol}}{{price}} / 次": "Prix du modèle : {{symbol}}{{price}} / requête", + "模型价格未配置": "Prix du modèle non configuré", "模型倍率": "Ratio", "模型倍率 {{modelRatio}}": "Ratio du modèle {{modelRatio}}", "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "Ratio du modèle {{modelRatio}}, ratio de cache {{cacheRatio}}, ratio de complétion {{completionRatio}}, {{ratioType}} {{ratio}}", @@ -1973,8 +2097,8 @@ "模型名称已存在": "Le nom du modèle existe déjà", "模型固定价格": "Prix du modèle par appel", "模型图标": "Icône du modèle", - "模型定价设置": "Model Pricing", "模型定价,需要登录访问": "Tarification du modèle, nécessite une connexion pour y accéder", + "模型定价设置": "Model Pricing", "模型广场": "Marché des modèles", "模型拉取失败: {{error}}": "Failed to pull model: {{error}}", "模型排行": "Classement des modèles", @@ -1984,8 +2108,8 @@ "模型更新成功!": "Modèle mis à jour avec succès !", "模型未加入列表,可能无法调用": "Le modèle n'est pas dans la liste, il peut ne pas être disponible", "模型正则": "Regex de modèle", - "模型正则不能为空": "La regex de modèle ne peut pas être vide", "模型正则(每行一个)": "Regex de modèle (un par ligne)", + "模型正则不能为空": "La regex de modèle ne peut pas être vide", "模型消耗分布": "Distribution de la consommation des modèles", "模型消耗趋势": "Tendance de la consommation des modèles", "模型版本": "Version du modèle", @@ -2016,17 +2140,24 @@ "次": "requête", "欢迎使用,请完成以下设置以开始使用系统": "Bienvenue, veuillez compléter les paramètres suivants pour commencer à utiliser le système", "欧元": "Euro", + "正则替换": "", "正在加载可用部署位置...": "Loading available deployment locations...", "正在加载签到状态...": "Chargement du statut d'enregistrement...", + "正在处理,请稍候": "", "正在处理大内容...": "Traitement de contenu volumineux...", + "正在批量处理,请稍候": "", + "正在批量检测,请稍候": "", "正在提交": "Envoi en cours", "正在构造请求体预览...": "Construction de l'aperçu du corps de la requête...", "正在检查 io.net 连接...": "Checking io.net connection...", + "正在检测,请稍候": "", "正在测试第 ${current} - ${end} 个模型 (共 ${total} 个)": "Test des modèles ${current} - ${end} sur ${total} au total", "正在跟随最新日志": "Following latest logs", "正在跳转 GitHub...": "Redirection vers GitHub...", "正在跳转...": "Redirection...", + "正常": "", "正常情况下,令牌的计费倍率由令牌所选的分组决定。特殊倍率可以根据「用户所在分组」进一步覆盖这个倍率。": "Normally, a token's billing ratio is determined by its selected group. Special ratios can override this based on the user's group.", + "正数为增加,负数为减少": "Positif pour ajouter, négatif pour soustraire", "此代理仅用于图片请求转发,Webhook通知发送等,AI API请求仍然由服务器直接发出,可在渠道设置中单独配置代理": "Ce proxy est utilisé uniquement pour le transfert des requêtes d'images, l'envoi de notifications Webhook, etc. Les requêtes d'API IA sont toujours émises directement par le serveur, le proxy peut être configuré séparément dans les paramètres du canal", "此修改将不可逆": "Cette modification sera irréversible", "此操作不可恢复,请仔细确认时间后再操作!": "Cette opération est irréversible, veuillez confirmer attentivement l'heure avant d'opérer !", @@ -2053,6 +2184,7 @@ "此项可选,用于覆盖请求参数。不支持覆盖 stream 参数": "Ceci est facultatif, utilisé pour remplacer les paramètres de requête. Ne prend pas en charge le remplacement du paramètre stream", "此项可选,用于覆盖请求头参数": "Ceci est facultatif, utilisé pour remplacer les paramètres d'en-tête de requête", "此项可选,用于通过自定义API地址来进行 API 调用,末尾不要带/v1和/": "Facultatif pour les appels d'API via une adresse d'API personnalisée, n'ajoutez pas /v1 et / à la fin", + "每个充值单位对应的 USD 金额,默认 1.0": "", "每个分组代表一个价格档位。管理员创建分组后,可以选择哪些档位对用户开放自选。": "Each group represents a pricing tier. After creating groups, admins can choose which tiers are open for user self-selection.", "每个用户最多可创建的令牌数量,默认 1000,设置过大可能会影响性能": "Nombre maximal de jetons que chaque utilisateur peut créer, par défaut 1000. Une valeur trop élevée peut affecter les performances", "每周": "Hebdomadaire", @@ -2068,7 +2200,9 @@ "永久删除您的两步验证设置": "Supprimer définitivement vos paramètres d'authentification à deux facteurs", "永久删除所有备用码(包括未使用的)": "Supprimer définitivement tous les codes de sauvegarde (y compris ceux non utilisés)", "汇率": "Taux de change", + "沙盒模式": "", "沙盒环境 RSA 私钥 Base64 (PKCS#8 DER)": "Clé privée RSA Base64 (PKCS#8 DER) de sandbox", + "沙盒环境 Waffo API 密钥": "", "沙盒环境 Waffo 公钥 Base64 (X.509 DER)": "Clé publique Waffo Base64 (X.509 DER) de sandbox", "没有匹配的字段": "Aucun champ correspondant", "没有匹配的日志条目": "No matching log entries", @@ -2085,8 +2219,8 @@ "注册": "S'inscrire", "注册 Passkey": "Enregistrer un Passkey", "注意": "Remarque", - "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "Remarque : Pour les API non-Chat, assurez-vous de saisir l'adresse API correcte, sinon elle pourrait ne pas fonctionner", "注意:JSON中重复的键只会保留最后一个同名键的值": "Remarque : Dans JSON, pour les clés dupliquées, seule la valeur de la dernière clé du même nom sera conservée", + "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "Remarque : Pour les API non-Chat, assurez-vous de saisir l'adresse API correcte, sinon elle pourrait ne pas fonctionner", "注销": "Se déconnecter", "注销成功!": "Déconnexion réussie !", "活跃文件": "Fichiers actifs", @@ -2095,7 +2229,9 @@ "流式": "Streaming", "流式响应完成": "Flux terminé", "流式输出": "Sortie en flux", + "流状态": "", "流量端口": "Traffic Port", + "流错误详情": "", "浅色": "Clair", "浅色模式": "Mode clair", "测活": "Health Check", @@ -2149,14 +2285,6 @@ "添加键值对": "Ajouter une paire clé-valeur", "添加问答": "Ajouter une FAQ", "添加额度": "Ajouter un quota", - "减少": "Soustraire", - "覆盖": "Remplacer", - "调整额度": "Ajuster le quota", - "调整额度成功": "Quota ajusté avec succès", - "当前额度": "Quota actuel", - "变更": "Modification", - "预计结果": "Résultat estimé", - "正数为增加,负数为减少": "Positif pour ajouter, négatif pour soustraire", "清理不活跃缓存": "Nettoyer le cache inactif", "清理失败": "Échec du nettoyage", "清理方式": "Mode de nettoyage", @@ -2165,6 +2293,7 @@ "清空全部缓存": "Vider tout le cache", "清空该规则缓存": "Vider le cache de cette règle", "清空重定向": "Effacer la redirection", + "清除": "", "清除历史日志": "Effacer les journaux historiques", "清除失效兑换码": "Effacer les codes d'échange non valides", "清除所有模型": "Effacer tous les modèles", @@ -2172,8 +2301,8 @@ "渠道 ID": "ID du Canal", "渠道ID,名称,密钥,API地址": "ID du canal, nom, clé, URL de base", "渠道亲和性": "Affinité de canal", - "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "L'affinité de canal réutilise le dernier canal réussi en fonction des clés extraites du contexte de la requête ou du body JSON.", "渠道亲和性:上游缓存命中": "Affinité de canal : hit du cache en amont", + "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "L'affinité de canal réutilise le dernier canal réussi en fonction des clés extraites du contexte de la requête ou du body JSON.", "渠道优先级": "Priorité du canal", "渠道信息": "Informations sur le canal", "渠道创建成功!": "Canal créé avec succès !", @@ -2201,6 +2330,8 @@ "点击\"确认延长\"后将立即扣除费用并延长容器运行时间": "After clicking \"Confirm Extension\", the fee will be deducted immediately and the container runtime will be extended", "点击上传文件或拖拽文件到这里": "Cliquez pour télécharger un fichier ou faites glisser et déposez un fichier ici", "点击下方按钮通过 Telegram 完成绑定": "Cliquez sur le bouton ci-dessous pour terminer la liaison via Telegram", + "点击处理删除模型": "", + "点击处理新增模型": "", "点击复制ID": "Click to copy ID", "点击复制模型名称": "Cliquez pour copier le nom du modèle", "点击查看差异": "Cliquez pour voir les différences", @@ -2219,6 +2350,7 @@ "状态页面Slug": "Slug de la page d'état", "环境变量": "Environment Variables", "生产环境 RSA 私钥 Base64 (PKCS#8 DER)": "Clé privée RSA Base64 (PKCS#8 DER) de production", + "生产环境 Waffo API 密钥": "", "生产环境 Waffo 公钥 Base64 (X.509 DER)": "Clé publique Waffo Base64 (X.509 DER) de production", "生成令牌": "Générer un jeton", "生成并填入": "Générer et remplir", @@ -2250,6 +2382,7 @@ "用户信息": "Informations utilisateur", "用户信息更新成功!": "Informations utilisateur mises à jour avec succès !", "用户信息缺失": "Informations utilisateur manquantes", + "用户最大令牌数量": "Nombre maximal de jetons par utilisateur", "用户分组": "Votre groupe par défaut", "用户分组和额度管理": "Groupes et quotas", "用户分组的联动作用": "How user group affects other features", @@ -2266,9 +2399,9 @@ "用户名字段(可选)": "Champ nom d'utilisateur (optionnel)", "用户名或邮箱": "Nom d'utilisateur ou e-mail", "用户名称": "Nom d'utilisateur", + "用户在充值页面看到的支付方式名称,例如:Credit Card": "", "用户控制面板,管理账户": "Panneau de configuration de l'utilisateur pour la gestion du compte", "用户新建令牌时可选的分组,格式为 JSON 字符串,例如:{\"vip\": \"VIP 用户\", \"test\": \"测试\"},表示用户可以选择 vip 分组和 test 分组": "Groupes sélectionnables par l'utilisateur lors de la création d'un jeton, format de chaîne JSON, par exemple : {\"vip\": \"Utilisateur VIP\", \"test\": \"Test\"}, indiquant que l'utilisateur peut sélectionner le groupe vip et le groupe test", - "用户最大令牌数量": "Nombre maximal de jetons par utilisateur", "用户每周期最多请求完成次数": "Nombre maximal de requêtes utilisateur réussies par période", "用户每周期最多请求次数": "Nombre maximal de requêtes utilisateur par période", "用户注册时看到的网站名称,比如'我的网站'": "Nom du site Web que les utilisateurs voient lors de l'inscription, par exemple 'Mon site Web'", @@ -2289,6 +2422,7 @@ "留空则使用账号绑定的邮箱": "Si ce champ est laissé vide, l'adresse e-mail liée au compte sera utilisée", "留空则使用默认端点;支持 {path, method}": "Laissez vide pour utiliser le point de terminaison par défaut ; prend en charge {path, method}", "留空则保持原有密钥": "Laisser vide pour conserver la clé existante", + "留空则自动使用 服务器地址 + /api/waffo/webhook": "", "留空则默认使用服务器地址,注意不能携带http://或者https://": "Laissez vide pour utiliser l'adresse du serveur par défaut, notez que vous ne pouvez pas inclure http:// ou https://", "登 录": "Se connecter", "登录": "Se connecter", @@ -2315,6 +2449,7 @@ "硬件类型": "Hardware Type", "硬件配置": "Hardware Configuration", "确定": "OK", + "确定?": "Sûr ?", "确定删除此组?": "Confirmer la suppression de ce groupe ?", "确定导入": "Confirmer l'importation", "确定是否要修复数据库一致性?": "Êtes-vous sûr de vouloir réparer la cohérence de la base de données ?", @@ -2327,16 +2462,17 @@ "确定是否要复制此渠道?": "Êtes-vous sûr de vouloir copier ce canal ?", "确定是否要注销此用户?": "Êtes-vous sûr de vouloir déconnecter cet utilisateur ?", "确定清除所有失效兑换码?": "Êtes-vous sûr de vouloir effacer tous les codes d'échange non valides ?", + "确定要仅检测全部渠道上游模型更新吗?(不执行新增/删除)": "", "确定要修改所有子渠道优先级为 ": "Confirmer la modification de toutes les priorités des sous-canaux en ", "确定要修改所有子渠道权重为 ": "Confirmer la modification de tous les poids des sous-canaux en ", "确定要充值 $": "Confirmer la recharge de $", "确定要删除供应商 \"{{name}}\" 吗?此操作不可撤销。": "Êtes-vous sûr de vouloir supprimer le fournisseur \"{{name}}\" ? Cette opération est irréversible.", "确定要删除所有已自动禁用的密钥吗?": "Êtes-vous sûr de vouloir supprimer toutes les clés désactivées automatiquement ?", - "确定要删除所选的 {{count}} 个令牌吗?_many": "Êtes-vous sûr de vouloir supprimer les {{count}} jetons sélectionnés ?", "确定要删除所选的 {{count}} 个令牌吗?_one": "Êtes-vous sûr de vouloir supprimer le jeton sélectionné ?", + "确定要删除所选的 {{count}} 个令牌吗?_many": "Êtes-vous sûr de vouloir supprimer les {{count}} jetons sélectionnés ?", "确定要删除所选的 {{count}} 个令牌吗?_other": "Êtes-vous sûr de vouloir supprimer les {{count}} jetons sélectionnés ?", - "确定要删除所选的 {{count}} 个模型吗?_many": "Êtes-vous sûr de vouloir supprimer les {{count}} modèles sélectionnés ?", "确定要删除所选的 {{count}} 个模型吗?_one": "Êtes-vous sûr de vouloir supprimer le modèle sélectionné ?", + "确定要删除所选的 {{count}} 个模型吗?_many": "Êtes-vous sûr de vouloir supprimer les {{count}} modèles sélectionnés ?", "确定要删除所选的 {{count}} 个模型吗?_other": "Êtes-vous sûr de vouloir supprimer les {{count}} modèles sélectionnés ?", "确定要删除此 OAuth 提供商吗?": "Êtes-vous sûr de vouloir supprimer ce fournisseur OAuth ?", "确定要删除此API信息吗?": "Êtes-vous sûr de vouloir supprimer ces informations d'API ?", @@ -2348,6 +2484,7 @@ "确定要删除选中的": "Are you sure you want to delete the selected", "确定要启用所有密钥吗?": "Êtes-vous sûr de vouloir activer toutes les clés ?", "确定要启用此用户吗?": "Êtes-vous sûr de vouloir activer cet utilisateur ?", + "确定要对全部渠道执行上游模型更新吗?": "", "确定要提升此用户吗?": "Êtes-vous sûr de vouloir promouvoir cet utilisateur ?", "确定要更新所有已启用通道余额吗?": "Êtes-vous sûr de vouloir mettre à jour le solde de tous les canaux activés ?", "确定要测试所有未手动禁用渠道吗?": "Êtes-vous sûr de vouloir tester tous les canaux sauf ceux désactivés manuellement ?", @@ -2358,15 +2495,14 @@ "确定要降级此用户吗?": "Êtes-vous sûr de vouloir rétrograder cet utilisateur ?", "确定重置": "Confirmer la réinitialisation", "确定重置模型倍率吗?": "Confirmer la réinitialisation du ratio de modèle ?", - "确定?": "Sûr ?", "确认": "Confirmer", "确认作废": "Confirmer l'invalidation", "确认关闭提示": "Confirmer la fermeture", "确认冲突项修改": "Confirmer la modification de l'élément de conflit", "确认删除": "Confirmer la suppression", "确认删除模型": "Confirm Delete Model", - "确认删除该分组的所有规则?": "Delete all rules for this group?", "确认删除该分组?": "Confirm delete this group?", + "确认删除该分组的所有规则?": "Delete all rules for this group?", "确认删除该规则?": "Confirm delete this rule?", "确认取消密码登录": "Confirmer l'annulation de la connexion par mot de passe", "确认启用": "Confirmer l'activation", @@ -2394,13 +2530,16 @@ "磁盘使用率超过此值时拒绝请求": "Rejeter les requêtes lorsque l'utilisation du disque dépasse cette valeur", "磁盘可用空间小于缓存最大总量设置": "L'espace disque disponible est inférieur au paramètre de taille maximale du cache", "磁盘命中": "Hits disque", + "磁盘缓存最大总量 (MB)": "Taille maximale du cache disque (Mo)", "磁盘缓存占用的最大空间": "Espace maximal occupé par le cache disque", "磁盘缓存已清理": "Cache disque nettoyé", - "磁盘缓存最大总量 (MB)": "Taille maximale du cache disque (Mo)", "磁盘缓存设置(磁盘换内存)": "Paramètres du cache disque (échange disque/mémoire)", "磁盘缓存阈值 (MB)": "Seuil du cache disque (Mo)", "示例": "Exemple", "示例:{\"default\": [200, 100], \"vip\": [0, 1000]}。": "Exemple : {\"default\": [200, 100], \"vip\": [0, 1000]}.", + "视频": "Vidéo", + "视频Remix": "Remix vidéo", + "视频无法在当前浏览器中播放,这可能是由于:": "La vidéo ne peut pas être lue dans ce navigateur, cela peut être dû à :", "禁用": "Désactiver", "禁用 store 透传": "Désactiver le passage de store", "禁用2FA失败": "Échec de la désactivation de 2FA", @@ -2418,10 +2557,12 @@ "私有镜像仓库的密码": "Password for private image registry", "私有镜像仓库的用户名": "Username for private image registry", "秒": "Seconde", + "移动": "", + "移动请求头": "", "移除": "Remove", "移除 (-:)": "Remove (-:)", - "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "La suppression de la marque de copyright de One API doit d'abord être autorisée. La maintenance du projet demande beaucoup d'efforts. Si ce projet a du sens pour vous, veuillez le soutenir activement.", "移除 functionResponse.id 字段": "Supprimer le champ functionResponse.id", + "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "La suppression de la marque de copyright de One API doit d'abord être autorisée. La maintenance du projet demande beaucoup d'efforts. Si ce projet a du sens pour vous, veuillez le soutenir activement.", "空": "Vide", "窗口处理": "gestion des fenêtres", "窗口等待": "attente de la fenêtre", @@ -2454,19 +2595,19 @@ "等待中": "En attente", "等待获取邮箱信息...": "En attente d'obtenir des informations par e-mail...", "筛选": "Filtre", + "签到最大额度": "Quota maximum d'enregistrement", + "签到最小额度": "Quota minimum d'enregistrement", "签到功能允许用户每日签到获取随机额度奖励": "La fonction d'enregistrement permet aux utilisateurs de s'enregistrer quotidiennement pour recevoir des récompenses de quota aléatoires", "签到失败": "Échec de l'enregistrement", "签到奖励将直接添加到您的账户余额": "Les récompenses d'enregistrement seront directement ajoutées à votre solde de compte", "签到奖励的最大额度": "Quota maximum pour les récompenses d'enregistrement", "签到奖励的最小额度": "Quota minimum pour les récompenses d'enregistrement", "签到成功!获得": "Enregistrement réussi ! Reçu", - "签到最大额度": "Quota maximum d'enregistrement", - "签到最小额度": "Quota minimum d'enregistrement", "签到设置": "Paramètres d'enregistrement", "简单来说:同一个令牌分组,不同等级的用户可以享受不同的价格。": "In short: same token group, different user tiers can enjoy different prices.", "简洁": "Simple", - "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。": "Le mode simple renvoie uniquement le message ; le code d'état et le type d'erreur utiliseront les valeurs par défaut du système.", "简洁模式:按 type 全量清理对象,例如 redacted_thinking。": "Mode simple : Nettoyer tous les objets par type, ex. redacted_thinking.", + "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。": "Le mode simple renvoie uniquement le message ; le code d'état et le type d'erreur utiliseront les valeurs par défaut du système.", "管理": "Gérer", "管理 Ollama 模型的拉取和删除": "Manage Ollama model pulling and deletion", "管理你的 LinuxDO OAuth App": "Gérer votre application OAuth LinuxDO", @@ -2477,8 +2618,8 @@ "管理员暂时未设置任何关于内容": "L'administrateur n'a encore défini aucun contenu personnalisé \"À propos\".", "管理员未开启 Creem 充值!": "L'administrateur n'a pas activé la recharge Creem !", "管理员未开启Stripe充值!": "L'administrateur n'a pas activé la recharge Stripe !", - "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "L'administrateur n'a pas activé la fonction de recharge en ligne, veuillez contacter l'administrateur pour l'activer ou recharger avec un code d'échange.", "管理员未开启在线充值!": "L'administrateur n'a pas activé la recharge en ligne !", + "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "L'administrateur n'a pas activé la fonction de recharge en ligne, veuillez contacter l'administrateur pour l'activer ou recharger avec un code d'échange.", "管理员未开启在线支付功能,请联系管理员配置。": "Le paiement en ligne n'est pas activé par l'administrateur. Veuillez contacter l'administrateur.", "管理员未设置用户可选分组": "L'administrateur n'a pas défini de groupes sélectionnables par l'utilisateur", "管理员给用户分配的分组(如 vip)不仅决定用户身份,还会影响后续两个功能:": "The group assigned to a user (e.g., vip) not only determines their identity, but also affects the following two features:", @@ -2518,14 +2659,16 @@ "系统数据统计": "Statistiques des données système", "系统文档和帮助信息": "Documentation système et informations d'aide", "系统消息": "Messages système", + "系统版本": "Version du système", "系统管理功能": "Fonctions de gestion du système", "系统设置": "Système", "系统访问令牌": "Jeton d'accès au système", + "约": "Environ", "索引": "Index", "紧凑列表": "Liste compacte", + "纯字符串会直接覆盖整条请求头,或者点击“查看 JSON 示例”按 token 规则处理。": "", "累计签到": "Total des enregistrements", "累计获得": "Total reçu", - "约": "Environ", "线路描述": "Description de l'itinéraire", "组列表": "Liste des groupes", "组名": "Nom du groupe", @@ -2555,44 +2698,45 @@ "统计次数": "Nombre de statistiques", "统计额度": "Quota statistique", "继续": "Continuer", - "缓存 Tokens": "Jetons de cache", "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Cache {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Cache {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio : {{ratio}})", + "缓存 Tokens": "Jetons de cache", "缓存: {{cacheRatio}}": "Cache : {{cacheRatio}}", "缓存价格:{{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "Prix du cache : {{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M tokens (taux de cache : {{cacheRatio}})", "缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "Prix du cache : {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (taux de cache : {{cacheRatio}})", "缓存倍率": "Ratio de cache", "缓存倍率 {{cacheRatio}}": "Ratio de cache {{cacheRatio}}", "缓存写": "Écriture cache", - "缓存创建 Tokens": "Jetons de création de cache", "缓存创建 {{price}} / 1M tokens": "Création de cache {{price}} / 1M tokens", "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Création de cache {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Création de cache {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio : {{ratio}})", + "缓存创建 Tokens": "Jetons de création de cache", + "缓存创建: {{cacheCreationRatio}}": "Création de cache : {{cacheCreationRatio}}", "缓存创建: 1h {{cacheCreationRatio1h}}": "Création de cache : 1h {{cacheCreationRatio1h}}", "缓存创建: 5m {{cacheCreationRatio5m}}": "Création de cache : 5m {{cacheCreationRatio5m}}", "缓存创建: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Création de cache : 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存创建: {{cacheCreationRatio}}": "Création de cache : {{cacheCreationRatio}}", + "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Création du cache : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de création du cache {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存创建价格": "Prix de création du cache d'entrée", "缓存创建价格 {{symbol}}{{price}} / 1M tokens": "Prix de création du cache {{symbol}}{{price}} / 1M tokens", - "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "Total du prix de création de cache : 5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "Prix de création du cache : {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (taux de création de cache : {{cacheCreationRatio}})", "缓存创建价格:{{symbol}}{{price}} / 1M tokens": "Prix de création du cache : {{symbol}}{{price}} / 1M tokens", + "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "Total du prix de création de cache : 5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens", "缓存创建倍率": "Ratio de création du cache", + "缓存创建倍率 {{cacheCreationRatio}}": "Ratio de création de cache {{cacheCreationRatio}}", "缓存创建倍率 1h {{cacheCreationRatio1h}}": "Multiplicateur de création de cache 1h {{cacheCreationRatio1h}}", "缓存创建倍率 5m {{cacheCreationRatio5m}}": "Multiplicateur de création de cache 5m {{cacheCreationRatio5m}}", "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Ratio de création du cache 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存创建倍率 {{cacheCreationRatio}}": "Ratio de création de cache {{cacheCreationRatio}}", - "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Création du cache : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de création du cache {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存条目数": "Nombre d'entrées en cache", "缓存目录": "Répertoire de cache", "缓存目录磁盘空间": "Espace disque du répertoire de cache", "缓存读": "Lecture cache", "缓存读 {{price}} / 1M tokens": "Lecture du cache {{price}} / 1M tokens", + "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Lecture du cache : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio du cache {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存读取价格": "Prix de lecture du cache d'entrée", "缓存读取价格 {{symbol}}{{price}} / 1M tokens": "Prix de lecture du cache {{symbol}}{{price}} / 1M tokens", "缓存读取价格:{{symbol}}{{price}} / 1M tokens": "Prix de lecture du cache : {{symbol}}{{price}} / 1M tokens", "缓存读取价格:{{symbol}}{{total}} / 1M tokens": "Prix de lecture du cache : {{symbol}}{{total}} / 1M tokens", - "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Lecture du cache : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio du cache {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "缓存读取倍率": "", "缓存输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Entrée en cache : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio du cache {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "编辑": "Modifier", "编辑 OAuth 提供商": "Modifier le fournisseur OAuth", @@ -2603,6 +2747,7 @@ "编辑公告内容": "Modifier le contenu de l'annonce", "编辑分类": "Modifier la catégorie", "编辑成功": "Modification réussie", + "编辑支付方式": "", "编辑方式": "Mode d'édition", "编辑标签": "Modifier l'étiquette", "编辑模型": "Modifier le modèle", @@ -2672,11 +2817,12 @@ "若你的 OIDC Provider 支持 Discovery Endpoint,你可以仅填写 OIDC Well-Known URL,系统会自动获取 OIDC 配置": "Si votre fournisseur OIDC prend en charge le Discovery Endpoint, vous pouvez simplement remplir l'URL OIDC Well-Known, le système obtiendra automatiquement la configuration OIDC", "获取 Discovery 配置": "Récupérer la configuration Discovery", "获取 Discovery 配置失败:": "Échec de la récupération de la configuration Discovery : ", + "获取 io.net API Key": "Get io.net API Key", "获取 OIDC 配置失败,请检查网络状况和 Well-Known URL 是否正确": "Échec de l'obtention de la configuration OIDC, veuillez vérifier l'état du réseau et si l'URL Well-Known est correcte", "获取 OIDC 配置成功!": "Configuration OIDC obtenue avec succès !", "获取 Ollama 版本失败": "Failed to get Ollama version", - "获取 io.net API Key": "Get io.net API Key", "获取2FA状态失败": "Échec de l'obtention de l'état 2FA", + "获取令牌密钥失败": "", "获取充值配置失败": "Échec de la récupération de la configuration de recharge", "获取充值配置异常": "Erreur de configuration de recharge", "获取初始化状态失败": "Échec de l'obtention de l'état d'initialisation", @@ -2708,9 +2854,9 @@ "补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "Complétion {{completion}} tokens * Ratio de sortie {{completionRatio}}", "补全 {{completion}} tokens / 1M tokens * {{symbol}}{{price}}": "Complétion {{completion}} tokens / 1M tokens * {{symbol}}{{price}}", "补全价格": "Prix de complétion", - "补全价格已锁定": "Le prix de complétion est verrouillé", "补全价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "Prix de complétion : {{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (taux de complétion : {{completionRatio}})", "补全价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens": "Prix de complétion : {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens", + "补全价格已锁定": "Le prix de complétion est verrouillé", "补全倍率": "Ratio de complétion", "补全倍率 {{completionRatio}}": "Ratio de complétion {{completionRatio}}", "补全倍率值": "Valeur du ratio de complétion", @@ -2719,6 +2865,7 @@ "补单成功": "Commande complétée avec succès", "表单引用错误,请刷新页面重试": "Erreur de référence de formulaire, veuillez actualiser la page et réessayer", "表格视图": "Vue tableau", + "覆盖": "Remplacer", "覆盖模式:将完全替换现有的所有密钥": "Mode de remplacement : remplacera complètement toutes les clés existantes", "覆盖模板": "Modèle de remplacement", "覆盖现有密钥": "Remplacer les clés existantes", @@ -2729,10 +2876,8 @@ "规则为 JSON 数组;可视化与 JSON 模式共用同一份数据。": "Les règles sont un tableau JSON ; les modes visuel et JSON partagent les mêmes données.", "规则名称(可读性更好,也会出现在管理侧日志中)。": "Nom de la règle (pour une meilleure lisibilité, apparaît également dans les journaux d'administration).", "规则导航": "Navigation des règles", + "规则描述(可选)": "", "规则未找到,请刷新后重试": "Règle non trouvée, veuillez actualiser et réessayer", - "视频": "Vidéo", - "视频Remix": "Remix vidéo", - "视频无法在当前浏览器中播放,这可能是由于:": "La vidéo ne peut pas être lue dans ce navigateur, cela peut être dû à :", "角色": "Rôle", "解析响应数据时发生错误": "Erreur lors de l'analyse des données de réponse", "解析密钥文件失败: {{msg}}": "Échec de l'analyse du fichier de clés : {{msg}}", @@ -2741,21 +2886,20 @@ "解绑 Passkey": "Supprimer le Passkey", "解绑后将无法使用 Passkey 登录,确定要继续吗?": "Après la dissociation, vous ne pourrez plus vous connecter avec Passkey. Êtes-vous sûr de vouloir continuer ?", "解绑成功": "Dissociation réussie", - "警告": "Avertissement", - "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔": "Avertissement : après l'activation du keep-alive, si une erreur de canal se produit après l'écriture des données de keep-alive, le système ne peut pas réessayer. Si vous devez l'activer, il est recommandé de définir un intervalle Ping aussi grand que possible", - "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "Avertissement : la désactivation de l'authentification à deux facteurs supprimera définitivement vos paramètres de vérification et tous les codes de sauvegarde. Cette action est irréversible !", "计价币种": "Pricing Currency", "计算中": "Calculating", "计算成本": "Calculate Cost", "计算费用中...": "Calculating fees...", "计费乘数,倍率越低费用越低。例如倍率 0.5 表示半价。": "Billing multiplier — the lower the ratio, the lower the cost. E.g., ratio 0.5 means half price.", "计费开始": "Billing Start", + "计费摘要": "", "计费方式": "Mode de facturation", "计费显示模式": "Mode d'affichage de la facturation", "计费模式": "Mode de facturation", "计费类型": "Type de facturation", "计费过程": "Processus de mise en lots", "订单号": "N° de commande", + "订单支付方式": "Méthode de paiement de la commande", "订阅": "Abonnement", "订阅剩余": "Abonnement restant", "订阅套餐": "Plans d'abonnement", @@ -2770,6 +2914,7 @@ "记录请求与错误日志IP": "Enregistrer l'adresse IP du journal des requêtes et des erreurs", "设备": "Device", "设备类型偏好": "Préférence de type d'appareil", + "设置": "", "设置 Logo": "Définir un logo", "设置2FA失败": "Échec de la configuration de 2FA", "设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}": "Définir les remises correspondant aux différents montants de recharge, la clé est le montant de recharge, la valeur est le taux de remise, par exemple : {\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}", @@ -2799,6 +2944,7 @@ "设置默认地区和特定模型的专用地区": "Définir la région par défaut et les régions dédiées pour des modèles spécifiques", "设计与开发由": "Conçu et développé avec amour par", "设计版本": "b80c3466cb6feafeb3990c7820e10e50", + "设请求头": "", "访问 io.net 控制台的 API Keys 页面": "Visit the API Keys page of the io.net console", "访问容器": "Access Container", "访问模型部署功能需要先启用 io.net 部署服务": "Accessing model deployment features requires enabling the io.net deployment service first", @@ -2814,6 +2960,8 @@ "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "Le ratio de complétion de ce modèle est fixé à {{ratio}} par le backend. Le prix de complétion ne peut pas être modifié ici.", "该渠道已开启请求透传,参数覆写、模型重定向等 NewAPI 内置功能将失效,非最佳实践。": "La transmission des requêtes est activée pour ce canal ; les fonctionnalités intégrées de NewAPI (comme la surcharge des paramètres et la redirection de modèle) seront désactivées. Ce n'est pas une bonne pratique.", "该渠道已开启请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "La transmission des requêtes est activée pour ce canal. Les fonctionnalités intégrées de NewAPI (surcharge des paramètres, redirection de modèle, adaptation du canal, etc.) seront désactivées. Ce n'est pas une bonne pratique. Si cela cause des problèmes, merci de ne pas ouvrir d'issue.", + "该渠道暂无可处理的上游模型更新": "", + "该渠道未开启上游模型更新检测": "", "该规则未启用“作用域:包含规则名称”,无法按规则清空缓存。": "Cette règle n'a pas activé « Portée : inclure le nom de la règle », impossible de vider le cache par règle.", "该规则未设置参数覆盖模板": "Cette règle n'a pas de modèle de remplacement de paramètres défini", "该规则的缓存保留时长;0 表示使用默认 TTL:": "Durée de rétention du cache pour cette règle ; 0 signifie utiliser le TTL par défaut : ", @@ -2825,10 +2973,10 @@ "语音输入": "Entrée vocale", "语音输出": "Sortie vocale", "说明": "Description", - "说明信息": "Description", "说明:": "Description :", "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。": "Remarque : les tests sur cette page utilisent des requêtes non-streaming. Si un canal ne prend en charge que les réponses en streaming, les tests peuvent échouer. Veuillez vous référer à l’usage réel.", "说明:生成结果是可直接粘贴到渠道密钥里的 JSON(包含 access_token / refresh_token / account_id)。": "Note : Le résultat généré est un JSON qui peut être collé directement dans la clé du canal (inclut access_token / refresh_token / account_id).", + "说明信息": "Description", "请上传密钥文件": "Veuillez télécharger le fichier de clé", "请上传密钥文件!": "Veuillez télécharger le fichier de clé !", "请为渠道命名": "Veuillez nommer le canal", @@ -2841,6 +2989,7 @@ "请先填写 Ollama API 地址": "Please fill in Ollama API address first", "请先填写服务器地址": "Veuillez d'abord remplir l'adresse du serveur", "请先开启并填写音频输入价格。": "Activez et renseignez d'abord le prix d'entrée audio.", + "请先新增模型或从左侧列表选择一个模型": "", "请先粘贴回调 URL": "Veuillez d'abord coller l'URL de rappel", "请先输入密钥": "Veuillez d'abord saisir la clé", "请先选择一个作为模板的模型": "Veuillez d'abord choisir un modèle comme modèle de référence", @@ -2856,6 +3005,7 @@ "请再次输入新密码": "Veuillez saisir à nouveau le nouveau mot de passe", "请前往个人设置 → 安全设置进行配置。": "Veuillez aller dans Paramètres personnels → Paramètres de sécurité pour configurer.", "请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "Ne faites pas trop confiance à cette fonctionnalité, l'IP peut être usurpée, veuillez l'utiliser en conjonction avec des passerelles telles que nginx et cdn", + "请在 Waffo 后台获取 API 密钥、商户 ID 以及 RSA 密钥对,并配置回调地址。": "", "请在系统设置页面编辑分组倍率以添加新的分组:": "Veuillez modifier les ratios de groupe dans les paramètres système pour ajouter de nouveaux groupes :", "请填写完整的产品信息": "Veuillez renseigner l'ensemble des informations produit", "请填写完整的管理员账号信息": "Veuillez remplir les informations complètes du compte administrateur", @@ -2874,7 +3024,6 @@ "请求参数无效": "Invalid request parameters", "请求发生错误": "Une erreur s'est produite lors de la demande", "请求发生错误: ": "Une erreur s'est produite lors de la demande : ", - "模型价格未配置": "Prix du modèle non configuré", "请求后端接口失败:": "Échec de la requête de l'interface backend : ", "请求失败": "Échec de la demande", "请求头覆盖": "Remplacement des en-têtes de demande", @@ -2905,18 +3054,18 @@ "请至少选择一个渠道": "Veuillez sélectionner au moins un canal", "请输入 API Key,一行一个,格式:APIKey|Region": "Saisissez une API Key par ligne, format : APIKey|Region", "请输入 API Key,格式:APIKey|Region": "Saisissez l'API Key au format : APIKey|Region", - "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com": "Veuillez saisir AZURE_OPENAI_ENDPOINT, par exemple : https://docs-test-001.openai.azure.com", "请输入 Authorization Endpoint": "Veuillez saisir l'Authorization Endpoint", + "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com": "Veuillez saisir AZURE_OPENAI_ENDPOINT, par exemple : https://docs-test-001.openai.azure.com", "请输入 Client ID": "Veuillez saisir le Client ID", "请输入 Client Secret": "Veuillez saisir le Client Secret", + "请输入 io.net API Key": "Please enter io.net API Key", + "请输入 io.net API Key(敏感信息不显示)": "Please enter io.net API Key (sensitive information not displayed)", "请输入 JSON 格式的 OAuth 凭据,例如:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}": "Veuillez saisir les identifiants OAuth au format JSON, ex. :\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}", "请输入 JSON 格式的密钥内容,例如:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}": "Veuillez saisir le contenu de la clé au format JSON, par exemple :\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}", "请输入 OIDC 的 Well-Known URL": "Veuillez saisir l'URL Well-Known de l'OIDC", "请输入 Slug": "Veuillez saisir le Slug", "请输入 Token Endpoint": "Veuillez saisir le Token Endpoint", "请输入 User Info Endpoint": "Veuillez saisir le User Info Endpoint", - "请输入 io.net API Key": "Please enter io.net API Key", - "请输入 io.net API Key(敏感信息不显示)": "Please enter io.net API Key (sensitive information not displayed)", "请输入6位验证码或8位备用码": "Veuillez saisir le code de vérification à 6 chiffres ou le code de sauvegarde à 8 chiffres", "请输入API地址": "Veuillez saisir l'adresse de l'API", "请输入API地址!": "Veuillez saisir l'adresse de l'API !", @@ -2926,9 +3075,9 @@ "请输入Gotify服务器地址": "Veuillez saisir l'adresse du serveur Gotify", "请输入Gotify服务器地址,例如: https://gotify.example.com": "Veuillez saisir l'adresse du serveur Gotify, par exemple : https://gotify.example.com", "请输入JSON数组,如 [\"model-a\",\"model-b\"]": "Saisissez un tableau JSON, par ex. [\"model-a\",\"model-b\"]", - "请输入URL链接": "Veuillez saisir le lien URL", "请输入Uptime Kuma地址": "Veuillez saisir l'adresse Uptime Kuma", "请输入Uptime Kuma服务地址,如:https://status.example.com": "Veuillez saisir l'adresse du service Uptime Kuma, telle que : https://status.example.com", + "请输入URL链接": "Veuillez saisir le lien URL", "请输入Webhook地址": "Veuillez saisir l'adresse du Webhook", "请输入Webhook地址,例如: https://example.com/webhook": "Veuillez saisir l'URL du Webhook, par exemple : https://example.com/webhook", "请输入你的账户名以确认删除!": "Veuillez saisir votre nom de compte pour confirmer la suppression !", @@ -2953,14 +3102,14 @@ "请输入备注(仅管理员可见)": "Veuillez saisir une remarque (visible uniquement par les administrateurs)", "请输入套餐标题": "Veuillez saisir le titre du plan", "请输入完整的 JSON 格式密钥内容": "Veuillez saisir le contenu complet de la clé au format JSON", - "请输入完整的URL链接": "Veuillez saisir le lien URL complet", "请输入完整的URL,例如:https://api.openai.com/v1/chat/completions": "Veuillez saisir l'URL complète, par exemple : https://api.openai.com/v1/chat/completions", + "请输入完整的URL链接": "Veuillez saisir le lien URL complet", "请输入容器名称": "Please enter container name", "请输入密码": "Veuillez saisir un mot de passe", "请输入密钥": "Veuillez saisir la clé", - "请输入密钥!": "Veuillez saisir la clé !", "请输入密钥,一行一个": "Veuillez saisir la clé, une par ligne", "请输入密钥,一行一个,格式:AccessKey|SecretAccessKey|Region": "Saisissez les clés une par ligne, format : AccessKey|SecretAccessKey|Region", + "请输入密钥!": "Veuillez saisir la clé !", "请输入延长时长": "Please enter extension duration", "请输入总额度": "Veuillez saisir le quota total", "请输入您的密码": "Veuillez saisir votre mot de passe", @@ -3013,9 +3162,9 @@ "请输入认证器验证码或备用码": "Veuillez saisir le code de vérification de l'authentificateur ou le code de sauvegarde", "请输入说明": "Veuillez saisir la description", "请输入运行时长": "Please enter runtime duration", + "请输入邮箱!": "Veuillez saisir votre e-mail !", "请输入邮箱地址": "Veuillez saisir l'adresse e-mail", "请输入邮箱验证码!": "Veuillez saisir le code de vérification de l'e-mail !", - "请输入邮箱!": "Veuillez saisir votre e-mail !", "请输入部署名称": "Please enter deployment name", "请输入部署名称以完成二次确认": "Enter deployment name to complete secondary confirmation", "请输入部署地区,例如:us-central1\n支持使用模型映射格式\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}": "Veuillez saisir la région de déploiement, par exemple : us-central1\nPrend en charge l'utilisation du format de mappage de modèle\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}", @@ -3058,12 +3207,17 @@ "请选择该渠道所支持的模型,留空则不更改": "Veuillez sélectionner les modèles pris en charge par le canal, laisser vide ne changera rien", "请选择过期时间": "Veuillez sélectionner une date d'expiration", "请选择通知方式": "Veuillez sélectionner la méthode de notification", + "调整额度": "Ajuster le quota", + "调整额度成功": "Quota ajusté avec succès", "调用次数": "Nombre d'appels", "调用次数分布": "Distribution des appels de modèles", "调用次数排行": "Classement des appels de modèles", "调用趋势": "Tendance des appels", "调试信息": "Informations de débogage", "谨慎": "Prudent", + "警告": "Avertissement", + "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔": "Avertissement : après l'activation du keep-alive, si une erreur de canal se produit après l'écriture des données de keep-alive, le système ne peut pas réessayer. Si vous devez l'activer, il est recommandé de définir un intervalle Ping aussi grand que possible", + "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "Avertissement : la désactivation de l'authentification à deux facteurs supprimera définitivement vos paramètres de vérification et tous les codes de sauvegarde. Cette action est irréversible !", "豆包": "Doubao", "账单": "Factures", "账户充值": "Recharge de compte", @@ -3098,15 +3252,19 @@ "路径正则": "Regex de chemin", "路径正则(每行一个)": "Regex de chemin (un par ligne)", "跳转": "Sauter", + "转大写": "", + "转小写": "", "轮询": "Sondage", "轮询模式": "Mode de sondage", "轮询模式必须搭配Redis和内存缓存功能使用,否则性能将大幅降低,并且无法实现轮询功能": "Le mode de sondage doit être utilisé avec les fonctionnalités Redis et cache mémoire, sinon les performances seront considérablement réduites et la fonctionnalité de sondage ne pourra pas être réalisée", + "软错误": "", "输入": "Entrée", + "输入 {{price}} / 1M tokens": "Entrée {{price}} / 1M tokens", + "输入 $/1M tokens": "", "输入 OIDC 的 Authorization Endpoint": "Saisir le point de terminaison d'autorisation OIDC", "输入 OIDC 的 Client ID": "Saisir l'ID client OIDC", "输入 OIDC 的 Token Endpoint": "Saisir le point de terminaison de jeton OIDC", "输入 OIDC 的 Userinfo Endpoint": "Saisir le point de terminaison des informations utilisateur OIDC", - "输入 {{price}} / 1M tokens": "Entrée {{price}} / 1M tokens", "输入IP地址后回车,如:8.8.8.8": "Saisissez l'adresse IP et appuyez sur Entrée, par exemple : 8.8.8.8", "输入JSON对象": "Saisir l'objet JSON", "输入价格": "Prix d'entrée", @@ -3115,6 +3273,7 @@ "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "Prix d'entrée : {{symbol}}{{price}} / 1M tokens{{audioPrice}}", "输入你注册的 LinuxDO OAuth APP 的 ID": "Saisir l'ID de votre application OAuth LinuxDO enregistrée", "输入你的账户名{{username}}以确认删除": "Saisissez votre nom de compte{{username}}pour confirmer la suppression", + "输入倍率": "", "输入域名后回车": "Saisissez le domaine et appuyez sur Entrée", "输入域名后回车,如:example.com": "Saisissez le domaine et appuyez sur Entrée, par exemple : example.com", "输入密码,最短 8 位,最长 20 位": "Saisissez un mot de passe, d'au moins 8 caractères et jusqu'à 20 caractères", @@ -3123,6 +3282,7 @@ "输入模型倍率": "Saisir le ratio de modèle", "输入模型名称,例如 gpt-4.1": "Saisissez un nom de modèle, par exemple gpt-4.1", "输入每次价格": "Saisir le prix par utilisation", + "输入每次调用价格": "", "输入端口后回车,如:80 或 8000-8999": "Saisissez le port et appuyez sur Entrée, par exemple : 80 ou 8000-8999", "输入系统提示词,用户的系统提示词将优先于此设置": "Saisissez l'invite système, l'invite système de l'utilisateur aura la priorité sur ce paramètre", "输入自定义模型名称": "Saisir un nom de modèle personnalisé", @@ -3138,14 +3298,14 @@ "输入验证码完成设置": "Saisissez le code de vérification pour terminer la configuration", "输出": "Sortie", "输出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}": "Sortie {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}}", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Sortie : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de complétion {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Sortie : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de sortie {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "输出价格": "Prix de sortie", "输出价格 {{symbol}}{{price}} / 1M tokens": "Prix de sortie {{symbol}}{{price}} / 1M tokens", "输出价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "Prix de sortie : {{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (ratio d'achèvement : {{completionRatio}})", "输出价格:{{symbol}}{{price}} / 1M tokens": "Prix de sortie : {{symbol}}{{price}} / 1M tokens", "输出价格:{{symbol}}{{total}} / 1M tokens": "Prix de sortie : {{symbol}}{{total}} / 1M tokens", "输出倍率 {{completionRatio}}": "Ratio de sortie {{completionRatio}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Sortie : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de complétion {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Sortie : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio de sortie {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "边栏设置": "Barre latérale", "过期于": "Expire le", "过期时间": "Date d'expiration", @@ -3159,6 +3319,7 @@ "运行时长(小时)": "Runtime Duration (hours)", "返回修改": "Revenir pour modifier", "返回登录": "Retour à la connexion", + "返回错误": "", "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "Cette interface utilise les prix par défaut et les reconvertit automatiquement en JSON de ratios requis par le backend lors de l'enregistrement.", "这些价格都是可选项,不填也可以。": "Tous ces prix sont optionnels et peuvent être laissés vides.", "这将删除超过 10 分钟未使用的临时缓存文件": "Cela supprimera les fichiers de cache temporaires non utilisés depuis plus de 10 minutes", @@ -3218,6 +3379,7 @@ "选择部署位置(可多选)": "Select deployment location(s) (multiple selections allowed)", "选择预设模板(可选)": "Sélectionner un modèle prédéfini (optionnel)", "透传请求体": "Corps de transmission", + "透传请求头": "", "递归": "Récursif", "递归策略": "Stratégie récursive", "通义千问": "Qwen", @@ -3287,7 +3449,9 @@ "配置 SMTP": "Configurer SMTP", "配置 Telegram 登录": "Configurer la connexion Telegram", "配置 Turnstile": "Configurer Turnstile", + "配置 Waffo 充值时可用的支付方式,保存后在充值页面展示给用户。": "", "配置 WeChat Server": "Configurer le serveur WeChat", + "配置:": "Configuration :", "配置后的效果:": "After configuration:", "配置和消息已全部重置": "La configuration et les messages ont été entièrement réinitialisés", "配置套餐的有效时长": "Configurer la durée de validité du plan", @@ -3306,13 +3470,13 @@ "配置自定义 OAuth 提供商,支持 GitHub Enterprise、GitLab、Gitea、Nextcloud、Keycloak、ORY 等兼容 OAuth 2.0 协议的身份提供商": "Configurer des fournisseurs OAuth personnalisés, prend en charge GitHub Enterprise, GitLab, Gitea, Nextcloud, Keycloak, ORY et d'autres fournisseurs d'identité compatibles OAuth 2.0", "配置说明": "Instructions de configuration", "配置邮箱域名白名单": "Configurer la liste blanche des domaines e-mail", - "配置:": "Configuration :", "重启部署失败": "Failed to restart deployment", "重命名部署": "Rename Deployment", "重复提交": "Soumission en double", "重复的键名": "Nom de clé dupliqué", "重复的键名,此值将被后面的同名键覆盖": "Nom de clé dupliqué, cette valeur sera écrasée par la clé du même nom qui suit", "重定向 URL 填": "URL de redirection", + "重新上传": "", "重新发送": "Renvoyer", "重新生成": "Régénérer", "重新生成备用码": "Régénérer les codes de sauvegarde", @@ -3366,15 +3530,15 @@ "限制周期统一使用上方配置的“限制周期”值。": "La période de limite utilise uniformément la valeur \"période de limite\" configurée ci-dessus.", "限流": "Limitation de débit", "限购": "Limite", - "随机": "Aléatoire", - "随机模式": "Mode aléatoire", - "随机种子 (留空为随机)": "Graine aléatoire (laisser vide pour aléatoire)", "隐私政策": "Politique de confidentialité", "隐私政策已更新": "La politique de confidentialité a été mise à jour", "隐私政策更新失败": "Échec de la mise à jour de la politique de confidentialité", "隐私设置": "Confidentialité", "隐藏操作项": "Masquer les actions", "隐藏调试": "Masquer le débogage", + "随机": "Aléatoire", + "随机模式": "Mode aléatoire", + "随机种子 (留空为随机)": "Graine aléatoire (laisser vide pour aléatoire)", "零一万物": "Yi", "需要安全验证": "Vérification de sécurité requise", "需要添加的额度(支持负数)": "Besoin d'ajouter un quota (prend en charge les nombres négatifs)", @@ -3392,13 +3556,15 @@ "音频补全价格": "Prix de complétion audio", "音频补全价格:{{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (音频补全倍率: {{audioCompRatio}})": "Prix d'achèvement audio : {{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (ratio d'achèvement audio : {{audioCompRatio}})", "音频补全价格:{{symbol}}{{price}} / 1M tokens": "Prix de complétion audio : {{symbol}}{{price}} / 1M tokens", + "音频补全倍率": "", "音频补全倍率(仅部分模型支持该计费)": "Ratio d'achèvement audio (seuls certains modèles prennent en charge cette facturation)", + "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Entrée audio : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio audio {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "音频输入价格": "Prix d'entrée audio", "音频输入价格:{{symbol}}{{price}} / 1M tokens": "Prix d'entrée audio : {{symbol}}{{price}} / 1M tokens", + "音频输入倍率": "", "音频输入相关的倍率设置,键为模型名称,值为倍率": "Paramètres de ratio liés à l'entrée audio, la clé est le nom du modèle, la valeur est le ratio", - "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Entrée audio : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio audio {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "Paramètres de ratio liés à l'achèvement de la sortie audio, la clé est le nom du modèle, la valeur est le ratio", "音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Sortie audio : {{tokens}} / 1M * ratio du modèle {{modelRatio}} * ratio audio {{audioRatio}} * ratio de complétion audio {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "Paramètres de ratio liés à l'achèvement de la sortie audio, la clé est le nom du modèle, la valeur est le ratio", "页脚": "Pied de page", "页面未找到,请检查您的浏览器地址是否正确": "Page non trouvée, veuillez vérifier si l'adresse de votre navigateur est correcte", "页面渲染出错,请刷新页面重试": "Une erreur est survenue lors du rendu de la page. Veuillez rafraîchir et réessayer.", @@ -3416,9 +3582,10 @@ "预览更新": "Mise à jour de l'aperçu", "预览模板": "Aperçu du modèle", "预览请求体": "Aperçu du corps de la requête", - "预警阈值必须为正数": "Le seuil d'alerte doit être un nombre positif", "预计结束": "Estimated End", + "预计结果": "Résultat estimé", "预设模板": "Modèle prédéfini", + "预警阈值必须为正数": "Le seuil d'alerte doit être un nombre positif", "频率惩罚,减少重复词汇的出现": "Pénalité de fréquence, réduit la répétition des mots", "频率限制的周期(分钟)": "Période de limitation de débit (minutes)", "颜色": "Couleur", @@ -3467,10 +3634,6 @@ "默认折叠侧边栏": "Réduire la barre latérale par défaut", "默认测试模型": "Modèle de test par défaut", "默认用户消息": "Bonjour", - "默认补全倍率": "Taux de complétion par défaut", - "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(Actuellement, seule l'interface Epay est prise en charge, l'adresse du serveur ci-dessus est utilisée par défaut comme adresse de rappel !)", - ",当前无生效订阅,将自动使用钱包": ", aucun abonnement actif, le portefeuille sera utilisé automatiquement.", - ",时间:": ", time:", - ",点击更新": ", cliquez sur Mettre à jour" + "默认补全倍率": "Taux de complétion par défaut" } } diff --git a/web/src/i18n/locales/ja.json b/web/src/i18n/locales/ja.json index e0dc5a72..178b38a7 100644 --- a/web/src/i18n/locales/ja.json +++ b/web/src/i18n/locales/ja.json @@ -9,26 +9,48 @@ " 吗?": "に変更しますか?", " 秒": " 秒", " 秒。": " 秒。", - "$/1M tokens": "$/1M tokens", + ",当前无生效订阅,将自动使用钱包": "、有効なサブスクリプションがないため、自動的にウォレットを使用します", + ",时间:": "、時間:", + ",点击更新": "、クリックして更新してください", + "(共 {{total}} 个,省略 {{omit}} 个)": "", + "(共 {{total}} 个)": "", + "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(現在、Epay APIのみに対応しています。デフォルトで、上記のサーバーURLがコールバックアドレスとして使用されます。)", "(筛选后显示 {{count}} 条)_other": "(Showing {{count}} items after filtering)", "(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(入力 {{input}} tokens / 1M tokens * {{symbol}}{{price}}", "(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(入力 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + オーディオ入力 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}", "(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(入力 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + キャッシュ {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}", "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(入力 {{nonImageInput}} tokens + 画像入力 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}", + "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "[最大リクエスト数]と[最大成功リクエスト数]の最大値は2147483647です", + "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[最大リクエスト数]は0以上、[最大成功リクエスト数]は1以上である必要があります", + "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", + "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", + "{{count}} 项操作_other": "", + "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", + "{{name}} ID": "{{name}} ID", + "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", + "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", + "「用户可选」决定用户创建令牌时能否自主选择该分组。未勾选的分组只能由管理员在后台分配给用户,不会出现在用户的令牌创建页面中。": "「ユーザー選択可」はトークン作成時にユーザーがこのグループを選択できるかを制御します。未チェックのグループは管理者のみが割り当て可能で、トークン作成画面には表示されません。", + "• 视频服务商的跨域限制": "• Cross-origin limitations from the video provider", + "• 防盗链保护机制": "• Hotlink protection mechanisms", + "• 需要特定的请求头或认证": "• Specific headers or authentication are required", + "© {{currentYear}}": "© {{currentYear}}", + "| 基于": "| ベース: ", + "$/1M tokens": "$/1M tokens", + "$/次": "", "0 - 最低": "0 - 最低", "0 表示不限": "0 は無制限を意味します", "0.002-1之间的小数": "0.002~1の小数", "0.1以上的小数": "0.1以上の小数", - "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。": "1) 「認可ページを開く」をクリックしてログインを完了します。2) ブラウザがlocalhostにリダイレクトされます(ページが開かなくても問題ありません)。3) アドレスバーの完全なURLをコピーして下に貼り付けます。4)「生成して入力」をクリックします。", "1. 管理员在此创建分组并设置倍率": "1. 管理者がここでグループを作成しレートを設定", + "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。": "1) 「認可ページを開く」をクリックしてログインを完了します。2) ブラウザがlocalhostにリダイレクトされます(ページが開かなくても問題ありません)。3) アドレスバーの完全なURLをコピーして下に貼り付けます。4)「生成して入力」をクリックします。", "10 - 最高": "10 - 最高", "1h缓存创建 {{price}} / 1M tokens": "1h キャッシュ作成 {{price}} / 1M tokens", "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "1h キャッシュ作成 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "1h cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio: {{ratio}})", + "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "1h キャッシュ作成: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 1h キャッシュ作成倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", "1h缓存创建价格 {{symbol}}{{price}} / 1M tokens": "1h キャッシュ作成価格 {{symbol}}{{price}} / 1M tokens", "1h缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h缓存创建倍率: {{cacheCreationRatio1h}})": "1h cache creation price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h cache creation ratio: {{cacheCreationRatio1h}})", "1h缓存创建价格:{{symbol}}{{price}} / 1M tokens": "1h キャッシュ作成価格:{{symbol}}{{price}} / 1M tokens", - "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "1h キャッシュ作成: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 1h キャッシュ作成倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", "2 - 低": "2 - 低", "2. 管理员在用户管理中将用户分配到对应分组(如 vip)": "2. 管理者がユーザー管理でユーザーをグループに割り当て(例:vip)", "2025年5月10日后添加的渠道,不需要再在部署的时候移除模型名称中的\".\"": "2025年5月10日以降に追加されたチャネルでは、デプロイ時にモデル名から「.」を削除する必要はなくなりました。", @@ -39,22 +61,25 @@ "5m缓存创建 {{price}} / 1M tokens": "5m キャッシュ作成 {{price}} / 1M tokens", "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "5m キャッシュ作成 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "5m cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio: {{ratio}})", + "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "5m キャッシュ作成: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 5m キャッシュ作成倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", "5m缓存创建价格 {{symbol}}{{price}} / 1M tokens": "5m キャッシュ作成価格 {{symbol}}{{price}} / 1M tokens", "5m缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m缓存创建倍率: {{cacheCreationRatio5m}})": "5m cache creation price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m cache creation ratio: {{cacheCreationRatio5m}})", "5m缓存创建价格:{{symbol}}{{price}} / 1M tokens": "5m キャッシュ作成価格:{{symbol}}{{price}} / 1M tokens", - "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "5m キャッシュ作成: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 5m キャッシュ作成倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", "8 - 高": "8 - 高", "AGPL v3.0协议": "AGPL v3.0ライセンス", "AI 对话": "AIチャット", "AI模型测试环境": "AIモデルテスト環境", "AI模型配置": "AIモデル設定", "AK/SK 模式:使用 AccessKey 和 SecretAccessKey;API Key 模式:使用 API Key": "AK/SK mode uses AccessKey and SecretAccessKey; API Key mode uses an API Key", + "anthropic-beta JSON 示例": "", "API Key": "API Key", "API Key 模式下不支持批量创建": "APIキーモードでは一括作成はサポート対象外です", "API Key 验证失败": "API Key verification failed", "API Key 验证成功!连接到 io.net 服务正常": "API Key verification successful! Connection to io.net service is normal", "API 地址和相关配置": "ベースURLと関連設定", "API 密钥": "APIキー", + "API 密钥 (沙盒)": "", + "API 密钥 (生产)": "", "API 文档": "APIドキュメント", "API 配置": "API設定", "API令牌管理": "APIトークン管理", @@ -64,14 +89,13 @@ "API地址": "ベースURL", "API渠道配置": "APIチャネル設定", "API端点": "APIエンドポイント", - "Authorization Endpoint": "Authorization Endpoint", "Authorization callback URL 填": "Authorization callback URLを入力してください", + "Authorization Endpoint": "Authorization Endpoint", + "auto分组调用链路": "自動グループ連携", "Bark推送URL": "BarkプッシュURL", "Bark推送URL必须以http://或https://开头": "BarkプッシュURLは、http://またはhttps://で始まることが必須です", "Bark通知": "Bark通知", "Basic Auth 头": "Basic Auth ヘッダー", - "CPU 使用率超过此值时拒绝请求": "CPU使用率がこの値を超えた場合にリクエストを拒否", - "CPU 阈值 (%)": "CPUしきい値 (%)", "Cached tokens": "Cached tokens", "Cached tokens 占比口径由后端返回:Claude 语义按 cached/(prompt+cached),其余按 cached/prompt。": "キャッシュトークン比率はバックエンドから返されます:Claudeのセマンティクスはcached/(prompt+cached)、その他はcached/promptで計算されます。", "Changing batch type to:": "Changing batch type to:", @@ -87,13 +111,20 @@ "Client Secret": "Client Secret", "Codex 授权": "Codex 認可", "Codex 渠道不支持批量创建": "Codexチャネルはバッチ作成をサポートしていません", + "common.changeLanguage": "common.changeLanguage", "Completion tokens": "Completion tokens", "Configuration": "Configuration", + "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。": "context_int/context_stringはリクエストコンテキストから読み取り、gjsonはエントリリクエストのJSON bodyからgjsonパスで読み取ります。", + "CPU 使用率超过此值时拒绝请求": "CPU使用率がこの値を超えた場合にリクエストを拒否", + "CPU 阈值 (%)": "CPUしきい値 (%)", "Creem API 密钥,敏感信息不显示": "Creem API key, sensitive information not displayed", "Creem Setting Tips": "Creem only supports preset fixed-amount products. These products and their prices need to be created and configured in advance on the Creem website, so custom dynamic amount top-ups are not supported. Configure the product name and price on Creem, obtain the Product Id, and then fill it in for the product below. Set the top-up amount and display price for this product in the new API.", "Creem 介绍": "Creem is the payment partner you always deserved, we strive for simplicity and straightforwardness on our APIs.", "Creem 充值": "Creem Recharge", "Creem 设置": "Creem Setting", + "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。": "default と vip は管理者が「ユーザー管理」で割り当てるのみ可能です。ユーザー等級別の料金設定やテストなど、ユーザーに自主選択させたくないシーンに適しています。", + "default为默认设置,可单独设置每个分类的安全等级": "「default」はデフォルト設定で、各分類のセキュリティレベルを個別に設定できます", + "default为默认设置,可单独设置每个模型的版本": "「default」はデフォルト設定で、各モデルのバージョンを個別に設定できます", "Dify渠道只适配chatflow和agent,并且agent不支持图片!": "Difyチャネルはchatflowとagentのみに対応しており、agentは画像のサポート対象外です", "Discord": "Discord", "Discord Client ID": "Discord Client ID", @@ -103,11 +134,10 @@ "Discovery scopes": "Discovery scopes", "Discovery 建议 scopes:": "推奨Discovery scopes:", "EUR (欧元)": "EUR (Euro)", + "false": "false", "GC 已执行": "GC実行済み", "GC 执行失败": "GC実行失敗", "GC 次数": "GC回数", - "GPU/容器": "GPU/Container", - "GPU数量": "Number of GPUs", "Gemini安全设置": "Geminiセキュリティ設定", "Gemini思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Gemini思考モード:BudgetTokens = MaxTokens * BudgetTokensの割合", "Gemini思考适配设置": "Gemini思考モード設定", @@ -122,10 +152,14 @@ "Gotify服务器地址": "GotifyサーバーURL", "Gotify服务器地址必须以http://或https://开头": "GotifyサーバーURLは、http://またはhttps://で始まることが必須です", "Gotify通知": "Gotify通知", + "GPU/容器": "GPU/Container", + "GPU数量": "Number of GPUs", "Grok设置": "Grok設定", "Haiku 模型": "Haikuモデル", "Homepage URL 填": "ホームページURLを入力してください", "ID": "ID", + "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护": "include_obfuscationはResponsesストリームの難読化フィールドを制御します。クライアントがこのセキュリティ保護を無効にするのを防ぐため、デフォルトで無効です", + "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "inference_geoフィールドはClaudeのデータ常駐推論リージョンを制御します。未承認の地理情報のパススルーを防ぐため、デフォルトで無効です", "IP": "IP", "IP白名单": "IP Whitelist", "IP白名单(支持CIDR表达式)": "IPホワイトリスト(CIDR表記に対応)", @@ -153,8 +187,8 @@ "LinuxDO": "LinuxDO", "LinuxDO ID": "LinuxDO ID", "Logo 图片地址": "ロゴ画像URL", - "MIT许可证": "MITライセンス", "Midjourney 任务记录": "Midjourneyタスク履歴", + "MIT许可证": "MITライセンス", "New API项目仓库地址:": "New APIプロジェクトリポジトリ:", "NewAPI 默认不会将入口请求的 User-Agent 透传到上游渠道;该条件仅用于识别访问本站点的客户端。": "NewAPIはデフォルトでは入力リクエストのUser-Agentを上流チャネルにパススルーしません。この条件はこのサイトにアクセスするクライアントの識別にのみ使用されます。", "OAuth Client ID": "OAuth Client ID", @@ -165,7 +199,6 @@ "Ollama 模型管理": "Ollama Model Management", "Ollama 版本信息": "Ollama Version Info", "Opus 模型": "Opusモデル", - "POST 参数": "POSTパラメータ", "Passkey": "Passkey", "Passkey 已解绑": "Passkeyが連携解除されました。", "Passkey 已重置": "Passkeyがリセットされました。", @@ -173,50 +206,71 @@ "Passkey 注册失败,请重试": "Passkeyの登録に失敗しました。再試行してください", "Passkey 注册成功": "Passkeyの登録に成功しました", "Passkey 登录": "Passkeyログイン", + "Pay Method Name": "", + "Pay Method Type": "", "Ping间隔(秒)": "Ping間隔(秒)", + "POST 参数": "POSTパラメータ", + "price_xxx 的商品价格 ID,新建产品后可获得": "price_xxx の料金ID。新規製品の作成後に取得できます", "Prompt cache hit tokens": "Prompt cache hit tokens", "Prompt tokens": "Prompt tokens", "Reasoning Effort": "Reasoning Effort", "Request ID": "Request ID", + "RSA 私钥 (沙盒)": "", + "RSA 私钥 (生产)": "", + "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "safety_identifierフィールドは、OpenAIが利用ポリシーに違反する可能性のあるアプリユーザーを特定するために使用されます。ユーザーのプライバシーを保護するため、デフォルトでは無効です", + "Scopes(可选)": "Scopes(オプション)", + "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "service_tierフィールドはサービス階層の指定に使用されます。パススルーを許可すると実際の課金額が想定を上回る場合があるため、追加料金を避けるためにデフォルトでは無効になっています", + "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "sk_xxx または rk_xxx のStripe APIキー。機密情報は表示されません", "SMTP 发送者邮箱": "SMTP 送信元メールアドレス", "SMTP 服务器地址": "SMTP サーバーURL", "SMTP 端口": "SMTP ポート", "SMTP 访问凭证": "SMTP 認証情報", "SMTP 账户": "SMTP アカウント", + "Sonnet 模型": "Sonnetモデル", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "speed フィールドは Claude の推論速度モードを制御します。意図せず fast モードへ切り替わるのを避けるため、デフォルトで無効です", "SSE 事件": "SSEイベント", "SSE数据流": "SSEデータストリーム", "SSRF防护开关详细说明": "SSRF保護スイッチの詳細説明", "SSRF防护设置": "SSRF保護設定", "SSRF防护详细说明": "SSRF保護の詳細説明", - "Scopes(可选)": "Scopes(オプション)", - "Sonnet 模型": "Sonnetモデル", + "standard 已被移除,vip 用户看不到": "standard は削除され、vipユーザーには表示されません", + "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "storeフィールドは、製品の評価と最適化のためにOpenAIがリクエストデータを保存することを許可します。デフォルトでは無効です。有効にすると、Codexが正常に利用できなくなる場合があります", "Stripe 设置": "Stripe 設定", "Stripe/Creem 商品ID(可选)": "Stripe/Creem 商品ID(任意)", "Stripe/Creem 需在第三方平台创建商品并填入 ID": "Stripe/Creem の商品は外部プラットフォームで作成し、ID を入力してください", - "TTL(秒)": "TTL(秒)", - "TTL(秒,0 表示默认)": "TTL(秒、0はデフォルト)", "Telegram": "Telegram", "Telegram Bot Token": "Telegram Bot Token", "Telegram Bot 名称": "Telegram Bot 名称", "Telegram ID": "Telegram ID", "Token Endpoint": "Token Endpoint", + "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。": "トークンは比率に基づいて「クォータ/回数」に換算されます。リクエスト完了後に差額精算(追加控除/返金)が行われます。", "Total tokens": "Total tokens", + "true": "true", + "TTL(秒,0 表示默认)": "TTL(秒、0はデフォルト)", + "TTL(秒)": "TTL(秒)", "Turnstile Secret Key": "Turnstile Secret Key", "Turnstile Site Key": "Turnstile Site Key", - "URL 标识,只能包含小写字母、数字和连字符": "URL識別子、小文字、数字、ハイフンのみ使用可能", - "URL链接": "URL", - "USD (美元)": "USD (US Dollar)", "Unix时间戳": "Unixタイムスタンプ", "Uptime Kuma地址": "Uptime Kumaアドレス", "Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)": "Uptime Kumaの監視分類管理:サービスステータス表示用に、複数の監視分類を設定できます(最大20個)", + "URL 标识,只能包含小写字母、数字和连字符": "URL識別子、小文字、数字、ハイフンのみ使用可能", + "URL链接": "URL", + "USD (美元)": "USD (US Dollar)", "User Info Endpoint": "User Info Endpoint", "User-Agent include(每行一个,可不写)": "User-Agent include(1行に1つ、オプション)", "Value 正则": "値の正規表現", "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AIはfunctionResponse.idフィールドをサポートしていません。有効にすると、このフィールドは自動的に削除されます", - "WeChat Server 服务器地址": "WeChat Server サーバーURL", - "WeChat Server 访问凭证": "WeChatサーバー認証情報", - "Web 搜索调用 {{webSearchCallCount}} 次": "Web 検索呼び出し {{webSearchCallCount}} 回", + "Waffo API 参数,可空,例如:CREDITCARD,DEBITCARD(最多64位)": "", + "Waffo API 参数,可空(最多64位)": "", + "Waffo 充值": "", + "Waffo 充值的最低数量,默认 1": "", + "Waffo 公钥 (沙盒)": "", + "Waffo 公钥 (生产)": "", + "Waffo 商户 ID": "", + "Waffo 是一个支付聚合平台,支持多种支付方式。": "", + "Waffo 设置": "", "Web 搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Web 検索: {{count}} / 1K * 単価 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", + "Web 搜索调用 {{webSearchCallCount}} 次": "Web 検索呼び出し {{webSearchCallCount}} 回", "Webhook 密钥": "Webhook Secret", "Webhook 签名密钥": "Webhook署名シークレット", "Webhook地址": "Webhook URL", @@ -224,62 +278,38 @@ "Webhook请求结构说明": "Webhookリクエスト構造の説明", "Webhook通知": "Webhook通知", "Web搜索价格:{{symbol}}{{price}} / 1K 次": "Web検索料金:{{symbol}}{{price}} / 1K回", + "WeChat Server 服务器地址": "WeChat Server サーバーURL", + "WeChat Server 访问凭证": "WeChatサーバー認証情報", "Well-Known URL": "Well-Known URL", "Well-Known URL 必须以 http:// 或 https:// 开头": "Well-Known URLは、http://またはhttps://で始まることが必須です", + "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示": "whsec_xxx のWebhook署名シークレット。機密情報は表示されません", "Worker地址": "Workerアドレス", "Worker密钥": "WorkerAPIキー", - "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "[最大リクエスト数]と[最大成功リクエスト数]の最大値は2147483647です", - "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[最大リクエスト数]は0以上、[最大成功リクエスト数]は1以上である必要があります", - "auto分组调用链路": "自動グループ連携", - "common.changeLanguage": "common.changeLanguage", - "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。": "context_int/context_stringはリクエストコンテキストから読み取り、gjsonはエントリリクエストのJSON bodyからgjsonパスで読み取ります。", - "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。": "default と vip は管理者が「ユーザー管理」で割り当てるのみ可能です。ユーザー等級別の料金設定やテストなど、ユーザーに自主選択させたくないシーンに適しています。", - "default为默认设置,可单独设置每个分类的安全等级": "「default」はデフォルト設定で、各分類のセキュリティレベルを個別に設定できます", - "default为默认设置,可单独设置每个模型的版本": "「default」はデフォルト設定で、各モデルのバージョンを個別に設定できます", - "false": "false", - "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护": "include_obfuscationはResponsesストリームの難読化フィールドを制御します。クライアントがこのセキュリティ保護を無効にするのを防ぐため、デフォルトで無効です", - "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "inference_geoフィールドはClaudeのデータ常駐推論リージョンを制御します。未承認の地理情報のパススルーを防ぐため、デフォルトで無効です", - "price_xxx 的商品价格 ID,新建产品后可获得": "price_xxx の料金ID。新規製品の作成後に取得できます", - "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "safety_identifierフィールドは、OpenAIが利用ポリシーに違反する可能性のあるアプリユーザーを特定するために使用されます。ユーザーのプライバシーを保護するため、デフォルトでは無効です", - "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "service_tierフィールドはサービス階層の指定に使用されます。パススルーを許可すると実際の課金額が想定を上回る場合があるため、追加料金を避けるためにデフォルトでは無効になっています", - "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "speed フィールドは Claude の推論速度モードを制御します。意図せず fast モードへ切り替わるのを避けるため、デフォルトで無効です", - "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "sk_xxx または rk_xxx のStripe APIキー。機密情報は表示されません", - "standard 已被移除,vip 用户看不到": "standard は削除され、vipユーザーには表示されません", - "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "storeフィールドは、製品の評価と最適化のためにOpenAIがリクエストデータを保存することを許可します。デフォルトでは無効です。有効にすると、Codexが正常に利用できなくなる場合があります", - "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。": "トークンは比率に基づいて「クォータ/回数」に換算されます。リクエスト完了後に差額精算(追加控除/返金)が行われます。", - "true": "true", - "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示": "whsec_xxx のWebhook署名シークレット。機密情報は表示されません", - "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", - "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", - "{{name}} ID": "{{name}} ID", - "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", - "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", - "| 基于": "| ベース: ", - "© {{currentYear}}": "© {{currentYear}}", - "• 视频服务商的跨域限制": "• Cross-origin limitations from the video provider", - "• 防盗链保护机制": "• Hotlink protection mechanisms", - "• 需要特定的请求头或认证": "• Specific headers or authentication are required", - "「用户可选」决定用户创建令牌时能否自主选择该分组。未勾选的分组只能由管理员在后台分配给用户,不会出现在用户的令牌创建页面中。": "「ユーザー選択可」はトークン作成時にユーザーがこのグループを選択できるかを制御します。未チェックのグループは管理者のみが割り当て可能で、トークン作成画面には表示されません。", "一个月": "1ヶ月", "一天": "1日", "一小时": "1時間", "一次调用消耗多少刀,优先级大于模型倍率": "1コールあたりの消費ドル額。モデル倍率より優先されます", - "一行一个屏蔽词,不需要符号分割": "NGワードを1行に1つずつ入力してください。記号による区切りは不要です", "一行一个,不区分大小写": "1行に1つずつ(大文字・小文字の区別なし)", + "一行一个屏蔽词,不需要符号分割": "NGワードを1行に1つずつ入力してください。記号による区切りは不要です", "一键填充到 FluentRead": "FluentReadにクイック入力", "三种操作的区别:": "3つの操作の違い:", "上一个表单块": "前のフォームブロック", "上一步": "前へ", + "上传 PNG/JPG/SVG 图片,建议尺寸 ≤ 128×128px": "", + "上传图片": "", "上次保存: ": "最終保存: ", + "上次检测到可加入模型": "", + "上次检测时间": "", "上游倍率同步": "アップストリーム倍率同期", "上游模型管理": "上流モデル管理", "上游返回": "Upstream response", "下一个表单块": "次のフォームブロック", + "下一次重置": "次回リセット", "下一步": "次へ", "下午好": "こんにちは", "下载日志": "Download Logs", "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "保存後にこのモデルでどのバックエンド項目に書き込まれるかを以下に表示します。元の JSON エディタとの整合確認に便利です。", + "下面是带注释的示例,仅用于参考;实际保存时请删除注释。": "", "不会出现": "表示されません", "不再提醒": "今後表示しない", "不勾选用户可选": "ユーザー選択不可", @@ -304,10 +334,10 @@ "两次输入的密码不一致": "パスワードが一致しません", "两次输入的密码不一致!": "パスワードが一致しません", "两步验证": "2要素認証", + "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。": "2要素認証(2FA)は、アカウントのセキュリティを強化する追加の保護機能です。有効にすると、ログイン時にパスワードと、認証アプリによって生成された認証コードの入力が必要になります。", "两步验证启用成功!": "2要素認証の有効化に成功しました", "两步验证已禁用": "2要素認証は無効になっています", "两步验证设置": "2要素認証設定", - "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。": "2要素認証(2FA)は、アカウントのセキュリティを強化する追加の保護機能です。有効にすると、ログイン時にパスワードと、認証アプリによって生成された認証コードの入力が必要になります。", "个": "個", "个GPU": " GPUs", "个人中心": "アカウント", @@ -364,6 +394,7 @@ "仅对自定义模型有效": "カスタムモデルにのみ有効", "仅当前层": "現在のレベルのみ", "仅当自动禁用开启时有效,关闭后不会自动禁用该渠道": "「自動的に無効にする」が有効な場合にのみ適用されます。無効にすると、このチャネルは自動的に無効になりません", + "仅提交已勾选": "", "仅支持": "対応形式:", "仅支持 JSON 对象,必须包含 access_token 与 account_id": "JSONオブジェクトのみサポート、access_tokenとaccount_idを含む必要があります", "仅支持 JSON 文件": "JSONファイルにのみ対応しています", @@ -371,14 +402,17 @@ "仅支持 OpenAI 接口格式": "OpenAI API形式にのみ対応しています", "仅显示已绑定": "バインド済みのみ表示", "仅显示矛盾倍率": "競合する倍率のみ表示", + "仅检测上游模型更新": "", "仅用于开发环境,生产环境应使用 HTTPS": "開発環境専用です。本番環境ではHTTPSを使用してください", "仅用于换算,实际保存的是额度": "換算用のみ、実際に保存されるのはクォータです", "仅用订阅": "サブスクリプションのみ", "仅用钱包": "ウォレットのみ", + "仅管理员可用。开启后,当系统定时检测全部渠道发现上游模型变更或检测异常时,将按你选择的通知方式发送汇总通知;渠道或模型过多时会自动省略部分明细。": "", "仅重置配置": "設定のみリセット", "今日关闭": "今日は表示しない", "今日已签到": "本日チェックイン済み", "今日已签到,累计签到": "本日チェックイン済み、累計チェックイン", + "仍有未处理项": "", "从 0.5 降到 0.3": "0.5から0.3に引き下げ", "从剪贴板粘贴配置": "クリップボードから貼り付け", "从官方模型库同步": "公式モデルライブラリから同期", @@ -389,10 +423,11 @@ "代理设置": "プロキシ設定", "代码已复制到剪贴板": "コードがクリップボードにコピーされました", "令牌": "トークン", + "令牌不存在": "", "令牌分组": "トークングループ", + "令牌分组,默认为用户的分组": "トークングループ、デフォルトはユーザーのグループ", "令牌分组设为 auto 时,按以下顺序依次尝试选择可用分组,排在前面的优先级更高": "トークングループがautoの場合、以下の順序で利用可能なグループを選択します。上位のグループが優先されます", "令牌分组设为 auto 时,系统按优先级顺序自动选择一个可用分组。": "トークングループがautoの場合、システムは優先順位に従って利用可能なグループを自動選択します。", - "令牌分组,默认为用户的分组": "トークングループ、デフォルトはユーザーのグループ", "令牌创建成功,请在列表页面点击复制获取令牌!": "トークンの作成に成功しました。リストページでコピーをクリックしてトークンを取得してください", "令牌名称": "トークン名", "令牌已重置并已复制到剪贴板": "トークンはリセットされ、クリップボードにコピーされました", @@ -404,8 +439,11 @@ "以及": "および", "仪表盘设置": "ダッシュボード", "价格": "料金", + "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "Price: {{symbol}}{{price}} * {{ratioType}}: {{ratio}}", + "价格:${{price}} * {{ratioType}}:{{ratio}}": "料金:${{price}} * {{ratioType}}:{{ratio}}", "价格摘要": "価格概要", "价格暂时不可用,请稍后重试": "Price temporarily unavailable, please try again later", + "价格模式": "", "价格模式(默认)": "価格モード(デフォルト)", "价格计算中...": "Calculating price...", "价格计算失败": "Price calculation failed", @@ -414,8 +452,6 @@ "价格设置方式": "料金設定方法", "价格重新计算中...": "Recalculating price...", "价格预估": "Price Estimate", - "价格:${{price}} * {{ratioType}}:{{ratio}}": "料金:${{price}} * {{ratioType}}:{{ratio}}", - "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "Price: {{symbol}}{{price}} * {{ratioType}}: {{ratio}}", "任一满足(OR)": "いずれか一致(OR)", "任务 ID": "タスクID", "任务ID": "タスクID", @@ -432,7 +468,6 @@ "余额充值管理": "残高チャージ管理", "作废": "無効化", "作废于": "無効化日", - "下一次重置": "次回リセット", "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "無効化するとこのサブスクリプションは直ちに失効します。履歴には影響しません。続行しますか?", "作用域": "スコープ", "作用域:包含分组": "スコープ:グループを含む", @@ -440,6 +475,8 @@ "作用域:包含规则名称": "スコープ:ルール名を含む", "你似乎并没有修改什么": "何も変更されていないようです", "你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "You can manually add them under “Custom model names”, click Fill and submit, or use the actions below to handle them automatically.", + "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?_other": "", + "使用 {{name}} 继续": "{{name}}で続行", "使用 Discord 继续": "Continue with Discord", "使用 GitHub 继续": "GitHubでログイン", "使用 JSON 对象格式,格式为:{\"组名\": [最多请求次数, 最多请求完成次数]}": "JSONオブジェクト形式で入力してください。形式:{\"グループ名\": [最大リクエスト数, 最大成功リクエスト数]}", @@ -448,43 +485,44 @@ "使用 Passkey 实现免密且更安全的登录体验": "Passkeyで、より安全なパスワードレスログインを実現。", "使用 Passkey 登录": "Passkeyでログイン", "使用 Passkey 验证": "Passkeyで認証", - "使用 {{name}} 继续": "{{name}}で続行", "使用 微信 继续": "WeChatでログイン", "使用 用户名 注册": "ユーザー名でサインアップ", "使用 邮箱或用户名 登录": "メールアドレスまたはユーザー名でログイン", "使用ID排序": "IDでソート", "使用分组": "使用グループ", + "使用原生额度输入": "生クォータで入力", "使用日志": "利用履歴", "使用模式": "利用モード", "使用统计": "利用統計", - "使用认证器应用扫描二维码": " 認証アプリスキャン", "使用认证器应用(如 Google Authenticator、Microsoft Authenticator)扫描下方二维码:": "Google Authenticator、Microsoft Authenticatorなどの認証アプリで、以下のQRコードをスキャンしてください:", + "使用认证器应用扫描二维码": " 認証アプリスキャン", "使用说明": "ガイド", "例如 /var/cache/new-api": "例:/var/cache/new-api", - "例如 https://docs.newapi.pro": "例:https://docs.newapi.pro", "例如 €, £, Rp, ₩, ₹...": "例:€, £, Rp, ₩, ₹...", + "例如 https://docs.newapi.pro": "例:https://docs.newapi.pro", + "例如 https://example.com/api/waffo/webhook": "", + "例如 https://example.com/console/topup": "", + "例如:": "例:", "例如: /bin/bash -c \"python app.py\"": "e.g.: /bin/bash -c \"python app.py\"", "例如: nginx:latest": "e.g.: nginx:latest", "例如: socks5://user:pass@host:port": "(例:socks5://user:pass@host:port)", - "例如发卡网站的购买链接": "例:カード発行サイトの購入リンク", - "例如(全渠道):": "例(全チャネル):", - "例如(指定渠道):": "例(指定チャネル):", - "例如:": "例:", "例如:-c": "e.g.: -c", "例如:/bin/bash": "e.g.: /bin/bash", "例如:0001": "例:0001", "例如:1000": "例:1000", "例如:100000": "e.g.: 100000", - "例如:2000": "例:2000", "例如:2,就是最低充值2$": "例:2(最低チャージ額$2)", + "例如:2000": "例:2000", "例如:4.99": "e.g.: 4.99", "例如:401, 403, 429, 500-599": "例:401, 403, 429, 500-599", "例如:7,就是7元/美金": "例:7(1USDあたり7CNY)", - "例如:GitHub Enterprise": "例:GitHub Enterprise", + "例如:Credit Card": "", "例如:email": "例:email", "例如:example.com": "例:example.com", "例如:github / si:google / https://example.com/logo.png / 🐱": "例:github / si:google / https://example.com/logo.png / 🐱", + "例如:GitHub Enterprise": "例:GitHub Enterprise", "例如:github-enterprise": "例:github-enterprise", + "例如:gpt-4.1-nano,regex:^claude-.*$,regex:^sora-.*$": "", "例如:https://example.com/.well-known/openid-configuration": "例:https://example.com/.well-known/openid-configuration", "例如:https://gitea.example.com": "例:https://gitea.example.com", "例如:https://yourdomain.com": "例:https://yourdomain.com", @@ -495,9 +533,13 @@ "例如:prod_6I8rBerHpPxyoiU9WK4kot": "e.g.: prod_6I8rBerHpPxyoiU9WK4kot", "例如:sub、id、data.user.id": "例:sub、id、data.user.id", "例如:基础套餐": "e.g.: Basic Package", + "例如:清理工具参数,避免上游校验错误": "", "例如:该请求不满足准入策略": "例:このリクエストはアドミッションポリシーを満たしていません", "例如:适合轻度使用": "例:軽めの利用に最適", "例如:需要等级 {{required}},你当前等级 {{current}}": "例:レベル{{required}}が必要です。現在のレベルは{{current}}です", + "例如(全渠道):": "例(全チャネル):", + "例如(指定渠道):": "例(指定チャネル):", + "例如发卡网站的购买链接": "例:カード発行サイトの購入リンク", "供应商": "プロバイダー", "供应商介绍": "プロバイダー紹介", "供应商信息:": "プロバイダー情報:", @@ -508,6 +550,8 @@ "供应商更新成功!": "プロバイダーの更新に成功しました!", "侧边栏管理(全局控制)": "サイドバー管理(グローバル設定)", "侧边栏设置保存成功": "サイドバー設定の保存に成功しました", + "保前缀": "", + "保后缀": "", "保存": "保存", "保存 Discord OAuth 设置": "Save Discord OAuth Settings", "保存 GitHub OAuth 设置": "GitHub OAuth 設定を保存", @@ -523,8 +567,8 @@ "保存备用码": "バックアップコード", "保存备用码以备不时之需": "万一に備え保存", "保存失败": "保存に失敗しました", - "保存失败:": "保存に失敗しました:", "保存失败,请重试": "保存に失敗しました。再試行してください", + "保存失败:": "保存に失敗しました:", "保存屏蔽词过滤设置": "NGワードフィルタリング設定を保存", "保存性能设置": "パフォーマンス設定を保存", "保存成功": "保存に成功しました", @@ -542,11 +586,11 @@ "保存预览": "保存プレビュー", "保存额度设置": "クォータ設定を保存", "保留": "保持", + "保留最近N个文件": "最新のN個のファイルを保持", + "保留最近N天": "最新のN日間を保持", "保留原值(目标已有值时不覆盖)": "元の値を保持(ターゲットに既に値がある場合は上書きしない)", "保留天数": "保持日数", "保留文件数": "保持ファイル数", - "保留最近N个文件": "最新のN個のファイルを保持", - "保留最近N天": "最新のN日間を保持", "修复数据库一致性": "データベースの整合性を修復", "修改为": "変更先:", "修改子渠道优先级": "サブチャネルの優先度を変更", @@ -558,6 +602,7 @@ "倍率信息": "倍率情報", "倍率是为了方便换算不同价格的模型": "倍率は、料金が異なるモデルの換算を容易にするためのものです", "倍率模式": "倍率モード", + "倍率模式(默认)": "", "倍率用于计费乘数,勾选「用户可选」后用户可在创建令牌时选择该分组": "レートは課金倍率です。「ユーザー選択可」をチェックすると、ユーザーがトークン作成時にこのグループを選択できます", "倍率类型": "倍率タイプ", "假设再加两个分组 default 和 vip,但不勾选用户可选:": "さらにdefaultとvipの2グループを追加しますが、ユーザー選択可にはチェックしません:", @@ -567,12 +612,12 @@ "停用": "無効", "允许 AccountFilter 参数": "AccountFilterパラメータを許可する", "允许 HTTP 协议图片请求(适用于自部署代理)": "HTTPプロトコルによる画像リクエストを許可する(セルフホストプロキシ向け)", - "允许 Turnstile 用户校验": "Turnstileによるユーザー検証を許可する", "允许 inference_geo 透传": "inference_geoパススルーを許可", "允许 safety_identifier 透传": "safety_identifierのパススルーを許可する", "允许 service_tier 透传": "service_tierのパススルーを許可する", "允许 speed 透传": "speed パススルーを許可", "允许 stream_options.include_obfuscation 透传": "stream_options.include_obfuscationパススルーを許可", + "允许 Turnstile 用户校验": "Turnstileによるユーザー検証を許可する", "允许不安全的 Origin(HTTP)": "安全でないオリジン(HTTP)を許可する", "允许回调(会泄露服务器 IP 地址)": "コールバックを許可する(サーバーIPアドレスが漏洩します)", "允许在 Stripe 支付中输入促销码": "Stripe決済でのプロモーションコード入力を許可する", @@ -593,13 +638,13 @@ "允许重试": "リトライを許可", "元": "CNY", "充值": "チャージ", - "充值价格显示": "チャージ料金表示", "充值价格(x元/美金)": "チャージ料金(x CNY/USD)", + "充值价格显示": "チャージ料金表示", "充值分组倍率": "チャージグループ倍率", "充值分组倍率不是合法的 JSON 字符串": "チャージグループ倍率は有効なJSON文字列ではありません", "充值数量": "チャージ額", - "充值数量不能小于": "チャージ額は次を下回れません:", "充值数量,最低 ": "チャージ額、最低", + "充值数量不能小于": "チャージ額は次を下回れません:", "充值方式设置": "チャージ方法設定", "充值方式设置不是合法的 JSON 字符串": "チャージ方法設定は有効なJSON文字列ではありません", "充值确认": "チャージの確認", @@ -615,8 +660,8 @@ "兑换成功!": "引き換えに成功しました", "兑换码充值": "引き換えコードによるチャージ", "兑换码创建成功": "引き換えコードの作成に成功しました", - "兑换码创建成功!": "引き換えコードの作成に成功しました", "兑换码创建成功,是否下载兑换码?": "引き換えコードの作成に成功しました。ダウンロードしますか?", + "兑换码创建成功!": "引き換えコードの作成に成功しました", "兑换码将以文本文件的形式下载,文件名为兑换码的名称。": "引き換えコードはテキストファイルとしてダウンロードされ、ファイル名は引き換えコードの名称になります。", "兑换码更新成功!": "引き換えコードの更新に成功しました", "兑换码生成管理": "引き換えコード生成管理", @@ -625,6 +670,7 @@ "全局控制侧边栏区域和功能显示,管理员隐藏的功能用户无法启用": "サイドバーの表示項目と機能をグローバルに制御します。管理者が非表示にした機能は、ユーザーは有効にできません", "全局设置": "グローバル設定", "全选": "すべて選択", + "全选当前列表模型": "", "全部": "すべて", "全部供应商": "すべて", "全部分组": "すべて", @@ -661,8 +707,8 @@ "关闭": "閉じる", "关闭侧边栏": "サイドバー折りたたみ", "关闭公告": "お知らせを閉じる", - "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "閉じると、このお知らせは今後表示されません(このブラウザのみ)。閉じてもよろしいですか?", "关闭后,此模型将不会被“同步官方”自动覆盖或创建": "オフにすると、このモデルは「公式から同期」機能によって自動的に上書き・作成されなくなります", + "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "閉じると、このお知らせは今後表示されません(このブラウザのみ)。閉じてもよろしいですか?", "关闭弹窗,已停止批量测试": "ポップアップを閉じたため、一括テストは停止されました", "关闭提示": "お知らせを閉じる", "其他": "その他", @@ -680,11 +726,25 @@ "内置": "組み込み", "内置 Ollama 镜像": "Built-in Ollama Image", "再次输入部署名称": "Enter Deployment Name Again", + "最低": "最低", + "最低充值数量": "", + "最低充值美元数量": "最低チャージUSD額", + "最后使用时间": "最終利用日時", + "最后更新": "Last Updated", + "最后请求": "最終リクエスト日時", + "最大GPU数量": "Max Number of GPUs", + "最大可用": "Max Available", + "最大条目数": "最大エントリ数", + "最终抵扣": "最終控除", + "最近一次": "最新", + "最近事件": "Recent Events", + "最高优先级": "最高優先", "写": "書込", "准入策略": "アドミッションポリシー", "准入策略 JSON(可选)": "アドミッションポリシー JSON(オプション)", "准备中...": "Preparing...", "准备完成初始化": "初期化準備完了", + "减少": "減少", "凭证已刷新": "資格情報が更新されました", "分类名称": "分類名称", "分组": "グループ", @@ -737,17 +797,20 @@ "创建用户": "ユーザー作成", "初始化失败,请重试": "初期化に失敗しました。再試行してください", "初始化系统": "システム初期化", + "删请求头": "", "删除": "削除", "删除 Key 来源": "キーソースを削除", "删除会彻底移除该订阅记录(含权益明细)。是否继续?": "削除するとこのサブスクリプション記録(特典詳細を含む)が完全に削除されます。続行しますか?", "删除后无法恢复,确定要删除模型 \"{{name}}\" 吗?": "Cannot be recovered after deletion, are you sure you want to delete model \"{{name}}\"?", "删除失败": "削除に失敗しました。", "删除密钥失败": "APIキーの削除に失敗しました", + "删除已选 {{selected}} / {{total}}": "", "删除成功": "削除に成功しました", "删除所选": "選択した項目を削除", "删除所选令牌": "選択したトークンを削除", "删除所选通道": "選択したチャネルを削除", "删除条件": "条件を削除", + "删除模型": "", "删除禁用密钥失败": "無効なAPIキーの削除に失敗しました", "删除禁用通道": "無効なチャネルを削除", "删除自动禁用密钥": "自動無効化APIキーを削除", @@ -764,19 +827,20 @@ "刷新缓存统计": "キャッシュ統計を更新", "刷新缓存统计失败": "キャッシュ統計の更新に失敗しました", "刷新页面": "ページを更新", + "前:": "前:", "前往 io.net API Keys": "Go to io.net API Keys", "前往设置": "Go to Settings", "前往设置页面": "Go to Settings Page", "前缀": "プレフィックス", - "前:": "前:", + "前置": "", + "剪贴板中未检测到连接信息": "クリップボードに接続情報が見つかりません", + "副本数量": "Number of Replicas", "剩余": "Remaining", "剩余备用码:": "残りバックアップコード:", "剩余时间": "Remaining Time", "剩余额度": "残りクォータ", - "剩余额度$": "残高 ($)", "剩余额度/总额度": "残りクォータ/総クォータ", - "剪贴板中未检测到连接信息": "クリップボードに接続情報が見つかりません", - "副本数量": "Number of Replicas", + "剩余额度$": "残高 ($)", "功能特性": "機能", "加入渠道": "Join Channel", "加入预填组": "事前入力グループへの参加", @@ -808,6 +872,7 @@ "区域": "リージョン", "升级分组": "アップグレードグループ", "单GPU小时费率": "Per GPU Hour Rate", + "单价 (USD)": "", "历史消耗": "消費履歴", "原价": "定価", "原价,和普通用户一样": "定価、一般ユーザーと同じ", @@ -815,19 +880,23 @@ "原密码": "現在のパスワード", "原生格式": "ネイティブ形式", "原生额度": "生クォータ", - "使用原生额度输入": "生クォータで入力", - "收起原生额度输入": "生クォータ入力を非表示", + "去前缀": "", + "去后缀": "", + "去处理{{type}}": "", + "去空格": "", "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "重複排除完了:重複排除前 {{before}} 個のAPIキー、重複排除後 {{after}} 個のAPIキー", "参与官方同步": "公式との同期", "参数": "パラメータ", "参数值": "パラメータ値", "参数覆盖": "パラメータの上書き", "参数覆盖 JSON 已复制": "パラメータオーバーライドJSONがコピーされました", + "参数覆盖已复制": "", "参数覆盖必须是合法的 JSON 对象": "パラメータオーバーライドは有効なJSONオブジェクトである必要があります", "参数覆盖必须是合法的 JSON 格式!": "パラメータオーバーライドは有効なJSON形式である必要があります!", "参数覆盖模板": "パラメータオーバーライドテンプレート", "参数覆盖模板 JSON 格式不正确": "パラメータオーバーライドテンプレートのJSON形式が正しくありません", "参数覆盖模板预览": "パラメータオーバーライドテンプレートプレビュー", + "参数覆盖详情": "", "参数配置": "パラメータ設定", "参数配置有误": "パラメータ設定が無効です", "参数错误": "パラメータエラー", @@ -841,6 +910,7 @@ "取消全选": "すべての選択を解除", "取消选择": "Deselect", "变换": "バリエーション", + "变更": "変更", "变焦": "ズーム", "变量值": "Variable Value", "变量名": "Variable Name", @@ -851,6 +921,7 @@ "可以根据用户分组增减令牌可选的分组范围。例如 vip 用户额外开放 premium 分组,或移除某个分组的选择权。": "ユーザーグループに基づいて選択可能なグループ範囲を調整できます。例:vipユーザーにpremiumグループへのアクセスを追加、または特定グループの選択権を削除。", "可以根据用户分组设置不同的计费倍率。例如 vip 用户使用 standard 令牌时倍率从 1.0 降为 0.8。": "ユーザーグループに基づいて異なる課金レートを設定できます。例:vipユーザーがstandardトークンを使用する際、レートが1.0から0.8に。", "可信": "信頼できる", + "可勾选需要执行的变更:新增会加入渠道模型列表,删除会从渠道模型列表移除。": "", "可在设置页面设置关于内容,支持 HTML & Markdown": "「このサービスについて」のコンテンツは設定ページで設定でき、HTML & Markdownに対応しています", "可手动填写,多个 scope 用空格分隔": "手動入力可能、複数のscopeはスペースで区切ります", "可用": "利用可能", @@ -866,14 +937,15 @@ "可视化": "可視化", "可视化倍率设置": "倍率設定の可視化", "可视化编辑": "ビジュアル編集", - "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。": "オプション。入力リクエストのUser-Agentをマッチ。いずれかの行がサブストリングとして一致(大文字小文字無視)すればヒットです。", - "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。": "オプション。抽出されたアフィニティキーを正規表現で検証。空欄は検証をスキップします。", - "可选。对请求路径进行匹配;不填表示匹配所有路径。": "オプション。リクエストパスをマッチ。空欄はすべてのパスにマッチします。", - "可选值": "選択可能な値", + "可空": "", "可选,公告的补充说明": "(オプション)お知らせの補足説明", "可选,用于复现结果": "オプション、結果の再現用", "可选:基于用户信息 JSON 做组合条件准入,条件不满足时返回自定义提示": "オプション:ユーザー情報JSONに基づく複合条件アドミッション。条件が満たされない場合、カスタムメッセージを返します", "可选:用于自动生成端点或 Discovery URL": "オプション:エンドポイントまたはDiscovery URLの自動生成に使用", + "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。": "オプション。入力リクエストのUser-Agentをマッチ。いずれかの行がサブストリングとして一致(大文字小文字無視)すればヒットです。", + "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。": "オプション。抽出されたアフィニティキーを正規表現で検証。空欄は検証をスキップします。", + "可选。对请求路径进行匹配;不填表示匹配所有路径。": "オプション。リクエストパスをマッチ。空欄はすべてのパスにマッチします。", + "可选值": "選択可能な値", "合计:{{total}}": "合計: {{total}}", "合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "合計: テキスト部分 {{textTotal}} + 音声部分 {{audioTotal}} = {{total}}", "同时重置消息": "メッセージも同時にリセット", @@ -881,6 +953,7 @@ "同步到渠道": "Sync to Channel", "同步向导": "同期ウィザード", "同步失败": "同期に失敗しました", + "同步字段": "", "同步成功": "同期に成功しました", "同步接口": "同期API", "同步渠道失败": "Failed to sync channel", @@ -890,10 +963,11 @@ "名称+密钥": "名称+APIキー", "名称不能为空": "名称は空にできません", "名称匹配类型": "名称マッチングタイプ", + "后:": "後:", + "后端固定": "", "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "バックエンド固定倍率: {{ratio}}。この項目は変換後の価格表示のみです。", "后端请求失败": "バックエンドリクエストに失敗しました", "后缀": "サフィックス", - "后:": "後:", "向右展开": "右に展開", "向左展开": "左に展開", "否": "いいえ", @@ -906,17 +980,17 @@ "启动部署失败": "Failed to start deployment", "启动配置": "Startup Configuration", "启用": "有効にする", - "启用 Prompt 检查": "プロンプトチェックを有効にする", "启用 io.net 部署": "Enable io.net Deployment", "启用 io.net 部署开关": "Enable io.net Deployment Switch", "启用 io.net 部署时必须填写 API Key": "API Key is required when enabling io.net deployment", + "启用 Prompt 检查": "プロンプトチェックを有効にする", + "启用 Waffo": "", "启用2FA失败": "2要素認証の有効化に失敗しました", "启用Claude思考适配(-thinking后缀)": "Claude思考モードを有効にする(-thinkingサフィックス)", "启用FunctionCall思维签名填充": "FunctionCall用のthoughtSignature自動付与を有効化", "启用Gemini思考后缀适配": "Gemini思考サフィックスモードを有効にする", "启用Ping间隔": "Ping間隔を有効にする", "启用SMTP SSL": "SMTP SSLを有効にする", - "强制使用 AUTH LOGIN": "AUTH LOGINを強制する", "启用SSRF防护(推荐开启以保护服务器安全)": "SSRF保護を有効にする(サーバーを保護するため、有効化を推奨します)", "启用供应商": "プロバイダーを有効化", "启用全部": "すべてを有効にする", @@ -925,6 +999,7 @@ "启用后套餐将在用户端展示。是否继续?": "有効化するとユーザー側に表示されます。続行しますか?", "启用后将优先复用上一次成功的渠道(粘滞选路)。": "有効にすると、前回成功したチャネルが優先的に再利用されます(スティッキールーティング)。", "启用后将使用 Creem Test Mode": "Use Creem Test Mode after enabling", + "启用后将使用 Waffo 沙盒环境": "", "启用密钥失败": "APIキーの有効化に失敗しました", "启用屏蔽词过滤功能": "NGワードフィルタリング機能を有効にする", "启用性能监控": "パフォーマンス監視を有効にする", @@ -954,10 +1029,14 @@ "响应缺少凭据": "レスポンスに資格情報がありません", "响应缺少授权链接": "レスポンスに認可リンクがありません", "商品价格 ID": "料金ID", + "商户 ID": "", "回答内容": "回答", "回调 URL 填": "コールバックURLを入力してください", "回调 URL 格式": "コールバックURL形式", "回调地址": "コールバックアドレス", + "回调支付方式": "コールバック支払い方法", + "回调调用者IP": "コールバック呼び出し元IP", + "回调通知地址": "", "固定价格": "固定料金", "固定价格(每次)": "固定料金(1回あたり)", "固定价格值": "固定料金", @@ -965,23 +1044,25 @@ "图标": "アイコン", "图标使用 react-icons(Simple Icons)或 URL/emoji,例如:github、gitlab、si:google": "アイコンはreact-icons(Simple Icons)またはURL/emojiを使用、例:github、gitlab、si:google", "图标使用@lobehub/icons库,如:OpenAI、Claude.Color,支持链式参数:OpenAI.Avatar.type={'platform'}、OpenRouter.Avatar.shape={'square'},查询所有可用图标请 ": "アイコンは `@lobehub/icons` ライブラリを使用しています(例:OpenAI、Claude.Color)。`OpenAI.Avatar.type={'platform'}`、`OpenRouter.Avatar.shape={'square'}` のようなチェーンパラメータに対応しています。利用可能なすべてのアイコンは、こちらで確認できます。", + "图标文件不能超过 100KB,请压缩后重新上传": "", "图混合": "Blend", "图片倍率 {{imageRatio}}": "画像倍率 {{imageRatio}}", "图片功能在自定义请求体模式下不可用": "カスタムリクエストモードでは画像機能は利用できません", "图片地址": "画像URL", "图片已添加": "画像が追加されました", - "图片生成调用:{{symbol}}{{price}} / 1次": "画像生成APIコール:{{symbol}}{{price}} / 1回", "图片生成:1 次 * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "画像生成: 1 回 * 単価 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", + "图片生成调用:{{symbol}}{{price}} / 1次": "画像生成APIコール:{{symbol}}{{price}} / 1回", "图片输入 {{price}} / 1M tokens": "画像入力 {{price}} / 1M tokens", "图片输入: {{imageRatio}}": "画像入力:{{imageRatio}}", + "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "画像入力: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 画像倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "图片输入价格": "画像入力価格", "图片输入价格 {{symbol}}{{price}} / 1M tokens": "画像入力価格 {{symbol}}{{price}} / 1M tokens", "图片输入价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (图片倍率: {{imageRatio}})": "画像入力料金:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens(画像倍率:{{imageRatio}})", "图片输入价格:{{symbol}}{{price}} / 1M tokens": "画像入力価格:{{symbol}}{{price}} / 1M tokens", "图片输入价格:{{symbol}}{{total}} / 1M tokens": "画像入力価格:{{symbol}}{{total}} / 1M tokens", + "图片输入倍率": "", "图片输入倍率(仅部分模型支持该计费)": "画像入力倍率(一部のモデルのみこの課金に対応)", "图片输入相关的倍率设置,键为模型名称,值为倍率,仅部分模型支持该计费": "画像入力に関する倍率設定です。キー:モデル名、値:倍率。この課金方法は一部のモデルのみ対応しています", - "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "画像入力: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 画像倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "图生文": "ディスクライブ", "图生视频": "画像からの動画生成", "在Gotify服务器创建应用后获得的令牌,用于发送通知": "Gotifyサーバーでアプリを作成後に取得する、通知送信用トークンです。", @@ -1012,12 +1093,12 @@ "填充新模板": "新しいテンプレートを入力", "填充旧模板": "古いテンプレートを入力", "填充模板": "テンプレートを入力", - "填充模板(全渠道)": "テンプレートを入力(全チャネル)", - "填充模板(指定渠道)": "テンプレートを入力(指定チャネル)", "填充模板:等级+激活": "テンプレート入力:レベル+アクティベーション", "填充模板:等级提示": "テンプレート入力:レベルプロンプト", "填充模板:组织或角色": "テンプレート入力:組織またはロール", "填充模板:组织提示": "テンプレート入力:組織プロンプト", + "填充模板(全渠道)": "テンプレートを入力(全チャネル)", + "填充模板(指定渠道)": "テンプレートを入力(指定チャネル)", "填入": "入力", "填入 CC Switch": "CC Switchを入力", "填入所有模型": "すべてのモデルを入力", @@ -1035,7 +1116,9 @@ "填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议": "ユーザー利用規約のコンテンツを設定すると、ユーザーがサインアップする際に、利用規約を読んだことへの同意チェックが求められます", "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "プライバシーポリシーのコンテンツを設定すると、ユーザーがサインアップする際に、プライバシーポリシーを読んだことへの同意チェックが求められます", "填写音频补全价格前,需要先填写音频输入价格。": "音声補完価格を入力する前に、先に音声入力価格を入力してください。", + "处理上游模型更新": "", "处理中": "Processing", + "处理全部渠道上游更新": "", "备份支持": "バックアップ対応", "备份状态": "バックアップステータス", "备注": "備考", @@ -1045,6 +1128,7 @@ "复制": "コピー", "复制代码": "コードをコピー", "复制令牌": "トークンをコピー", + "复制令牌失败": "", "复制全部": "すべてをコピー", "复制名称": "名称をコピー", "复制失败": "コピーに失敗しました", @@ -1063,6 +1147,7 @@ "复制渠道的所有信息": "チャネルのすべての情報をコピー", "复制版本号": "Copy Version", "复制生成的密钥并粘贴到此处": "Copy the generated key and paste it here", + "复制请求头": "", "复制连接信息": "接続情報をコピー", "复制链接": "Copy link", "外接设备": "外部デバイス", @@ -1086,13 +1171,13 @@ "套餐标题": "プラン名", "套餐标题不能为空": "プラン名は空にできません", "套餐的基本信息和定价": "プランの基本情報と価格", + "如:大带宽批量分析图片推荐": "例:広帯域での画像一括分析に推奨", + "如:香港线路": "例:香港回線", "如果亲和到的渠道失败,重试到其他渠道成功后,将亲和更新到成功的渠道。": "アフィニティチャネルが失敗した場合、別のチャネルでリトライが成功すると、アフィニティが成功したチャネルに更新されます。", "如果你对接的是上游One API或者New API等转发项目,请使用OpenAI类型,不要使用此类型,除非你知道你在做什么。": "New APIなどのリレープロジェクトに接続する場合は、OpenAIタイプを利用してください。設定内容を熟知している場合を除き、このタイプは利用しないでください", "如果用户请求中包含系统提示词,则使用此设置拼接到用户的系统提示词前面": "ユーザーリクエストにシステムプロンプトが含まれている場合、この設定内容がユーザーのシステムプロンプトの前に追加されます", "如果镜像为私有,请填写密码或Token": "If the image is private, please fill in the password or token", "如果镜像为私有,请填写用户名": "If the image is private, please fill in the username", - "如:大带宽批量分析图片推荐": "例:広帯域での画像一括分析に推奨", - "如:香港线路": "例:香港回線", "始终使用浅色主题": "常にライトテーマを使用", "始终使用深色主题": "常にダークテーマを使用", "字段映射": "フィールドマッピング", @@ -1160,6 +1245,7 @@ "密钥": "APIキー", "密钥 JSON 必须包含 access_token": "キーJSONにはaccess_tokenを含む必要があります", "密钥 JSON 必须包含 account_id": "キーJSONにはaccount_idを含む必要があります", + "密钥(编辑模式下,保存的密钥不会显示)": "APIキー(編集モードでは、保存済みのAPIキーは表示されません)", "密钥去重": "APIキーの重複排除", "密钥将以Bearer方式添加到请求头中,用于验证webhook请求的合法性": "シークレットキーは、Webhookリクエストの正当性を検証するため、Bearerトークンとしてリクエストヘッダーに追加されます。", "密钥已删除": "APIキーが削除されました", @@ -1177,7 +1263,6 @@ "密钥获取成功": "APIキーの取得に成功しました", "密钥输入方式": "APIキーの入力方式", "密钥预览": "APIキーのプレビュー", - "密钥(编辑模式下,保存的密钥不会显示)": "APIキー(編集モードでは、保存済みのAPIキーは表示されません)", "对于官方渠道,new-api已经内置地址,除非是第三方代理站点或者Azure的特殊接入地址,否则不需要填写": "公式チャネルの場合、new-apiにはベースURLが組み込まれているため、サードパーティのプロキシサイトやAzureの専用のエンドポイントでない限り、入力する必要はありません。", "对免费模型启用预消耗": "Enable pre-consumption for free models", "对域名启用 IP 过滤(推荐开启)": "ドメインのIPフィルタリングを有効にする(推奨)", @@ -1202,6 +1287,7 @@ "将只保留最近 {{value}} 个日志文件,其余将被删除。": "最新の {{value}} 個のログファイルのみ保持し、残りは削除されます。", "将大请求体临时存储到磁盘": "大きなリクエストボディをディスクに一時保存", "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "現在編集中のモデル {{name}} の価格設定を、選択済みの {{count}} 個のモデルに一括適用します。", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。_other": "", "将清除所有保存的配置并恢复默认设置,此操作不可撤销。是否继续?": "保存されているすべての設定がクリアされ、デフォルト設定に復元されます。この操作は元に戻すことはできません。続行しますか?", "将清除选定时间之前的所有日志": "選択した日時以前のすべてのログをクリアします", "将追加 2 条规则到现有规则列表。": "既存のルールリストに2つのルールが追加されます。", @@ -1216,9 +1302,9 @@ "展示价格": "Display Pricing", "嵌套映射:用户分组 → 使用分组 → 倍率": "ネスト構造:ユーザーグループ → 使用グループ → レート", "左侧边栏个人设置": "サイドバーのアカウント設定", - "已为 ${count} 个渠道设置标签!": "${count}個のチャネルにタグが設定されました", "已为 {{count}} 个模型设置{{type}}_one": "{{count}}個のモデルに{{type}}が設定されました_one", "已为 {{count}} 个模型设置{{type}}_other": "{{count}}個のモデルに{{type}}_otherが設定されました_other", + "已为 ${count} 个渠道设置标签!": "${count}個のチャネルにタグが設定されました", "已从 Discovery 自动填充配置": "Discoveryから設定を自動入力しました", "已从 Discovery 获取配置,可继续手动修改所有字段。": "Discoveryから設定を取得しました。すべてのフィールドを手動で変更できます。", "已作废": "無効化済み", @@ -1233,15 +1319,16 @@ "已切换至最优倍率视图,每个模型使用其最低倍率分组": "各モデルが最低倍率グループを利用する最適倍率ビューに切り替えられました", "已初始化": "初期化済み", "已删除": "削除済み", - "已删除 ${data} 个通道!": "${data}個のチャネルが削除されました!", "已删除 {{count}} 个令牌!": "{{count}}個のトークンが削除されました", "已删除 {{count}} 个令牌!_other": "Deleted {{count}} tokens!", "已删除 {{count}} 条失效兑换码_one": "無効な引き換えコードが{{count}}件削除されました_one", "已删除 {{count}} 条失效兑换码_other": "無効な引き換えコードが{{count}}件削除されました_other", + "已删除 ${data} 个通道!": "${data}個のチャネルが削除されました!", "已删除所有禁用渠道,共计 ${data} 个": "すべての無効なチャネル(合計${data}個)が削除されました", "已删除消息及其回复": "メッセージとその返信が削除されました", "已勾选": "選択済み", "已勾选 {{count}} 个模型": "{{count}} 個のモデルを選択済み", + "已勾选 {{count}} 个模型_other": "", "已发起支付": "支払いを開始しました", "已发送到 Fluent": "Fluentに送信されました", "已取消 Passkey 注册": "Passkeyの登録がキャンセルされました", @@ -1253,10 +1340,13 @@ "已填充提示模板": "プロンプトテンプレートが入力されました", "已填充模版": "テンプレートが入力されました", "已填充策略模板": "ポリシーテンプレートが入力されました", + "已处理上游模型更新:加入 {{added}} 个,删除 {{removed}} 个,本次忽略 {{ignored}} 个,当前已忽略模型 {{totalIgnored}} 个": "", "已备份": "バックアップ済み", "已复制": "コピーされました", "已复制 ${count} 个模型": "${count}個のモデルがコピーされました", "已复制 ID 到剪贴板": "ID copied to clipboard", + "已复制:": "コピーされました:", + "已复制:{{name}}": "コピーされました:{{name}}", "已复制全部数据": "すべてのデータをコピーしました", "已复制到剪切板": "クリップボードにコピーされました", "已复制到剪贴板": "クリップボードにコピーされました", @@ -1265,14 +1355,15 @@ "已复制模型名称": "モデル名がコピーされました", "已复制版本号": "Version copied", "已复制自动生成的 API Key": "Auto-generated API Key copied", - "已复制:": "コピーされました:", - "已复制:{{name}}": "コピーされました:{{name}}", "已完成": "Completed", "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "モデル {{name}} の価格設定を {{count}} 個のモデルに一括適用しました", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型_other": "", "已开启全局请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "全体のリクエストパススルーが有効です。パラメータ上書き、モデルリダイレクト、チャネル適応などの NewAPI 内蔵機能は無効になります。ベストプラクティスではありません。これにより問題が発生しても issue を投稿しないでください。", + "已忽略模型": "", "已成功开始测试所有已启用通道,请刷新页面查看结果。": "有効なすべてのチャネルのテストを開始しました。ページを更新して結果を確認してください。", "已打开授权页面": "認可ページを開きました", "已打开支付页面": "決済ページを開きました", + "已批量处理上游模型更新:渠道 {{channels}} 个,加入 {{added}} 个,删除 {{removed}} 个,失败 {{fails}} 个": "", "已提交": "送信済み", "已支付金额": "Amount Paid", "已新增 {{count}} 个模型:{{list}}_one": "{{count}}個のモデルが追加されました:{{list}}_one", @@ -1288,6 +1379,7 @@ "已添加 {{count}} 个模板_other": "{{count}}個のテンプレートが追加されました", "已添加到白名单": "ホワイトリストに追加されました", "已清理 {{count}} 个日志文件,释放 {{size}}": "{{count}} 個のログファイルをクリーンアップし、{{size}} を解放しました", + "已清理 {{count}} 个日志文件,释放 {{size}}_other": "", "已清空": "クリア済み", "已清空测试结果": "テスト結果がクリアされました", "已生成授权凭据": "認可資格情報が生成されました", @@ -1306,13 +1398,14 @@ "已达到购买上限": "購入上限に達しました", "已过期": "有効期限切れ", "已运行时间": "Uptime", - "已选择 ${count} 个渠道": "${count}個のチャネルが選択されました", "已选择 {{count}} 个模型_one": "{{count}}個のモデルが選択されました_one", "已选择 {{count}} 个模型_other": "{{count}}個のモデルが選択されました_other", "已选择 {{selected}} / {{total}}": "{{selected}} / {{total}} 件選択済み", + "已选择 ${count} 个渠道": "${count}個のチャネルが選択されました", "已重置为默认配置": "デフォルト設定にリセットされました", "已销毁": "Destroyed", "币种": "通貨", + "帐号信息": "", "常用上下文 Key(用于 context_*)": "一般的なコンテキストキー(context_*用)", "常见问答": "FAQ", "常见问答管理,为用户提供常见问题的答案(最多50个,前端显示最新20条)": "FAQ管理:ユーザー向けのFAQと回答を管理します。(最大50件、フロントエンドには最新20件が表示されます)", @@ -1331,19 +1424,16 @@ "延长容器时长将会产生额外费用,请确认您有足够的账户余额。": "Extending container duration will incur additional charges, please ensure you have sufficient account balance.", "延长操作一旦确认无法撤销,费用将立即扣除。": "Once confirmed, the extension operation cannot be undone, and charges will be deducted immediately.", "延长时长": "Extension Duration", + "延长时长(小时)": "Extension Duration (hours)", "延长时长不能超过720小时(30天)": "Extension duration cannot exceed 720 hours (30 days)", "延长时长失败": "Failed to extend duration", "延长时长至少为1小时": "Extension duration must be at least 1 hour", - "延长时长(小时)": "Extension Duration (hours)", "建立连接时发生错误": "接続時にエラーが発生しました", "建议在生产环境中使用 MySQL 或 PostgreSQL 数据库,或确保 SQLite 数据库文件已映射到宿主机的持久化存储。": "本番環境では MySQL または PostgreSQL データベースを使用するか、SQLite データベースファイルがホストマシンの永続ストレージにマッピングされていることを確認することを推奨します", "开": "On", "开启「默认使用 auto 分组」后,新建令牌和初始令牌都会自动设为 auto。": "「デフォルトでautoグループを使用」を有効にすると、新規トークンと初期トークンが自動的にautoに設定されます。", "开启之后会清除用户提示词中的": "有効にすると、ユーザープロンプトがクリアされます", "开启之后将上游地址替换为服务器地址": "有効にすると、アップストリームアドレスがサーバーURLに置換されます", - "开启后不限制:必须设置模型倍率": "有効化後は制限なし:モデル倍率の設定が必須", - "开启后创建令牌默认选择auto分组,初始令牌也将设为auto": "有効にするとトークン作成時にデフォルトでautoグループが選択され、初期トークンもautoに設定されます", - "开启后未登录用户无法访问模型广场": "有効にすると、ログインしていないユーザーはモデルマーケットプレイスにアクセスできなくなります", "开启后,using_group 会参与 cache key(不同分组隔离)。": "有効にすると、using_groupがキャッシュキーに含まれます(グループごとに隔離)。", "开启后,仅\"消费\"和\"错误\"日志将记录您的客户端IP地址": "有効にすると、「消費」と「エラー」のログにのみ、クライアントIPアドレスが記録されます", "开启后,对免费模型(倍率为0,或者价格为0)的模型也会预消耗额度": "After enabling, free models (ratio 0 or price 0) will also pre-consume quota", @@ -1355,14 +1445,21 @@ "开启后,规则名称会参与 cache key(不同规则隔离)。": "有効にすると、ルール名がキャッシュキーに含まれます(ルールごとに隔離)。", "开启后,该渠道请求 Claude 时将强制追加 ?beta=true(无需客户端手动传参)": "有効にすると、このチャネルでClaudeにリクエストする際に?beta=trueが強制追加されます(クライアント側で手動パラメータ渡し不要)", "开启后,违规请求将额外扣费。": "有効にすると、違反リクエストには追加料金が発生します。", + "开启后不限制:必须设置模型倍率": "有効化後は制限なし:モデル倍率の設定が必須", + "开启后创建令牌默认选择auto分组,初始令牌也将设为auto": "有効にするとトークン作成時にデフォルトでautoグループが選択され、初期トークンもautoに設定されます", + "开启后未登录用户无法访问模型广场": "有効にすると、ログインしていないユーザーはモデルマーケットプレイスにアクセスできなくなります", + "开启后检测到新增模型会自动加入当前渠道模型列表": "", + "开启后由后端定时任务检测该渠道上游模型变化": "", "开启批量操作": "一括操作を有効にする", "开始": "開始", "开始同步": "同期開始", "开始批量测试 ${count} 个模型,已清空上次结果...": "${count}個のモデルの一括テストを開始します。前回の結果はリセットされました…", "开始时间": "開始時間", + "异常": "", "异步任务退款": "非同期タスク返金", "张图片": "枚の画像", "弱变换": "バリエーション(弱)", + "强制使用 AUTH LOGIN": "AUTH LOGINを強制する", "强制将响应格式化为 OpenAI 标准格式(只适用于OpenAI渠道类型)": "レスポンスをOpenAI標準形式に強制フォーマットします(OpenAIタイプのチャネルのみ対応)。", "强制格式化": "強制フォーマット", "强制要求": "必須", @@ -1400,6 +1497,7 @@ "当前设置类型: ": "現在の設定タイプ:", "当前跟随系统": "システム設定に準拠", "当前配置无法连接到 io.net。": "Unable to connect to io.net with current configuration.", + "当前额度": "現在の残高", "当某个分组的用户使用另一个分组的令牌时,可设置特殊倍率覆盖基础倍率。例如:vip 分组的用户使用 default 分组时倍率为 0.5": "あるグループのユーザーが別のグループのトークンを使用する際、特殊レートで基本レートを上書きできます。例:vipユーザーがdefaultグループを使用する際のレートを0.5に", "当模型没有设置价格时仍接受调用,仅当您信任该网站时使用,可能会产生高额费用": "モデルに料金が設定されていない場合でもAPIコールを受け付けます。信頼できるウェブサイトの場合にのみ使用してください。高額な料金が発生する可能性があります", "当运行通道全部测试时,超过此时间将自动禁用通道": "すべてのチャネルテストの実行時、この時間を超えたチャネルは自動的に無効になります", @@ -1479,10 +1577,13 @@ "批量删除失败": "一括削除に失敗しました", "批量删除成功": "Batch deletion successful", "批量删除模型": "モデルの一括削除", + "批量处理失败": "", "批量应用当前模型价格": "現在のモデル価格を一括適用", "批量操作": "一括操作", "批量操作失败": "Batch operation failed", "批量操作完成: {{success}}个成功, {{failed}}个失败": "Batch operation completed: {{success}} succeeded, {{failed}} failed", + "批量检测失败": "", + "批量检测完成:渠道 {{channels}} 个,新增 {{add}} 个,删除 {{remove}} 个,失败 {{fails}} 个": "", "批量测试${count}个模型": "${count}個のモデルを一括テスト", "批量测试完成!成功: ${success}, 失败: ${fail}, 总计: ${total}": "一括テストが完了しました!成功:${success}、失敗:${fail}、総計:${total}", "批量测试已停止": "一括テストは停止されました", @@ -1504,28 +1605,29 @@ "按倍率设置": "倍率設定", "按次": "リクエストごと", "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "リクエストごと {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "按次计费": "リクエストごとの課金", "按次:{{symbol}}{{price}}": "リクエストごと:{{symbol}}{{price}}", "按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "リクエストごと:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}", + "按次计费": "リクエストごとの課金", "按照如下格式输入:AccessKey|SecretAccessKey|Region": "Enter in the format: AccessKey|SecretAccessKey|Region", "按量计费": "従量課金", "按量计费下需要先填写输入价格,才能保存其它价格项。": "従量課金では、他の価格項目を保存する前に入力価格を設定する必要があります。", "按顺序替换content中的变量占位符": "content内の変数プレースホルダーを順番に置換します", "换脸": "フェイススワップ", - "授权失败": "認可に失敗しました", "授权,需在遵守": "の条件に基づき、", + "授权失败": "認可に失敗しました", "排序": "並び順", "排队中": "待機中", "接受未设置价格模型": "料金未設定モデルを許可", "接口凭证": "API認証情報", "接口密钥已过期": "API key has expired", + "接收上游模型更新通知": "", "控制台": "コンソール", "控制台区域": "コンソールエリア", "控制输出的随机性和创造性": "出力のランダム性と創造性を制御", "控制顶栏模块显示状态,全局生效": "トップバーモジュールの表示ステータスをグローバルに制御します", "推荐": "おすすめ", - "推荐使用(用户可选)": "(オプション)推奨", "推荐:用户可以选择是否使用指纹等验证": "推奨:ユーザーは指紋認証などの利用を選択できます", + "推荐使用(用户可选)": "(オプション)推奨", "描述": "説明", "提交": "送信", "提交时间": "送信日時", @@ -1535,14 +1637,14 @@ "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "Prompt {{input}} tokens / 1M tokens * {{symbol}}{{price}}", "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "プロンプト {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 補完 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "プロンプト {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + キャッシュ {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + キャッシュ作成 {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + 補完 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "提示价格:{{symbol}}{{price}} / 1M tokens": "プロンプト料金:{{symbol}}{{price}} / 1M tokens", - "提示缓存倍率": "プロンプトキャッシュ倍率", "提示:如需备份数据,只需复制上述目录即可": "ヒント:データをバックアップする際は、上記のディレクトリをコピーしてください", "提示:此处配置仅用于控制「模型广场」对用户的展示效果,不会影响模型的实际调用与路由。若需配置真实调用行为,请前往「渠道管理」进行设置。": "注意: ここでの設定は「モデル広場」での表示にのみ影響し、実際の呼び出しやルーティングには影響しません。実際の呼び出しを設定する場合は、「チャネル管理」で設定してください。", "提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。": "注意: エンドポイントマッピングは「モデル広場」での表示専用で、実際の呼び出しには影響しません。実際の呼び出し設定は「チャネル管理」で行ってください。", "提示:该功能为测试版,未来配置结构与功能行为可能发生变更,请勿在生产环境使用。": "注意: この機能はベータ版です。今後、設定構造や挙動が変更される可能性があります。本番環境では使用しないでください。", "提示:语言偏好会同步到您登录的所有设备,并影响API返回的错误消息语言。": "ヒント:言語設定はログインしているすべてのデバイスに同期され、APIが返すエラーメッセージの言語に影響します。", "提示:链接中的{key}将被替换为API密钥,{address}将被替换为服务器地址": "ヒント:リンク内の{key}はAPIキーに、{address}はサーバーURLに置換されます", + "提示价格:{{symbol}}{{price}} / 1M tokens": "プロンプト料金:{{symbol}}{{price}} / 1M tokens", + "提示缓存倍率": "プロンプトキャッシュ倍率", "搜索供应商": "プロバイダーで検索", "搜索关键字": "検索キーワード", "搜索失败": "Search failed", @@ -1556,6 +1658,7 @@ "搜索模型失败": "モデルの検索に失敗しました", "搜索渠道名称或地址": "チャネル名またはベースURLで検索", "搜索聊天应用名称": "チャットアプリ名で検索", + "搜索规则(描述 / 类型 / 路径 / 来源 / 目标)": "", "搜索规则(类型 / 路径 / 来源 / 目标)": "ルールを検索(タイプ/パス/ソース/ターゲット)", "搜索部署名称": "Search deployment name", "操作": "操作", @@ -1569,13 +1672,16 @@ "支付": "支払う", "支付地址": "決済URL", "支付失败": "支払いに失敗しました", + "支付完成后用户跳转的页面,留空则自动使用 服务器地址 + /console/topup": "", "支付宝": "Alipay", "支付方式": "チャージ方法", "支付方式名称": "決済方法名", + "支付方式名称不能为空": "", "支付方式类型": "決済方法タイプ", "支付渠道": "決済チャネル", "支付设置": "決済", "支付请求失败": "決済リクエストに失敗しました", + "支付返回地址": "", "支付金额": "決済金額", "支持 Ctrl+V 粘贴图片": "Ctrl+V で画像を貼り付け可能", "支持6位TOTP验证码或8位备用码,可到`个人设置-安全设置-两步验证设置`配置或查看。": "6桁のTOTP認証コードまたは8桁のバックアップコードに対応しています。`アカウント設定 - セキュリティ設定 - 2要素認証設定`で設定または確認できます。", @@ -1592,6 +1698,7 @@ "支持拉取 Ollama 官方模型库中的所有模型,拉取过程可能需要几分钟时间": "Supports pulling all models from the Ollama official model library, the pulling process may take a few minutes", "支持搜索用户的 ID、用户名、显示名称和邮箱地址": "ユーザーID、ユーザー名、表示名、メールアドレスで検索", "支持的图像模型": "利用可能な画像モデル", + "支持精确匹配;使用 regex: 开头可按正则匹配。": "", "支持通配符格式,如:example.com, *.api.example.com": "ワイルドカード形式に対応しています(例:example.com, *.api.example.com)", "支持逻辑 and/or 与嵌套 groups;操作符支持 eq/ne/gt/gte/lt/lte/in/not_in/contains/exists": "論理and/orとネストされたグループをサポート。演算子:eq/ne/gt/gte/lt/lte/in/not_in/contains/exists", "收益": "収益", @@ -1599,6 +1706,7 @@ "收起": "折りたたみ", "收起侧边栏": "サイドバー折りたたみ", "收起内容": "コンテンツ折りたたみ", + "收起原生额度输入": "生クォータ入力を非表示", "放大": "アップスケール", "放大编辑": "エディタで開く", "敏感信息不会发送到前端显示": "機密情報はフロントエンドに送信されず、表示されることはありません", @@ -1615,9 +1723,9 @@ "数据看板默认时间粒度": "ダッシュボードのデフォルト時間粒度", "数据管理和日志查看": "データ管理とログ閲覧", "文件上传": "ファイルアップロード", + "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "ファイル検索: {{count}} / 1K * 単価 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "文件搜索价格:{{symbol}}{{price}} / 1K 次": "ファイル検索料金:{{symbol}}{{price}} / 1K 回", "文件搜索调用 {{fileSearchCallCount}} 次": "ファイル検索呼び出し {{fileSearchCallCount}} 回", - "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "ファイル検索: {{count}} / 1K * 単価 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "プロンプト {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 補完 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "テキストプロンプト {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + テキスト補完 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音声プロンプト {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音声補完 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "文字提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "プロンプト {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + キャッシュ {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 補完 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", @@ -1632,8 +1740,11 @@ "新增 Key 来源": "キーソースを追加", "新增供应商": "プロバイダーの追加", "新增失败": "追加に失敗しました", + "新增已选 {{selected}} / {{total}}": "", "新增成功": "正常に追加されました", + "新增支付方式": "", "新增条件": "条件を追加", + "新增模型": "", "新增规则": "ルールを追加", "新增订阅": "サブスクリプションを追加", "新密码": "新しいパスワード", @@ -1644,9 +1755,9 @@ "新建容器部署": "Create Container Deployment", "新建数量": "作成数", "新建组": "新規グループ", - "新格式模板": "新規形式テンプレート", "新格式(支持条件判断与json自定义):": "新規形式(条件判断とカスタムJSONに対応):", "新格式(规则 + 条件)": "新形式(ルール + 条件)", + "新格式模板": "新規形式テンプレート", "新版本": "新しいバージョン", "新用户使用邀请码奖励额度": "招待コードを利用した新規ユーザーへの特典クォータ", "新用户初始额度": "新規ユーザーの初期クォータ", @@ -1683,10 +1794,10 @@ "日志类型": "ログタイプ", "日志设置": "ログ設定", "日志详情": "ログ詳細", - "旧格式必须是 JSON 对象": "レガシー形式はJSONオブジェクトである必要があります", - "旧格式模板": "旧形式テンプレート", "旧格式(JSON 对象)": "レガシー形式(JSONオブジェクト)", "旧格式(直接覆盖):": "旧形式(直接上書き):", + "旧格式必须是 JSON 对象": "レガシー形式はJSONオブジェクトである必要があります", + "旧格式模板": "旧形式テンプレート", "旧的备用码已失效,请保存新的备用码": "古いバックアップコードは無効になりました。新規バックアップコードを保存してください", "早上好": "おはようございます", "时间": "時間", @@ -1699,16 +1810,18 @@ "是否为企业账户": "エンタープライズアカウント", "是否同时重置对话消息?选择\"是\"将清空所有对话记录并恢复默认示例;选择\"否\"将保留当前对话记录。": "チャットメッセージも同時にリセットしますか?「はい」を選択すると、すべてのチャット履歴がクリアされ、デフォルトのサンプルが復元されます。「いいえ」を選択すると、現在のチャット履歴は保持されます。", "是否将该订单标记为成功并为用户入账?": "この注文を成功として処理し、ユーザーにクレジットを付与しますか?", + "是否检测上游模型更新": "", "是否确认充值?": "Confirm the recharge?", + "是否自动同步上游模型更新": "", "是否自动禁用": "自動的に無効にする", "是否要求指纹/面容等生物识别": "生体認証を要求する", "显示倍率": "表示倍率", + "显示最新20条": "最新20件を表示", "显示名称": "表示名", "显示名称字段(可选)": "表示名フィールド(オプション)", "显示完整内容": "すべてのコンテンツを表示", "显示操作项": "操作項目を表示", "显示更多": "もっと見る", - "显示最新20条": "最新20件を表示", "显示第": "表示", "显示设置": "表示設定", "显示调试": "デバッグを表示", @@ -1729,6 +1842,7 @@ "暂无公告": "お知らせはありません", "暂无分组,点击下方按钮添加": "グループがありません。下のボタンをクリックして追加してください。", "暂无匹配模型": "マッチングするモデルはありません", + "暂无参数覆盖记录": "", "暂无可复制 JSON": "コピー可能なJSONがありません", "暂无可复制的版本信息": "No version information to copy", "暂无可展示数据": "表示可能なデータがありません", @@ -1742,6 +1856,7 @@ "暂无已绑定项": "バインド済みの項目はありません", "暂无常见问答": "FAQはありません", "暂无成功模型": "成功したモデルはありません", + "暂无支付方式,点击上方按钮新增": "", "暂无数据": "データなし", "暂无数据,点击下方按钮添加键值对": "データなし。下のボタンをクリックしてキー/値ペアを追加してください", "暂无日志": "No logs", @@ -1770,6 +1885,7 @@ "更新": "更新", "更新 Creem 设置": "Update Creem Settings", "更新 Stripe 设置": "Stripe設定の更新", + "更新 Waffo 设置": "", "更新SSRF防护设置": "SSRF保護設定の更新", "更新Worker设置": "Worker設定の更新", "更新令牌信息": "トークン情報の更新", @@ -1792,18 +1908,7 @@ "更新配置后,容器可能需要重启以应用新的设置。请确保您了解这些更改的影响。": "After updating the configuration, the container may need to restart to apply the new settings. Please ensure you understand the impact of these changes.", "更新配置失败": "Failed to update configuration", "更新预填组": "事前入力グループの更新", - "最低": "最低", - "最低充值美元数量": "最低チャージUSD額", - "最后使用时间": "最終利用日時", - "最后更新": "Last Updated", - "最后请求": "最終リクエスト日時", - "最大GPU数量": "Max Number of GPUs", - "最大可用": "Max Available", - "最大条目数": "最大エントリ数", - "最终抵扣": "最終控除", - "最近一次": "最新", - "最近事件": "Recent Events", - "最高优先级": "最高優先", + "替换": "", "月": "月", "有 Reasoning": "推論あり", "有序字符串数组": "順序付き文字列配列", @@ -1813,6 +1918,7 @@ "有效期设置": "有効期限設定", "服务可用性": "サービスの可用性", "服务商": "Service Provider", + "服务器IP": "サーバーIP", "服务器地址": "サーバーURL", "服务器日志功能未启用(未配置日志目录)": "サーバーログ機能が有効になっていません(ログディレクトリが未設定)", "服务器日志管理": "サーバーログ管理", @@ -1859,8 +1965,8 @@ "本地数据存储": "ローカルストレージ", "本地计费": "Local billing", "本月获得": "今月の獲得", - "本设备内置": "このデバイス", "本设备:手机指纹/面容,外接:USB安全密钥": "内蔵:スマートフォンの指紋/顔認証、外部:USBセキュリティキー", + "本设备内置": "このデバイス", "本项目根据": "本プロジェクトは", "机密环境变量": "Secret Environment Variables", "机密环境变量将被加密存储,适用于存储密码、API密钥等敏感信息。": "Secret environment variables will be stored encrypted, suitable for storing passwords, API keys and other sensitive information.", @@ -1869,19 +1975,21 @@ "权限设置": "権限設定", "条": "件", "条 - 第": "~", + "条,共": "件、合計", "条件取反": "条件を反転", "条件数": "条件数", "条件规则": "条件ルール", "条件项设置": "条件項目設定", "条日志已清理!": "件のログがクリアされました", "条规则": "件のルール", - "条,共": "件、合計", "来源": "ソース", "来源于 IO.NET 部署": "From IO.NET Deployment", "来源端点": "ソースエンドポイント", "来自模型重定向,尚未加入模型列表": "From model redirect, not yet added to the model list", "某些配置更改可能需要几分钟才能生效。": "Some configuration changes may take a few minutes to take effect.", "查看": "詳細", + "查看 Codex 帐号信息与用量": "", + "查看 JSON 示例": "", "查看关联部署": "View Associated Deployment", "查看图片": "画像を表示", "查看密钥": "APIキーの詳細", @@ -1913,10 +2021,13 @@ "格式示例:": "フォーマット例:", "格式错误": "無効な形式", "检查更新": "更新を確認", + "检测全部渠道上游更新": "", "检测到 FluentRead(流畅阅读)": "FluentReadが検出されました", "检测到剪贴板中的连接信息": "クリップボードに接続情報が検出されました", "检测到多个密钥,您可以单独复制每个密钥,或点击复制全部获取完整内容。": "複数のAPIキーが検出されました。各キーを個別にコピーするか、「すべてコピー」をクリックして全内容を取得できます。", "检测到该消息后有AI回复,是否删除后续回复并重新生成?": "このメッセージの後にAIからの返信があります。後続の返信を削除して再生成しますか?", + "检测失败": "", + "检测完成:新增 {{add}} 个,删除 {{remove}} 个": "", "检测必须等待绘图成功才能进行放大等操作": "アップスケールなどの操作を行うには、画像生成が成功するまで待つ必要があります", "概览": "概要", "模型": "モデル", @@ -1932,6 +2043,7 @@ "模型价格:{{symbol}}{{price}}": "モデル価格:{{symbol}}{{price}}", "模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "モデル料金:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}", "模型价格:{{symbol}}{{price}} / 次": "モデル価格:{{symbol}}{{price}} / リクエスト", + "模型价格未配置": "モデル価格が未設定", "模型倍率": "モデル倍率", "模型倍率 {{modelRatio}}": "Model ratio {{modelRatio}}", "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "モデル倍率 {{modelRatio}}、キャッシュ倍率 {{cacheRatio}}、補完倍率 {{completionRatio}}、{{ratioType}} {{ratio}}", @@ -1956,8 +2068,8 @@ "模型名称已存在": "そのモデル名はすでに存在します", "模型固定价格": "モデル固定料金", "模型图标": "モデルアイコン", - "模型定价设置": "モデル料金設定", "模型定价,需要登录访问": "モデル料金(アクセスにはログインが必要です)", + "模型定价设置": "モデル料金設定", "模型广场": "モデルマーケットプレイス", "模型拉取失败: {{error}}": "Failed to pull model: {{error}}", "模型排行": "モデルランキング", @@ -1967,8 +2079,8 @@ "模型更新成功!": "モデルの更新に成功しました!", "模型未加入列表,可能无法调用": "Model not in the list; requests may fail", "模型正则": "モデル正規表現", - "模型正则不能为空": "モデル正規表現は空にできません", "模型正则(每行一个)": "モデル正規表現(1行に1つ)", + "模型正则不能为空": "モデル正規表現は空にできません", "模型消耗分布": "モデル消費分布", "模型消耗趋势": "モデル消費推移", "模型版本": "モデルバージョン", @@ -1999,17 +2111,24 @@ "次": "リクエスト", "欢迎使用,请完成以下设置以开始使用系统": "ようこそ。システムを利用開始するには、以下の設定を完了してください", "欧元": "EUR", + "正则替换": "", "正在加载可用部署位置...": "Loading available deployment locations...", "正在加载签到状态...": "チェックイン状態を読み込み中...", + "正在处理,请稍候": "", "正在处理大内容...": "大容量のコンテンツを処理中...", + "正在批量处理,请稍候": "", + "正在批量检测,请稍候": "", "正在提交": "送信中", "正在构造请求体预览...": "リクエストボディのプレビューを生成中...", "正在检查 io.net 连接...": "Checking io.net connection...", + "正在检测,请稍候": "", "正在测试第 ${current} - ${end} 个模型 (共 ${total} 个)": "モデル ${current} - ${end} 個目をテスト中(合計 ${total} 個)", "正在跟随最新日志": "Following latest logs", "正在跳转 GitHub...": "GitHub にリダイレクトしています...", "正在跳转...": "リダイレクト中...", + "正常": "", "正常情况下,令牌的计费倍率由令牌所选的分组决定。特殊倍率可以根据「用户所在分组」进一步覆盖这个倍率。": "通常、トークンの課金レートは選択したグループで決まります。特殊レートを使えば、ユーザーの所属グループに基づいてこのレートを上書きできます。", + "正数为增加,负数为减少": "正の数で追加、負の数で減少", "此代理仅用于图片请求转发,Webhook通知发送等,AI API请求仍然由服务器直接发出,可在渠道设置中单独配置代理": "このプロキシは、画像リクエストの転送やWebhook通知の送信などにのみ使用されます。AI APIリクエストは引き続きサーバーから直接送信されます。プロキシが必要な場合は、チャネル設定で個別に設定してください", "此修改将不可逆": "この変更は元に戻すことはできません。", "此操作不可恢复,请仔细确认时间后再操作!": "この操作は元に戻すことができません。時刻を慎重にご確認の上、実行してください", @@ -2036,6 +2155,7 @@ "此项可选,用于覆盖请求参数。不支持覆盖 stream 参数": "(オプション)リクエストヘッダーのパラメータを上書きする場合に使用します。stream パラメータの上書きはサポート対象外です。", "此项可选,用于覆盖请求头参数": "(オプション)リクエストヘッダーのパラメータを上書きする場合に使用します", "此项可选,用于通过自定义API地址来进行 API 调用,末尾不要带/v1和/": "(オプション)カスタムベースURLでのAPIコール用。末尾に/v1, /は含めません", + "每个充值单位对应的 USD 金额,默认 1.0": "", "每个分组代表一个价格档位。管理员创建分组后,可以选择哪些档位对用户开放自选。": "各グループは1つの料金プランを表します。管理者がグループを作成後、どのプランをユーザーに公開するか選択できます。", "每个用户最多可创建的令牌数量,默认 1000,设置过大可能会影响性能": "各ユーザーが作成できるトークンの最大数、デフォルト1000。大きすぎるとパフォーマンスに影響する場合があります", "每周": "毎週", @@ -2051,7 +2171,9 @@ "永久删除您的两步验证设置": "2要素認証設定を永久に削除", "永久删除所有备用码(包括未使用的)": "すべてのバックアップコード(未使用分を含む)を永久に削除", "汇率": "為替レート", + "沙盒模式": "", "沙盒环境 RSA 私钥 Base64 (PKCS#8 DER)": "サンドボックス RSA 秘密鍵 Base64 (PKCS#8 DER)", + "沙盒环境 Waffo API 密钥": "", "沙盒环境 Waffo 公钥 Base64 (X.509 DER)": "サンドボックス Waffo 公開鍵 Base64 (X.509 DER)", "没有匹配的字段": "一致するフィールドがありません", "没有匹配的日志条目": "No matching log entries", @@ -2068,8 +2190,8 @@ "注册": "サインアップ", "注册 Passkey": "Passkeyの登録", "注意": "ご注意", - "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "ご注意:Chat API以外の場合、必ず正しいベースURLを入力してください。正しく入力しないと、利用できません", "注意:JSON中重复的键只会保留最后一个同名键的值": "ご注意:JSONでは、キーが重複している場合、最後の同名キーの値のみが保持されます", + "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "ご注意:Chat API以外の場合、必ず正しいベースURLを入力してください。正しく入力しないと、利用できません", "注销": "ログアウト", "注销成功!": "ログアウトしました", "活跃文件": "アクティブファイル", @@ -2078,7 +2200,9 @@ "流式": "ストリーミング", "流式响应完成": "ストリーム完了", "流式输出": "ストリーム出力", + "流状态": "", "流量端口": "Traffic Port", + "流错误详情": "", "浅色": "ライト", "浅色模式": "ライトモード", "测活": "Health Check", @@ -2132,14 +2256,6 @@ "添加键值对": "キー/値ペア追加", "添加问答": "FAQ追加", "添加额度": "残高追加", - "减少": "減少", - "覆盖": "上書き", - "调整额度": "残高調整", - "调整额度成功": "残高の調整に成功しました", - "当前额度": "現在の残高", - "变更": "変更", - "预计结果": "予想結果", - "正数为增加,负数为减少": "正の数で追加、負の数で減少", "清理不活跃缓存": "非アクティブなキャッシュをクリーンアップ", "清理失败": "クリーンアップに失敗しました", "清理方式": "クリーンアップモード", @@ -2148,6 +2264,7 @@ "清空全部缓存": "すべてのキャッシュをクリア", "清空该规则缓存": "このルールのキャッシュをクリア", "清空重定向": "マッピングをクリア", + "清除": "", "清除历史日志": "履歴ログのクリア", "清除失效兑换码": "無効な引き換えコードを削除", "清除所有模型": "すべてのモデルをクリア", @@ -2155,8 +2272,8 @@ "渠道 ID": "チャネルID", "渠道ID,名称,密钥,API地址": "チャネルID\\名称\\キー\\ベースURL", "渠道亲和性": "チャネル親和性", - "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "チャネルアフィニティは、リクエストコンテキストまたはJSON Bodyから抽出されたキーに基づいて、前回成功したチャネルを優先的に再利用します。", "渠道亲和性:上游缓存命中": "チャネルアフィニティ:上流キャッシュヒット", + "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "チャネルアフィニティは、リクエストコンテキストまたはJSON Bodyから抽出されたキーに基づいて、前回成功したチャネルを優先的に再利用します。", "渠道优先级": "チャネル優先度", "渠道信息": "チャネル情報", "渠道创建成功!": "チャネルの作成に成功しました", @@ -2184,6 +2301,8 @@ "点击\"确认延长\"后将立即扣除费用并延长容器运行时间": "After clicking \"Confirm Extension\", the fee will be deducted immediately and the container runtime will be extended", "点击上传文件或拖拽文件到这里": "クリックしてファイルをアップロードするか、ファイルをここにドラッグ&ドロップしてください", "点击下方按钮通过 Telegram 完成绑定": "下のボタンをクリックしてTelegram連携を完了してください", + "点击处理删除模型": "", + "点击处理新增模型": "", "点击复制ID": "Click to copy ID", "点击复制模型名称": "モデル名をコピー", "点击查看差异": "差分を表示", @@ -2202,6 +2321,7 @@ "状态页面Slug": "ステータスページスラッグ", "环境变量": "Environment Variables", "生产环境 RSA 私钥 Base64 (PKCS#8 DER)": "本番環境 RSA 秘密鍵 Base64 (PKCS#8 DER)", + "生产环境 Waffo API 密钥": "", "生产环境 Waffo 公钥 Base64 (X.509 DER)": "本番環境 Waffo 公開鍵 Base64 (X.509 DER)", "生成令牌": "トークン生成", "生成并填入": "生成して入力", @@ -2233,6 +2353,7 @@ "用户信息": "ユーザー情報", "用户信息更新成功!": "ユーザー情報の更新に成功しました", "用户信息缺失": "ユーザー情報がありません", + "用户最大令牌数量": "ユーザーあたりの最大トークン数", "用户分组": "ユーザーグループ", "用户分组和额度管理": "ユーザーグループとクォータの管理", "用户分组的联动作用": "ユーザーグループの連動効果", @@ -2249,9 +2370,9 @@ "用户名字段(可选)": "ユーザー名フィールド(オプション)", "用户名或邮箱": "ユーザー名かメールアドレス", "用户名称": "ユーザー名", + "用户在充值页面看到的支付方式名称,例如:Credit Card": "", "用户控制面板,管理账户": "ユーザーコンソールでアカウントを管理します", "用户新建令牌时可选的分组,格式为 JSON 字符串,例如:{\"vip\": \"VIP 用户\", \"test\": \"测试\"},表示用户可以选择 vip 分组和 test 分组": "ユーザーが新規トークンを作成する際に利用可能なグループです。JSON文字列の形式で入力してください。例:{\"vip\": \"VIPユーザー\", \"test\": \"テスト\"} は、ユーザーがvipグループとtestグループを選択できることを示します。", - "用户最大令牌数量": "ユーザーあたりの最大トークン数", "用户每周期最多请求完成次数": "期間ごとのユーザー最大成功リクエスト数", "用户每周期最多请求次数": "期間ごとのユーザー最大リクエスト数", "用户注册时看到的网站名称,比如'我的网站'": "ユーザーがサインアップ時に表示されるウェブサイト名です。例:「マイサイト」", @@ -2272,6 +2393,7 @@ "留空则使用账号绑定的邮箱": "未入力の場合、アカウントに登録されているメールアドレスが使用されます", "留空则使用默认端点;支持 {path, method}": "未入力の場合、デフォルトのエンドポイントが使用されます。{path, method}に対応しています", "留空则保持原有密钥": "空欄で既存のキーを保持", + "留空则自动使用 服务器地址 + /api/waffo/webhook": "", "留空则默认使用服务器地址,注意不能携带http://或者https://": "未入力の場合、デフォルトのサーバーURLが使用されます。ご注意:http://またはhttps://は含めないでください", "登 录": "ログイン", "登录": "ログイン", @@ -2298,6 +2420,7 @@ "硬件类型": "Hardware Type", "硬件配置": "Hardware Configuration", "确定": "確認", + "确定?": "確認", "确定删除此组?": "このグループを削除してもよろしいですか?", "确定导入": "インポート", "确定是否要修复数据库一致性?": "データベースの整合性を修復してもよろしいですか?", @@ -2310,6 +2433,7 @@ "确定是否要复制此渠道?": "このチャネルをコピーしてもよろしいですか?", "确定是否要注销此用户?": "このユーザーを削除してもよろしいですか?", "确定清除所有失效兑换码?": "すべての無効な引き換えコードを削除してもよろしいですか?", + "确定要仅检测全部渠道上游模型更新吗?(不执行新增/删除)": "", "确定要修改所有子渠道优先级为 ": "すべてのサブチャネルの優先度を", "确定要修改所有子渠道权重为 ": "すべてのサブチャネルのウェイトを", "确定要充值 $": "Confirm to recharge $", @@ -2329,6 +2453,7 @@ "确定要删除选中的": "Are you sure you want to delete the selected", "确定要启用所有密钥吗?": "すべてのAPIキーを有効にしてもよろしいですか?", "确定要启用此用户吗?": "このユーザーを有効にしてもよろしいですか?", + "确定要对全部渠道执行上游模型更新吗?": "", "确定要提升此用户吗?": "このユーザーを昇格させてもよろしいですか?", "确定要更新所有已启用通道余额吗?": "有効なすべてのチャネルのクォータを更新してもよろしいですか?", "确定要测试所有未手动禁用渠道吗?": "手動で無効化されたチャネルを除くすべてのチャネルをテストしてもよろしいですか?", @@ -2339,15 +2464,14 @@ "确定要降级此用户吗?": "このユーザーを降格させてもよろしいですか?", "确定重置": "リセットの確認", "确定重置模型倍率吗?": "モデル倍率をリセットしますか?", - "确定?": "確認", "确认": "確認", "确认作废": "無効化の確認", "确认关闭提示": "閉じる確認", "确认冲突项修改": "競合項目の変更の確認", "确认删除": "削除の確認", "确认删除模型": "Confirm Delete Model", - "确认删除该分组的所有规则?": "このグループの全ルールを削除しますか?", "确认删除该分组?": "このグループを削除しますか?", + "确认删除该分组的所有规则?": "このグループの全ルールを削除しますか?", "确认删除该规则?": "このルールを削除しますか?", "确认取消密码登录": "パスワードログイン無効化の確認", "确认启用": "有効化を確認", @@ -2375,13 +2499,16 @@ "磁盘使用率超过此值时拒绝请求": "ディスク使用率がこの値を超えた場合にリクエストを拒否", "磁盘可用空间小于缓存最大总量设置": "ディスクの空き容量がキャッシュの最大合計サイズ設定より少ないです", "磁盘命中": "ディスクヒット", + "磁盘缓存最大总量 (MB)": "ディスクキャッシュ最大合計 (MB)", "磁盘缓存占用的最大空间": "ディスクキャッシュが占める最大容量", "磁盘缓存已清理": "ディスクキャッシュがクリアされました", - "磁盘缓存最大总量 (MB)": "ディスクキャッシュ最大合計 (MB)", "磁盘缓存设置(磁盘换内存)": "ディスクキャッシュ設定(ディスク/メモリ交換)", "磁盘缓存阈值 (MB)": "ディスクキャッシュ閾値 (MB)", "示例": "サンプル", "示例:{\"default\": [200, 100], \"vip\": [0, 1000]}。": "例:{\"default\": [200, 100], \"vip\": [0, 1000]}。", + "视频": "動画", + "视频Remix": "動画リミックス", + "视频无法在当前浏览器中播放,这可能是由于:": "The video cannot be played in this browser, possibly because:", "禁用": "無効にする", "禁用 store 透传": "ストアパススルーを無効にする", "禁用2FA失败": "2要素認証の無効化に失敗しました", @@ -2399,10 +2526,12 @@ "私有镜像仓库的密码": "Password for private image registry", "私有镜像仓库的用户名": "Username for private image registry", "秒": "秒", + "移动": "", + "移动请求头": "", "移除": "削除", "移除 (-:)": "削除 (-:)", - "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "One APIの著作権表示を削除するには、事前の許可が必要です。プロジェクトの維持には多大な労力がかかります。もしこのプロジェクトがあなたにとって有意義でしたら、積極的なご支援をお願いいたします", "移除 functionResponse.id 字段": "functionResponse.idフィールドを削除", + "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "One APIの著作権表示を削除するには、事前の許可が必要です。プロジェクトの維持には多大な労力がかかります。もしこのプロジェクトがあなたにとって有意義でしたら、積極的なご支援をお願いいたします", "空": "空", "窗口处理": "ウィンドウ処理", "窗口等待": "ウィンドウ待機中", @@ -2435,19 +2564,19 @@ "等待中": "待機中", "等待获取邮箱信息...": "メールアドレス情報を取得中...", "筛选": "フィルター", + "签到最大额度": "チェックイン最大クォータ", + "签到最小额度": "チェックイン最小クォータ", "签到功能允许用户每日签到获取随机额度奖励": "チェックイン機能により、ユーザーは毎日チェックインしてランダムなクォータ報酬を獲得できます", "签到失败": "チェックインに失敗しました", "签到奖励将直接添加到您的账户余额": "チェックイン報酬は直接アカウント残高に追加されます", "签到奖励的最大额度": "チェックイン報酬の最大クォータ", "签到奖励的最小额度": "チェックイン報酬の最小クォータ", "签到成功!获得": "チェックイン成功!獲得", - "签到最大额度": "チェックイン最大クォータ", - "签到最小额度": "チェックイン最小クォータ", "签到设置": "チェックイン設定", "简单来说:同一个令牌分组,不同等级的用户可以享受不同的价格。": "簡単に言えば:同じトークングループでも、異なる等級のユーザーが異なる価格を享受できます。", "简洁": "シンプル", - "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。": "シンプルモードはメッセージのみを返します。ステータスコードとエラータイプはシステムのデフォルト値を使用します。", "简洁模式:按 type 全量清理对象,例如 redacted_thinking。": "シンプルモード:typeごとにすべてのオブジェクトをプルーニングします(例:redacted_thinking)。", + "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。": "シンプルモードはメッセージのみを返します。ステータスコードとエラータイプはシステムのデフォルト値を使用します。", "管理": "管理", "管理 Ollama 模型的拉取和删除": "Manage Ollama model pulling and deletion", "管理你的 LinuxDO OAuth App": "LinuxDO OAuth Appの管理", @@ -2458,8 +2587,8 @@ "管理员暂时未设置任何关于内容": "管理者はまだ「このサービスについて」のコンテンツを設定していません", "管理员未开启 Creem 充值!": "The administrator has not enabled Creem recharge!", "管理员未开启Stripe充值!": "管理者がStripeチャージを有効にしていません", - "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "管理者がオンラインチャージ機能を有効にしていません。管理者にお問い合わせいただくか、引き換えコードでチャージしてください。", "管理员未开启在线充值!": "管理者がオンラインチャージを有効にしていません", + "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "管理者がオンラインチャージ機能を有効にしていません。管理者にお問い合わせいただくか、引き換えコードでチャージしてください。", "管理员未开启在线支付功能,请联系管理员配置。": "管理者がオンライン決済を有効にしていません。管理者に連絡してください。", "管理员未设置用户可选分组": "管理者がユーザー利用可能なグループを設定していません", "管理员给用户分配的分组(如 vip)不仅决定用户身份,还会影响后续两个功能:": "ユーザーに割り当てるグループ(例:vip)はユーザーの身分を決定するだけでなく、以下の2つの機能にも影響します:", @@ -2499,14 +2628,16 @@ "系统数据统计": "システムデータ統計", "系统文档和帮助信息": "システムのドキュメントとヘルプ", "系统消息": "システムメッセージ", + "系统版本": "システムバージョン", "系统管理功能": "システム管理機能", "系统设置": "システム", "系统访问令牌": "システムアクセストークン", + "约": "約", "索引": "インデックス", "紧凑列表": "コンパクトリスト", + "纯字符串会直接覆盖整条请求头,或者点击“查看 JSON 示例”按 token 规则处理。": "", "累计签到": "累計チェックイン", "累计获得": "累計獲得", - "约": "約", "线路描述": "チャネルの説明", "组列表": "グループリスト", "组名": "グループ名", @@ -2536,44 +2667,45 @@ "统计次数": "リクエスト数統計", "统计额度": "クォータ統計", "继续": "次へ", - "缓存 Tokens": "キャッシュトークン", "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "キャッシュ {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Cache {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio: {{ratio}})", + "缓存 Tokens": "キャッシュトークン", "缓存: {{cacheRatio}}": "キャッシュ:{{cacheRatio}}", "缓存价格:{{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "キャッシュ料金:{{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M tokens(キャッシュ倍率:{{cacheRatio}})", "缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "キャッシュ料金:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens(キャッシュ倍率:{{cacheRatio}})", "缓存倍率": "キャッシュ倍率", "缓存倍率 {{cacheRatio}}": "Cache ratio {{cacheRatio}}", "缓存写": "キャッシュ書込", - "缓存创建 Tokens": "キャッシュ作成トークン", "缓存创建 {{price}} / 1M tokens": "キャッシュ作成 {{price}} / 1M tokens", "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "キャッシュ作成 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio: {{ratio}})", + "缓存创建 Tokens": "キャッシュ作成トークン", + "缓存创建: {{cacheCreationRatio}}": "キャッシュ作成:{{cacheCreationRatio}}", "缓存创建: 1h {{cacheCreationRatio1h}}": "キャッシュ作成:1h {{cacheCreationRatio1h}}", "缓存创建: 5m {{cacheCreationRatio5m}}": "キャッシュ作成:5m {{cacheCreationRatio5m}}", "缓存创建: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Cache creation: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存创建: {{cacheCreationRatio}}": "キャッシュ作成:{{cacheCreationRatio}}", + "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "キャッシュ作成: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * キャッシュ作成倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存创建价格": "入力キャッシュ作成価格", "缓存创建价格 {{symbol}}{{price}} / 1M tokens": "キャッシュ作成価格 {{symbol}}{{price}} / 1M tokens", - "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "Cache creation price total: 5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "キャッシュ作成料金:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1Mtokens(キャッシュ作成倍率:{{cacheCreationRatio}})", "缓存创建价格:{{symbol}}{{price}} / 1M tokens": "キャッシュ作成価格:{{symbol}}{{price}} / 1M tokens", + "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "Cache creation price total: 5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens", "缓存创建倍率": "キャッシュ作成倍率", + "缓存创建倍率 {{cacheCreationRatio}}": "Cache creation ratio {{cacheCreationRatio}}", "缓存创建倍率 1h {{cacheCreationRatio1h}}": "キャッシュ作成倍率 1h {{cacheCreationRatio1h}}", "缓存创建倍率 5m {{cacheCreationRatio5m}}": "キャッシュ作成倍率 5m {{cacheCreationRatio5m}}", "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "キャッシュ作成倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存创建倍率 {{cacheCreationRatio}}": "Cache creation ratio {{cacheCreationRatio}}", - "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "キャッシュ作成: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * キャッシュ作成倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存条目数": "キャッシュエントリ数", "缓存目录": "キャッシュディレクトリ", "缓存目录磁盘空间": "キャッシュディレクトリのディスク容量", "缓存读": "キャッシュ読取", "缓存读 {{price}} / 1M tokens": "キャッシュ読み取り {{price}} / 1M tokens", + "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "キャッシュ読み取り: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * キャッシュ倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存读取价格": "入力キャッシュ読み取り価格", "缓存读取价格 {{symbol}}{{price}} / 1M tokens": "キャッシュ読み取り価格 {{symbol}}{{price}} / 1M tokens", "缓存读取价格:{{symbol}}{{price}} / 1M tokens": "キャッシュ読み取り価格:{{symbol}}{{price}} / 1M tokens", "缓存读取价格:{{symbol}}{{total}} / 1M tokens": "キャッシュ読み取り価格:{{symbol}}{{total}} / 1M tokens", - "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "キャッシュ読み取り: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * キャッシュ倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "缓存读取倍率": "", "缓存输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "キャッシュ入力: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * キャッシュ倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "编辑": "編集", "编辑 OAuth 提供商": "OAuthプロバイダーを編集", @@ -2584,6 +2716,7 @@ "编辑公告内容": "お知らせ内容編集", "编辑分类": "分類編集", "编辑成功": "編集に成功しました", + "编辑支付方式": "", "编辑方式": "編集モード", "编辑标签": "タグ編集", "编辑模型": "モデル編集", @@ -2653,11 +2786,12 @@ "若你的 OIDC Provider 支持 Discovery Endpoint,你可以仅填写 OIDC Well-Known URL,系统会自动获取 OIDC 配置": "お使いのOIDCプロバイダーがディスカバリーエンドポイントに対応している場合、OIDC Well-Known URLを入力するだけで、システムが自動的にOIDC設定を取得します。", "获取 Discovery 配置": "Discovery設定を取得", "获取 Discovery 配置失败:": "Discovery設定の取得に失敗しました:", + "获取 io.net API Key": "Get io.net API Key", "获取 OIDC 配置失败,请检查网络状况和 Well-Known URL 是否正确": "OIDC設定の取得に失敗しました。ネットワーク状況とWell-Known URLが正しいかご確認ください", "获取 OIDC 配置成功!": "OIDC設定の取得に成功しました", "获取 Ollama 版本失败": "Failed to get Ollama version", - "获取 io.net API Key": "Get io.net API Key", "获取2FA状态失败": "2FAステータスの取得に失敗しました", + "获取令牌密钥失败": "", "获取充值配置失败": "チャージ設定の取得に失敗しました", "获取充值配置异常": "チャージ設定エラー", "获取初始化状态失败": "初期化ステータスの取得に失敗しました", @@ -2689,9 +2823,9 @@ "补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "補完 {{completion}} tokens * 出力倍率 {{completionRatio}}", "补全 {{completion}} tokens / 1M tokens * {{symbol}}{{price}}": "Completion {{completion}} tokens / 1M tokens * {{symbol}}{{price}}", "补全价格": "補完価格", - "补全价格已锁定": "補完価格はロックされています", "补全价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "補完料金:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens(補完倍率:{{completionRatio}})", "补全价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens": "補完料金:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens", + "补全价格已锁定": "補完価格はロックされています", "补全倍率": "補完倍率", "补全倍率 {{completionRatio}}": "補完倍率 {{completionRatio}}", "补全倍率值": "補完倍率", @@ -2700,6 +2834,7 @@ "补单成功": "手動チャージに成功しました", "表单引用错误,请刷新页面重试": "フォームの参照でエラーが発生しました。ページを更新して再試行してください", "表格视图": "テーブルビュー", + "覆盖": "上書き", "覆盖模式:将完全替换现有的所有密钥": "上書きモード:既存のすべてのAPIキーを完全に置き換えます", "覆盖模板": "オーバーライドテンプレート", "覆盖现有密钥": "既存のAPIキーを上書き", @@ -2710,10 +2845,8 @@ "规则为 JSON 数组;可视化与 JSON 模式共用同一份数据。": "ルールはJSON配列です。ビジュアルモードとJSONモードは同じデータを共有します。", "规则名称(可读性更好,也会出现在管理侧日志中)。": "ルール名(読みやすさ向上のため、管理側ログにも表示されます)。", "规则导航": "ルールナビゲーション", + "规则描述(可选)": "", "规则未找到,请刷新后重试": "ルールが見つかりません。更新してから再試行してください", - "视频": "動画", - "视频Remix": "動画リミックス", - "视频无法在当前浏览器中播放,这可能是由于:": "The video cannot be played in this browser, possibly because:", "角色": "ロール", "解析响应数据时发生错误": "レスポンスデータの解析時にエラーが発生しました", "解析密钥文件失败: {{msg}}": "APIキーファイルの解析に失敗しました:{{msg}}", @@ -2722,21 +2855,20 @@ "解绑 Passkey": "Passkey連携解除", "解绑后将无法使用 Passkey 登录,确定要继续吗?": "連携解除後は、Passkeyでログインできなくなります。連携を解除してもよろしいですか?", "解绑成功": "バインド解除に成功しました", - "警告": "警告", - "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔": "警告:キープアライブを有効にした後、データ書き込み後にチャネルエラーが発生した場合、システムは再試行できません。有効化が必須の場合は、Ping間隔を可能な限り長く設定することを推奨します", - "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "警告:2要素認証を無効にすると、認証設定とすべてのバックアップコードが永久に削除されます。この操作は元に戻すことができません", "计价币种": "Pricing Currency", "计算中": "Calculating", "计算成本": "Calculate Cost", "计算费用中...": "Calculating fees...", "计费乘数,倍率越低费用越低。例如倍率 0.5 表示半价。": "課金倍率。レートが低いほど費用が低くなります。例:レート0.5は半額を意味します。", "计费开始": "Billing Start", + "计费摘要": "", "计费方式": "課金方式", "计费显示模式": "課金表示モード", "计费模式": "Billing mode", "计费类型": "課金タイプ", "计费过程": "課金プロセス", "订单号": "注文番号", + "订单支付方式": "注文支払い方法", "订阅": "サブスクリプション", "订阅剩余": "サブスクリプション残り", "订阅套餐": "サブスクリプションプラン", @@ -2751,6 +2883,7 @@ "记录请求与错误日志IP": "リクエストログとエラーログのIP記録", "设备": "Device", "设备类型偏好": "優先デバイスタイプ", + "设置": "", "设置 Logo": "ロゴを設定", "设置2FA失败": "2要素認証の設定に失敗しました", "设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}": "チャージ額に応じた割引を設定します。キー:チャージ額、値:割引率。例:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}", @@ -2780,6 +2913,7 @@ "设置默认地区和特定模型的专用地区": "デフォルトリージョンと特定モデル専用のリージョンを設定します", "设计与开发由": "開発元:", "设计版本": "b80c3466cb6feafeb3990c7820e10e50", + "设请求头": "", "访问 io.net 控制台的 API Keys 页面": "Visit the API Keys page of the io.net console", "访问容器": "Access Container", "访问模型部署功能需要先启用 io.net 部署服务": "Accessing model deployment features requires enabling the io.net deployment service first", @@ -2795,6 +2929,8 @@ "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "このモデルの補完倍率はバックエンドで {{ratio}} に固定されています。ここでは補完価格を変更できません。", "该渠道已开启请求透传,参数覆写、模型重定向等 NewAPI 内置功能将失效,非最佳实践。": "このチャネルではリクエストのパススルーが有効です。パラメータ上書きやモデルリダイレクトなどの NewAPI 内蔵機能は無効になります。ベストプラクティスではありません。", "该渠道已开启请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "このチャネルではリクエストのパススルーが有効です。パラメータ上書き、モデルリダイレクト、チャネル適応などの NewAPI 内蔵機能は無効になります。ベストプラクティスではありません。これにより問題が発生しても issue を投稿しないでください。", + "该渠道暂无可处理的上游模型更新": "", + "该渠道未开启上游模型更新检测": "", "该规则未启用“作用域:包含规则名称”,无法按规则清空缓存。": "このルールは「スコープ:ルール名を含む」が有効になっていないため、ルールごとのキャッシュクリアができません。", "该规则未设置参数覆盖模板": "このルールにはパラメータオーバーライドテンプレートが設定されていません", "该规则的缓存保留时长;0 表示使用默认 TTL:": "このルールのキャッシュ保持期間。0はデフォルトTTLを使用:", @@ -2806,10 +2942,10 @@ "语音输入": "音声入力", "语音输出": "音声出力", "说明": "説明", - "说明信息": "説明", "说明:": "説明:", "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。": "注意: このページのテストは非ストリーミングリクエストです。チャネルがストリーミング応答のみ対応の場合、テストが失敗することがあります。実際の利用結果を優先してください。", "说明:生成结果是可直接粘贴到渠道密钥里的 JSON(包含 access_token / refresh_token / account_id)。": "注:生成結果はチャネルキーに直接貼り付けられるJSON(access_token / refresh_token / account_idを含む)です。", + "说明信息": "説明", "请上传密钥文件": "APIキーファイルをアップロードしてください", "请上传密钥文件!": "APIキーファイルをアップロードしてください", "请为渠道命名": "チャネル名を入力してください", @@ -2822,6 +2958,7 @@ "请先填写 Ollama API 地址": "Please fill in Ollama API address first", "请先填写服务器地址": "まずサーバーURLを入力してください", "请先开启并填写音频输入价格。": "先に音声入力価格を有効にして入力してください。", + "请先新增模型或从左侧列表选择一个模型": "", "请先粘贴回调 URL": "まずコールバックURLを貼り付けてください", "请先输入密钥": "まずAPIキーを入力してください", "请先选择一个作为模板的模型": "まずテンプレートとして使うモデルを選択してください", @@ -2837,6 +2974,7 @@ "请再次输入新密码": "新しいパスワードを再入力してください", "请前往个人设置 → 安全设置进行配置。": "アカウント設定 → セキュリティ設定 にて設定してください。", "请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "IPは偽装される可能性があるため、この機能を過信しないでください。nginxやCDNなどのゲートウェイと組み合わせて使用してください。", + "请在 Waffo 后台获取 API 密钥、商户 ID 以及 RSA 密钥对,并配置回调地址。": "", "请在系统设置页面编辑分组倍率以添加新的分组:": "新規グループを追加するには、システム設定ページでグループ倍率を編集してください:", "请填写完整的产品信息": "Please fill in complete product information", "请填写完整的管理员账号信息": "管理者アカウント情報をすべて入力してください", @@ -2855,7 +2993,6 @@ "请求参数无效": "Invalid request parameters", "请求发生错误": "リクエストでエラーが発生しました", "请求发生错误: ": "リクエストでエラーが発生しました:", - "模型价格未配置": "モデル価格が未設定", "请求后端接口失败:": "バックエンドAPIリクエストに失敗しました:", "请求失败": "リクエストに失敗しました", "请求头覆盖": "リクエストヘッダーの上書き", @@ -2886,18 +3023,18 @@ "请至少选择一个渠道": "チャネルを少なくとも1つ選択してください", "请输入 API Key,一行一个,格式:APIKey|Region": "Enter API Key, one per line, format: APIKey|Region", "请输入 API Key,格式:APIKey|Region": "Enter API Key, format: APIKey|Region", - "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com": "AZURE_OPENAI_ENDPOINTを入力してください(例:https://docs-test-001.openai.azure.com)", "请输入 Authorization Endpoint": "Authorization Endpointを入力してください", + "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com": "AZURE_OPENAI_ENDPOINTを入力してください(例:https://docs-test-001.openai.azure.com)", "请输入 Client ID": "Client IDを入力してください", "请输入 Client Secret": "Client Secretを入力してください", + "请输入 io.net API Key": "Please enter io.net API Key", + "请输入 io.net API Key(敏感信息不显示)": "Please enter io.net API Key (sensitive information not displayed)", "请输入 JSON 格式的 OAuth 凭据,例如:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}": "JSON形式のOAuth資格情報を入力してください。例:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}", "请输入 JSON 格式的密钥内容,例如:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}": "JSON形式のAPIキーを入力してください。例:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}", "请输入 OIDC 的 Well-Known URL": "OIDCのWell-Known URLを入力してください", "请输入 Slug": "Slugを入力してください", "请输入 Token Endpoint": "Token Endpointを入力してください", "请输入 User Info Endpoint": "User Info Endpointを入力してください", - "请输入 io.net API Key": "Please enter io.net API Key", - "请输入 io.net API Key(敏感信息不显示)": "Please enter io.net API Key (sensitive information not displayed)", "请输入6位验证码或8位备用码": "6桁の認証コードまたは8桁のバックアップコードを入力してください", "请输入API地址": "ベースURLを入力してください", "请输入API地址!": "ベースURLを入力してください!", @@ -2907,9 +3044,9 @@ "请输入Gotify服务器地址": "GotifyサーバーURLを入力してください", "请输入Gotify服务器地址,例如: https://gotify.example.com": "GotifyサーバーURLを入力してください(例: https://gotify.example.com)", "请输入JSON数组,如 [\"model-a\",\"model-b\"]": "JSON配列を入力してください(例:[\"model-a\",\"model-b\"])", - "请输入URL链接": "URLを入力してください", "请输入Uptime Kuma地址": "Uptime Kumaアドレスを入力してください", "请输入Uptime Kuma服务地址,如:https://status.example.com": "Uptime Kumaサービスアドレスを入力してください(例: https://status.example.com)", + "请输入URL链接": "URLを入力してください", "请输入Webhook地址": "Webhook URLを入力してください", "请输入Webhook地址,例如: https://example.com/webhook": "Webhook URLを入力してください(例:https://example.com/webhook)", "请输入你的账户名以确认删除!": "削除を確認するには、アカウント名を入力してください", @@ -2934,14 +3071,14 @@ "请输入备注(仅管理员可见)": "備考を入力してください(管理者のみ閲覧可能です)", "请输入套餐标题": "プラン名を入力してください", "请输入完整的 JSON 格式密钥内容": "完全なJSON形式のAPIキーを入力してください", - "请输入完整的URL链接": "完全なURLを入力してください", "请输入完整的URL,例如:https://api.openai.com/v1/chat/completions": "完全なURLを入力してください(例:https://api.openai.com/v1/chat/completions)", + "请输入完整的URL链接": "完全なURLを入力してください", "请输入容器名称": "Please enter container name", "请输入密码": "パスワードを入力してください", "请输入密钥": "APIキーを入力してください", - "请输入密钥!": "APIキーを入力してください", "请输入密钥,一行一个": "APIキーを入力してください(1行に1つずつ)", "请输入密钥,一行一个,格式:AccessKey|SecretAccessKey|Region": "Enter keys one per line, format: AccessKey|SecretAccessKey|Region", + "请输入密钥!": "APIキーを入力してください", "请输入延长时长": "Please enter extension duration", "请输入总额度": "総クォータを入力してください", "请输入您的密码": "パスワードを入力してください", @@ -2994,9 +3131,9 @@ "请输入认证器验证码或备用码": "オーセンティケーターの認証コードまたはバックアップコードを入力してください", "请输入说明": "説明を入力してください", "请输入运行时长": "Please enter runtime duration", + "请输入邮箱!": "メールアドレスを入力してください", "请输入邮箱地址": "メールアドレスを入力してください", "请输入邮箱验证码!": "メール認証コードを入力してください", - "请输入邮箱!": "メールアドレスを入力してください", "请输入部署名称": "Please enter deployment name", "请输入部署名称以完成二次确认": "Enter deployment name to complete secondary confirmation", "请输入部署地区,例如:us-central1\n支持使用模型映射格式\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}": "デプロイ先リージョンを入力してください(例:us-central1)\nモデルマッピング形式に対応しています\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}", @@ -3039,12 +3176,17 @@ "请选择该渠道所支持的模型,留空则不更改": "このチャネルに対応しているモデルを選択してください。空欄の場合は変更されません", "请选择过期时间": "有効期限を選択してください", "请选择通知方式": "通知方法を選択してください", + "调整额度": "残高調整", + "调整额度成功": "残高の調整に成功しました", "调用次数": "呼び出し回数", "调用次数分布": "呼び出し回数分布", "调用次数排行": "呼び出し回数ランキング", "调用趋势": "呼び出し推移", "调试信息": "デバッグ情報", "谨慎": "注意", + "警告": "警告", + "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔": "警告:キープアライブを有効にした後、データ書き込み後にチャネルエラーが発生した場合、システムは再試行できません。有効化が必須の場合は、Ping間隔を可能な限り長く設定することを推奨します", + "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "警告:2要素認証を無効にすると、認証設定とすべてのバックアップコードが永久に削除されます。この操作は元に戻すことができません", "豆包": "豆包", "账单": "請求情報", "账户充值": "アカウントチャージ", @@ -3079,15 +3221,19 @@ "路径正则": "パス正規表現", "路径正则(每行一个)": "パス正規表現(1行に1つ)", "跳转": "リダイレクト", + "转大写": "", + "转小写": "", "轮询": "ポーリング", "轮询模式": "ポーリングモード", "轮询模式必须搭配Redis和内存缓存功能使用,否则性能将大幅降低,并且无法实现轮询功能": "ポーリングモードは、Redisとメモリキャッシュ機能との併用が必須です。併用しない場合、パフォーマンスが大幅に低下し、ポーリング機能も実現できません", + "软错误": "", "输入": "入力", + "输入 {{price}} / 1M tokens": "入力 {{price}} / 1M tokens", + "输入 $/1M tokens": "", "输入 OIDC 的 Authorization Endpoint": "OIDCのAuthorization Endpointを入力してください", "输入 OIDC 的 Client ID": "OIDCのClient IDを入力してください", "输入 OIDC 的 Token Endpoint": "OIDCのToken Endpointを入力してください", "输入 OIDC 的 Userinfo Endpoint": "OIDCのUserinfo Endpointを入力してください", - "输入 {{price}} / 1M tokens": "入力 {{price}} / 1M tokens", "输入IP地址后回车,如:8.8.8.8": "IPアドレスを入力してEnter(例:8.8.8.8)", "输入JSON对象": "JSONオブジェクトを入力してください", "输入价格": "入力価格", @@ -3096,6 +3242,7 @@ "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "入力料金:{{symbol}}{{price}} / 1M tokens{{audioPrice}}", "输入你注册的 LinuxDO OAuth APP 的 ID": "登録したLinuxDO OAuth APPのIDを入力してください", "输入你的账户名{{username}}以确认删除": "削除確認: アカウント名{{username}}を入力してください", + "输入倍率": "", "输入域名后回车": "ドメインを入力してEnter", "输入域名后回车,如:example.com": "ドメインを入力してEnter(例:example.com)", "输入密码,最短 8 位,最长 20 位": "パスワードを入力してください(8~20文字)", @@ -3104,6 +3251,7 @@ "输入模型倍率": "モデル倍率を入力してください", "输入模型名称,例如 gpt-4.1": "モデル名を入力してください。例: gpt-4.1", "输入每次价格": "1回あたりの料金を入力してください", + "输入每次调用价格": "", "输入端口后回车,如:80 或 8000-8999": "ポートを入力してEnter(例: 80 または 8000-8999)", "输入系统提示词,用户的系统提示词将优先于此设置": "システムプロンプトを入力してください。ユーザーのシステムプロンプトがこの設定より優先されます", "输入自定义模型名称": "カスタムモデル名を入力してください", @@ -3119,14 +3267,14 @@ "输入验证码完成设置": "認証コードを入力して設定を完了してください", "输出": "出力", "输出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}": "補完 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "出力: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 補完倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "出力: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 出力倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "输出价格": "補完料金", "输出价格 {{symbol}}{{price}} / 1M tokens": "補完料金 {{symbol}}{{price}} / 1M tokens", "输出价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "補完料金:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (補完倍率:{{completionRatio}})", "输出价格:{{symbol}}{{price}} / 1M tokens": "補完料金:{{symbol}}{{price}} / 1M tokens", "输出价格:{{symbol}}{{total}} / 1M tokens": "補完料金:{{symbol}}{{total}} / 1M tokens", "输出倍率 {{completionRatio}}": "Output ratio {{completionRatio}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "出力: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 補完倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "出力: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 出力倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "边栏设置": "サイドバー設定", "过期于": "有効期限", "过期时间": "有効期限", @@ -3140,6 +3288,7 @@ "运行时长(小时)": "Runtime Duration (hours)", "返回修改": "Go back and edit", "返回登录": "ログインに戻る", + "返回错误": "", "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "この画面では価格を基準に入力し、保存時にバックエンドが必要とする倍率 JSON に自動変換されます。", "这些价格都是可选项,不填也可以。": "これらの価格はすべて任意項目で、未入力でも構いません。", "这将删除超过 10 分钟未使用的临时缓存文件": "10分以上使用されていない一時キャッシュファイルを削除します", @@ -3199,6 +3348,7 @@ "选择部署位置(可多选)": "Select deployment location(s) (multiple selections allowed)", "选择预设模板(可选)": "プリセットテンプレートを選択(オプション)", "透传请求体": "リクエストボディパススルー", + "透传请求头": "", "递归": "再帰", "递归策略": "再帰戦略", "通义千问": "Qwen", @@ -3268,7 +3418,9 @@ "配置 SMTP": "SMTP 設定", "配置 Telegram 登录": "Telegram ログイン設定", "配置 Turnstile": "Turnstile 設定", + "配置 Waffo 充值时可用的支付方式,保存后在充值页面展示给用户。": "", "配置 WeChat Server": "WeChatサーバー設定", + "配置:": "設定:", "配置后的效果:": "設定後の効果:", "配置和消息已全部重置": "設定とメッセージがすべてリセットされました", "配置套餐的有效时长": "プランの有効期間を設定", @@ -3287,13 +3439,13 @@ "配置自定义 OAuth 提供商,支持 GitHub Enterprise、GitLab、Gitea、Nextcloud、Keycloak、ORY 等兼容 OAuth 2.0 协议的身份提供商": "カスタムOAuthプロバイダーを設定。GitHub Enterprise、GitLab、Gitea、Nextcloud、Keycloak、ORYなどのOAuth 2.0互換IDプロバイダーをサポート", "配置说明": "設定の説明", "配置邮箱域名白名单": "メールドメインのホワイトリスト設定", - "配置:": "設定:", "重启部署失败": "Failed to restart deployment", "重命名部署": "Rename Deployment", "重复提交": "二重送信", "重复的键名": "重複したキー名", "重复的键名,此值将被后面的同名键覆盖": "キー名が重複しています。この値は後続の同名キーによって上書きされます", "重定向 URL 填": "リダイレクトURLを入力してください", + "重新上传": "", "重新发送": "再送信", "重新生成": "再生成", "重新生成备用码": "バックアップコードの再生成", @@ -3347,15 +3499,15 @@ "限制周期统一使用上方配置的“限制周期”值。": "制限期間は、一律で上記にて設定された「制限期間」の値を使用します。", "限流": "レート制限", "限购": "購入制限", - "随机": "ランダム", - "随机模式": "ランダムモード", - "随机种子 (留空为随机)": "ランダムシード(空欄でランダム)", "隐私政策": "プライバシーポリシー", "隐私政策已更新": "プライバシーポリシーが更新されました", "隐私政策更新失败": "プライバシーポリシーの更新に失敗しました", "隐私设置": "プライバシー設定", "隐藏操作项": "操作項目を非表示", "隐藏调试": "デバッグを非表示", + "随机": "ランダム", + "随机模式": "ランダムモード", + "随机种子 (留空为随机)": "ランダムシード(空欄でランダム)", "零一万物": "Yi", "需要安全验证": "セキュリティ認証が必要です", "需要添加的额度(支持负数)": "追加する残高(マイナス値も可)", @@ -3373,13 +3525,15 @@ "音频补全价格": "音声補完価格", "音频补全价格:{{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (音频补全倍率: {{audioCompRatio}})": "オーディオ補完料金:{{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens(オーディオ補完倍率:{{audioCompRatio}})", "音频补全价格:{{symbol}}{{price}} / 1M tokens": "音声補完価格:{{symbol}}{{price}} / 1M tokens", + "音频补全倍率": "", "音频补全倍率(仅部分模型支持该计费)": "オーディオ補完倍率(一部のモデルのみこの課金に対応)", + "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "音声入力: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 音声倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "音频输入价格": "音声入力価格", "音频输入价格:{{symbol}}{{price}} / 1M tokens": "音声入力価格:{{symbol}}{{price}} / 1M tokens", + "音频输入倍率": "", "音频输入相关的倍率设置,键为模型名称,值为倍率": "オーディオ入力に関する倍率設定です。キー:モデル名、値:倍率。", - "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "音声入力: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 音声倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "オーディオ補完に関する倍率設定です。キー:モデル名、値:倍率。", "音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "音声出力: {{tokens}} / 1M * モデル倍率 {{modelRatio}} * 音声倍率 {{audioRatio}} * 音声補完倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "オーディオ補完に関する倍率設定です。キー:モデル名、値:倍率。", "页脚": "フッター", "页面未找到,请检查您的浏览器地址是否正确": "ページが見つかりませんでした。ブラウザのアドレスが正しいかご確認ください", "页面渲染出错,请刷新页面重试": "ページのレンダリング中にエラーが発生しました。ページを更新して再試行してください。", @@ -3397,9 +3551,10 @@ "预览更新": "更新のプレビュー", "预览模板": "テンプレートをプレビュー", "预览请求体": "リクエストボディのプレビュー", - "预警阈值必须为正数": "アラートしきい値は0より大きい必要があります", "预计结束": "Estimated End", + "预计结果": "予想結果", "预设模板": "プリセットテンプレート", + "预警阈值必须为正数": "アラートしきい値は0より大きい必要があります", "频率惩罚,减少重复词汇的出现": "頻度ペナルティ、単語の繰り返しを減少", "频率限制的周期(分钟)": "レート制限の期間(分)", "颜色": "カラー", @@ -3448,10 +3603,6 @@ "默认折叠侧边栏": "サイドバーをデフォルトで折りたたむ", "默认测试模型": "デフォルトテストモデル", "默认用户消息": "こんにちは", - "默认补全倍率": "デフォルト補完倍率", - "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(現在、Epay APIのみに対応しています。デフォルトで、上記のサーバーURLがコールバックアドレスとして使用されます。)", - ",当前无生效订阅,将自动使用钱包": "、有効なサブスクリプションがないため、自動的にウォレットを使用します", - ",时间:": "、時間:", - ",点击更新": "、クリックして更新してください" + "默认补全倍率": "デフォルト補完倍率" } } diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index a3503904..6ec34b87 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -1,41 +1,66 @@ { "translation": { + " + Web搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_one": " + Web-поиск {{count}} раз / 1K раз * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " + Web搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_few": " + Web-поиск {{count}} раза / 1K раз * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " + Web搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_many": " + Web-поиск {{count}} раз / 1K раз * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", - " + Web搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_one": " + Web-поиск {{count}} раз / 1K раз * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " + Web搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_other": " + Web-поиск {{count}} раз / 1K раз * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " + 图片生成调用 {{symbol}}{{price}} / 1次 * {{ratioType}} {{ratio}}": " + Генерация изображения {{symbol}}{{price}} / 1 вызов * {{ratioType}} {{ratio}}", + " + 文件搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_one": " + Поиск файлов {{count}} раз / 1K раз * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " + 文件搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_few": " + Поиск файлов {{count}} раза / 1K раз * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " + 文件搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_many": " + Поиск файлов {{count}} раз / 1K раз * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", - " + 文件搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_one": " + Поиск файлов {{count}} раз / 1K раз * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " + 文件搜索 {{count}}次 / 1K 次 * {{symbol}}{{price}} * {{ratioType}} {{ratio}}_other": " + Поиск файлов {{count}} раз / 1K раз * {{symbol}}{{price}} * {{ratioType}} {{ratio}}", " 个模型设置相同的值": " моделей с одинаковыми значениями настроек", " 吗?": "?", " 秒": " сек", " 秒。": " сек.", - "$/1M tokens": "$/1M токенов", + ",当前无生效订阅,将自动使用钱包": ", нет активной подписки, автоматически будет использоваться кошелек.", + ",时间:": ", время: ", + ",点击更新": ", нажмите для обновления", + "(共 {{total}} 个,省略 {{omit}} 个)": "", + "(共 {{total}} 个)": "", + "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(В настоящее время поддерживается только интерфейс YiPay, по умолчанию используется адрес сервера выше в качестве адреса обратного вызова!)", + "(筛选后显示 {{count}} 条)_one": "(Showing {{count}} item after filtering)", "(筛选后显示 {{count}} 条)_few": "(Показано {{count}} элемента после фильтрации)", "(筛选后显示 {{count}} 条)_many": "(Показано {{count}} элементов после фильтрации)", - "(筛选后显示 {{count}} 条)_one": "(Showing {{count}} item after filtering)", "(筛选后显示 {{count}} 条)_other": "(Showing {{count}} items after filtering)", "(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(Ввод {{input}} токенов / 1M токенов * {{symbol}}{{price}}", "(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(Ввод {{nonAudioInput}} токенов / 1M токенов * {{symbol}}{{price}} + аудио ввод {{audioInput}} токенов / 1M токенов * {{symbol}}{{audioPrice}}", "(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(Ввод {{nonCacheInput}} токенов / 1M токенов * {{symbol}}{{price}} + кэш {{cacheInput}} токенов / 1M токенов * {{symbol}}{{cachePrice}}", "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(Ввод {{nonImageInput}} токенов + ввод изображения {{imageInput}} токенов / 1M токенов * {{symbol}}{{price}}", + "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "[Максимальное количество запросов] и [Максимальное количество выполненных запросов] имеют максимальное значение 2147483647.", + "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[Максимальное количество запросов] должно быть больше или равно 0, [Максимальное количество выполненных запросов] должно быть больше или равно 1.", + "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", + "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", + "{{count}} 项操作_one": "", + "{{count}} 项操作_few": "", + "{{count}} 项操作_many": "", + "{{count}} 项操作_other": "", + "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", + "{{name}} ID": "{{name}} ID", + "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", + "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", + "「用户可选」决定用户创建令牌时能否自主选择该分组。未勾选的分组只能由管理员在后台分配给用户,不会出现在用户的令牌创建页面中。": "\"User Selectable\" controls whether users can choose this group when creating tokens. Unchecked groups can only be assigned by admin and won't appear in the token creation page.", + "• 视频服务商的跨域限制": "• Ограничения кросс-доменных запросов со стороны видеосервиса", + "• 防盗链保护机制": "• Защита от хотлинков", + "• 需要特定的请求头或认证": "• Требуются специальные заголовки или авторизация", + "© {{currentYear}}": "© {{currentYear}}", + "| 基于": "| Основано на", + "$/1M tokens": "$/1M токенов", + "$/次": "", "0 - 最低": "0 - Минимум", "0 表示不限": "0 означает без лимита", "0.002-1之间的小数": "Десятичное число между 0.002-1", "0.1以上的小数": "Десятичное число выше 0.1", - "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。": "1) Нажмите «Открыть страницу авторизации» для входа; 2) Браузер перенаправит на localhost (ничего страшного, если страница не откроется); 3) Скопируйте полный URL из адресной строки и вставьте ниже; 4) Нажмите «Сгенерировать и заполнить».", "1. 管理员在此创建分组并设置倍率": "1. Admin creates groups and sets ratios here", + "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。": "1) Нажмите «Открыть страницу авторизации» для входа; 2) Браузер перенаправит на localhost (ничего страшного, если страница не откроется); 3) Скопируйте полный URL из адресной строки и вставьте ниже; 4) Нажмите «Сгенерировать и заполнить».", "10 - 最高": "10 - Максимум", "1h缓存创建 {{price}} / 1M tokens": "Создание кэша 1h {{price}} / 1M tokens", "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Создание кэша 1h {{tokens}} токенов / 1M токенов * {{symbol}}{{price}}", "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Создание кеша 1ч {{tokens}} токенов / 1M токенов * {{symbol}}{{price}} (множитель: {{ratio}})", + "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "Создание кэша 1h: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент создания кэша 1h {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", "1h缓存创建价格 {{symbol}}{{price}} / 1M tokens": "Цена создания кеша 1h {{symbol}}{{price}} / 1M tokens", "1h缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h缓存创建倍率: {{cacheCreationRatio1h}})": "Цена создания кеша за 1ч: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M токенов (множитель создания 1ч: {{cacheCreationRatio1h}})", "1h缓存创建价格:{{symbol}}{{price}} / 1M tokens": "Цена создания кеша 1h: {{symbol}}{{price}} / 1M tokens", - "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "Создание кэша 1h: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент создания кэша 1h {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", "2 - 低": "2 - Низкий", "2. 管理员在用户管理中将用户分配到对应分组(如 vip)": "2. Admin assigns users to groups in User Management (e.g., vip)", "2025年5月10日后添加的渠道,不需要再在部署的时候移除模型名称中的\".\"": "Каналы, добавленные после 10 мая 2025 года, не требуют удаления \".\" из имен моделей при развертывании", @@ -46,22 +71,25 @@ "5m缓存创建 {{price}} / 1M tokens": "Создание кэша 5m {{price}} / 1M tokens", "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Создание кэша 5m {{tokens}} токенов / 1M токенов * {{symbol}}{{price}}", "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Создание кеша 5м {{tokens}} токенов / 1M токенов * {{symbol}}{{price}} (множитель: {{ratio}})", + "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "Создание кэша 5m: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент создания кэша 5m {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", "5m缓存创建价格 {{symbol}}{{price}} / 1M tokens": "Цена создания кеша 5m {{symbol}}{{price}} / 1M tokens", "5m缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m缓存创建倍率: {{cacheCreationRatio5m}})": "Цена создания кеша за 5м: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M токенов (множитель создания 5м: {{cacheCreationRatio5m}})", "5m缓存创建价格:{{symbol}}{{price}} / 1M tokens": "Цена создания кеша 5m: {{symbol}}{{price}} / 1M tokens", - "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "Создание кэша 5m: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент создания кэша 5m {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", "8 - 高": "8 - Высокий", "AGPL v3.0协议": "Лицензия AGPL v3.0", "AI 对话": "AI диалог", "AI模型测试环境": "Среда тестирования AI моделей", "AI模型配置": "Конфигурация AI моделей", "AK/SK 模式:使用 AccessKey 和 SecretAccessKey;API Key 模式:使用 API Key": "Режим AK/SK: используйте AccessKey и SecretAccessKey; режим API Key: используйте API Key", + "anthropic-beta JSON 示例": "", "API Key": "API Key", "API Key 模式下不支持批量创建": "Режим API Key не поддерживает массовое создание", "API Key 验证失败": "API Key verification failed", "API Key 验证成功!连接到 io.net 服务正常": "API Key verification successful! Connection to io.net service is normal", "API 地址和相关配置": "Адрес API и связанные настройки", "API 密钥": "Ключ API", + "API 密钥 (沙盒)": "", + "API 密钥 (生产)": "", "API 文档": "Документация API", "API 配置": "Конфигурация API", "API令牌管理": "Управление токенами API", @@ -71,14 +99,13 @@ "API地址": "Адрес API", "API渠道配置": "Конфигурация каналов API", "API端点": "Конечная точка API", - "Authorization Endpoint": "Конечная точка авторизации", "Authorization callback URL 填": "URL обратного вызова авторизации:", + "Authorization Endpoint": "Конечная точка авторизации", + "auto分组调用链路": "Цепочка вызовов автоматической группировки", "Bark推送URL": "URL для push-уведомлений Bark", "Bark推送URL必须以http://或https://开头": "URL для push-уведомлений Bark должен начинаться с http:// или https://", "Bark通知": "Уведомления Bark", "Basic Auth 头": "Заголовок Basic Auth", - "CPU 使用率超过此值时拒绝请求": "Отклонять запросы, когда использование CPU превышает это значение", - "CPU 阈值 (%)": "Порог CPU (%)", "Cached tokens": "Cached tokens", "Cached tokens 占比口径由后端返回:Claude 语义按 cached/(prompt+cached),其余按 cached/prompt。": "Доля кэшированных токенов возвращается бэкендом: семантика Claude считает cached/(prompt+cached), остальные — cached/prompt.", "Changing batch type to:": "Изменение типа пакета на:", @@ -94,13 +121,20 @@ "Client Secret": "Секрет клиента", "Codex 授权": "Авторизация Codex", "Codex 渠道不支持批量创建": "Канал Codex не поддерживает пакетное создание", + "common.changeLanguage": "common.changeLanguage", "Completion tokens": "Completion tokens", "Configuration": "Конфигурация", + "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。": "context_int/context_string читаются из контекста запроса; gjson читает из JSON body входящего запроса по gjson path.", + "CPU 使用率超过此值时拒绝请求": "Отклонять запросы, когда использование CPU превышает это значение", + "CPU 阈值 (%)": "Порог CPU (%)", "Creem API 密钥,敏感信息不显示": "API-ключ Creem, чувствительные данные не отображаются", "Creem Setting Tips": "Creem поддерживает только преднастроенные товары с фиксированной суммой. Эти товары и их цены нужно заранее создать и настроить на сайте Creem, поэтому пополнения с произвольной суммой не поддерживаются. Настройте название и цену товара в Creem, получите идентификатор товара и укажите его ниже. Затем задайте сумму пополнения и отображаемую цену в new-api.", "Creem 介绍": "О сервисе Creem", "Creem 充值": "Пополнение через Creem", "Creem 设置": "Настройки Creem", + "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。": "default and vip can only be assigned to users by admin in \"User Management\". Suitable for tiered pricing, internal testing, or other scenarios where user self-selection is not desired.", + "default为默认设置,可单独设置每个分类的安全等级": "default - это настройка по умолчанию, можно отдельно установить уровень безопасности для каждой категории", + "default为默认设置,可单独设置每个模型的版本": "default - это настройка по умолчанию, можно отдельно установить версию для каждой модели", "Dify渠道只适配chatflow和agent,并且agent不支持图片!": "Канал Dify адаптирован только для chatflow и agent, и agent не поддерживает изображения!", "Discord": "Discord", "Discord Client ID": "ID клиента Discord", @@ -110,11 +144,10 @@ "Discovery scopes": "Discovery scopes", "Discovery 建议 scopes:": "Рекомендуемые Discovery scopes:", "EUR (欧元)": "EUR (евро)", + "false": "false", "GC 已执行": "GC выполнен", "GC 执行失败": "Ошибка выполнения GC", "GC 次数": "Количество GC", - "GPU/容器": "GPU/Container", - "GPU数量": "Number of GPUs", "Gemini安全设置": "Настройки безопасности Gemini", "Gemini思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Адаптация мышления Gemini BudgetTokens = MaxTokens * процент BudgetTokens", "Gemini思考适配设置": "Настройки адаптации мышления Gemini", @@ -129,10 +162,14 @@ "Gotify服务器地址": "Адрес сервера Gotify", "Gotify服务器地址必须以http://或https://开头": "Адрес сервера Gotify должен начинаться с http:// или https://", "Gotify通知": "Уведомления Gotify", + "GPU/容器": "GPU/Container", + "GPU数量": "Number of GPUs", "Grok设置": "Настройки Grok", "Haiku 模型": "Модель Haiku", "Homepage URL 填": "URL домашней страницы:", "ID": "ID", + "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护": "include_obfuscation управляет полями обфускации в потоке Responses. Отключено по умолчанию, чтобы клиенты не отключали эту защиту", + "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "Поле inference_geo управляет регионом размещения данных инференса Claude. Отключено по умолчанию для предотвращения несанкционированной передачи географической информации", "IP": "IP", "IP白名单": "IP Whitelist", "IP白名单(支持CIDR表达式)": "Белый список IP (поддерживает выражения CIDR)", @@ -160,8 +197,8 @@ "LinuxDO": "LinuxDO", "LinuxDO ID": "ID LinuxDO", "Logo 图片地址": "Адрес изображения логотипа", - "MIT许可证": "Лицензия MIT", "Midjourney 任务记录": "Записи задач Midjourney", + "MIT许可证": "Лицензия MIT", "New API项目仓库地址:": "Адрес репозитория проекта New API:", "NewAPI 默认不会将入口请求的 User-Agent 透传到上游渠道;该条件仅用于识别访问本站点的客户端。": "NewAPI по умолчанию не передаёт User-Agent входящего запроса в вышестоящие каналы; это условие используется только для идентификации клиентов, обращающихся к данному сайту.", "OAuth Client ID": "OAuth Client ID", @@ -172,7 +209,6 @@ "Ollama 模型管理": "Ollama Model Management", "Ollama 版本信息": "Ollama Version Info", "Opus 模型": "Модель Opus", - "POST 参数": "Параметры POST", "Passkey": "Passkey", "Passkey 已解绑": "Passkey отвязан", "Passkey 已重置": "Passkey сброшен", @@ -180,50 +216,71 @@ "Passkey 注册失败,请重试": "Регистрация Passkey не удалась, попробуйте еще раз", "Passkey 注册成功": "Регистрация Passkey успешна", "Passkey 登录": "Вход через Passkey", + "Pay Method Name": "", + "Pay Method Type": "", "Ping间隔(秒)": "Интервал Ping (секунды)", + "POST 参数": "Параметры POST", + "price_xxx 的商品价格 ID,新建产品后可获得": "ID цены товара price_xxx, можно получить после создания нового продукта", "Prompt cache hit tokens": "Prompt cache hit tokens", "Prompt tokens": "Prompt tokens", "Reasoning Effort": "Усилие рассуждения", "Request ID": "Request ID", + "RSA 私钥 (沙盒)": "", + "RSA 私钥 (生产)": "", + "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "Поле safety_identifier помогает OpenAI идентифицировать пользователей приложений, которые могут нарушать политику использования. По умолчанию отключено для защиты конфиденциальности пользователей", + "Scopes(可选)": "Scopes (необязательно)", + "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "Поле service_tier используется для указания уровня сервиса, позволяет передавать параметры, которые могут привести к фактической оплате выше ожидаемой. По умолчанию отключено для избежания дополнительных расходов", + "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "Ключ Stripe sk_xxx или rk_xxx, конфиденциальная информация не отображается", "SMTP 发送者邮箱": "Email отправителя SMTP", "SMTP 服务器地址": "Адрес сервера SMTP", "SMTP 端口": "Порт SMTP", "SMTP 访问凭证": "Учетные данные доступа SMTP", "SMTP 账户": "Учетная запись SMTP", + "Sonnet 模型": "Модель Sonnet", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "Поле speed управляет режимом скорости инференса Claude. По умолчанию отключено, чтобы избежать непреднамеренного переключения в режим fast", "SSE 事件": "Событие SSE", "SSE数据流": "Поток данных SSE", "SSRF防护开关详细说明": "Подробное описание переключателя защиты SSRF", "SSRF防护设置": "Настройки защиты SSRF", "SSRF防护详细说明": "Подробное описание защиты SSRF", - "Scopes(可选)": "Scopes (необязательно)", - "Sonnet 模型": "Модель Sonnet", + "standard 已被移除,vip 用户看不到": "standard has been removed, vip users cannot see it", + "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "Поле store используется для авторизации OpenAI хранить данные запросов для оценки и оптимизации продукта. По умолчанию отключено, после включения может привести к неработоспособности Codex", "Stripe 设置": "Настройки Stripe", "Stripe/Creem 商品ID(可选)": "ID продукта Stripe/Creem (необязательно)", "Stripe/Creem 需在第三方平台创建商品并填入 ID": "Товары Stripe/Creem нужно создать на сторонней платформе и указать их ID", - "TTL(秒)": "TTL (секунды)", - "TTL(秒,0 表示默认)": "TTL (секунды, 0 — по умолчанию)", "Telegram": "Telegram", "Telegram Bot Token": "Токен бота Telegram", "Telegram Bot 名称": "Имя бота Telegram", "Telegram ID": "ID Telegram", "Token Endpoint": "Конечная точка токена", + "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。": "Токены конвертируются в квоту/количество использований по коэффициенту. После завершения запроса производится расчёт разницы (дополнительное списание/возврат).", "Total tokens": "Total tokens", + "true": "true", + "TTL(秒,0 表示默认)": "TTL (секунды, 0 — по умолчанию)", + "TTL(秒)": "TTL (секунды)", "Turnstile Secret Key": "Секретный ключ Turnstile", "Turnstile Site Key": "Ключ сайта Turnstile", - "URL 标识,只能包含小写字母、数字和连字符": "Идентификатор URL, допускаются только строчные буквы, цифры и дефисы", - "URL链接": "URL ссылка", - "USD (美元)": "USD (доллар США)", "Unix时间戳": "Временная метка Unix", "Uptime Kuma地址": "Адрес Uptime Kuma", "Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)": "Управление категориями мониторинга Uptime Kuma, можно настроить несколько категорий мониторинга для отображения статуса сервисов (максимум 20)", + "URL 标识,只能包含小写字母、数字和连字符": "Идентификатор URL, допускаются только строчные буквы, цифры и дефисы", + "URL链接": "URL ссылка", + "USD (美元)": "USD (доллар США)", "User Info Endpoint": "Конечная точка информации о пользователе", "User-Agent include(每行一个,可不写)": "User-Agent include (по одному в строке, необязательно)", "Value 正则": "Regex значения", "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AI не поддерживает поле functionResponse.id. При включении это поле будет автоматически удалено", - "WeChat Server 服务器地址": "Адрес сервера WeChat Server", - "WeChat Server 访问凭证": "Учетные данные доступа WeChat Server", - "Web 搜索调用 {{webSearchCallCount}} 次": "Web-поиск вызван {{webSearchCallCount}} раз", + "Waffo API 参数,可空,例如:CREDITCARD,DEBITCARD(最多64位)": "", + "Waffo API 参数,可空(最多64位)": "", + "Waffo 充值": "", + "Waffo 充值的最低数量,默认 1": "", + "Waffo 公钥 (沙盒)": "", + "Waffo 公钥 (生产)": "", + "Waffo 商户 ID": "", + "Waffo 是一个支付聚合平台,支持多种支付方式。": "", + "Waffo 设置": "", "Web 搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Web-поиск: {{count}} / 1K * цена за единицу {{price}} * {{ratioType}} {{ratio}} = {{amount}}", + "Web 搜索调用 {{webSearchCallCount}} 次": "Web-поиск вызван {{webSearchCallCount}} раз", "Webhook 密钥": "Секрет вебхука", "Webhook 签名密钥": "Ключ подписи Webhook", "Webhook地址": "Адрес Webhook", @@ -231,62 +288,38 @@ "Webhook请求结构说明": "Описание структуры запроса Webhook", "Webhook通知": "Уведомления Webhook", "Web搜索价格:{{symbol}}{{price}} / 1K 次": "Цена Web-поиска: {{symbol}}{{price}} / 1K раз", + "WeChat Server 服务器地址": "Адрес сервера WeChat Server", + "WeChat Server 访问凭证": "Учетные данные доступа WeChat Server", "Well-Known URL": "Well-Known URL", "Well-Known URL 必须以 http:// 或 https:// 开头": "Well-Known URL должен начинаться с http:// или https://", + "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示": "Ключ подписи Webhook whsec_xxx, конфиденциальная информация не отображается", "Worker地址": "Адрес Worker", "Worker密钥": "Ключ Worker", - "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "[Максимальное количество запросов] и [Максимальное количество выполненных запросов] имеют максимальное значение 2147483647.", - "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[Максимальное количество запросов] должно быть больше или равно 0, [Максимальное количество выполненных запросов] должно быть больше или равно 1.", - "auto分组调用链路": "Цепочка вызовов автоматической группировки", - "common.changeLanguage": "common.changeLanguage", - "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。": "context_int/context_string читаются из контекста запроса; gjson читает из JSON body входящего запроса по gjson path.", - "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。": "default and vip can only be assigned to users by admin in \"User Management\". Suitable for tiered pricing, internal testing, or other scenarios where user self-selection is not desired.", - "default为默认设置,可单独设置每个分类的安全等级": "default - это настройка по умолчанию, можно отдельно установить уровень безопасности для каждой категории", - "default为默认设置,可单独设置每个模型的版本": "default - это настройка по умолчанию, можно отдельно установить версию для каждой модели", - "false": "false", - "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护": "include_obfuscation управляет полями обфускации в потоке Responses. Отключено по умолчанию, чтобы клиенты не отключали эту защиту", - "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "Поле inference_geo управляет регионом размещения данных инференса Claude. Отключено по умолчанию для предотвращения несанкционированной передачи географической информации", - "price_xxx 的商品价格 ID,新建产品后可获得": "ID цены товара price_xxx, можно получить после создания нового продукта", - "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "Поле safety_identifier помогает OpenAI идентифицировать пользователей приложений, которые могут нарушать политику использования. По умолчанию отключено для защиты конфиденциальности пользователей", - "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "Поле service_tier используется для указания уровня сервиса, позволяет передавать параметры, которые могут привести к фактической оплате выше ожидаемой. По умолчанию отключено для избежания дополнительных расходов", - "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "Поле speed управляет режимом скорости инференса Claude. По умолчанию отключено, чтобы избежать непреднамеренного переключения в режим fast", - "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "Ключ Stripe sk_xxx или rk_xxx, конфиденциальная информация не отображается", - "standard 已被移除,vip 用户看不到": "standard has been removed, vip users cannot see it", - "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "Поле store используется для авторизации OpenAI хранить данные запросов для оценки и оптимизации продукта. По умолчанию отключено, после включения может привести к неработоспособности Codex", - "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。": "Токены конвертируются в квоту/количество использований по коэффициенту. После завершения запроса производится расчёт разницы (дополнительное списание/возврат).", - "true": "true", - "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示": "Ключ подписи Webhook whsec_xxx, конфиденциальная информация не отображается", - "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", - "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", - "{{name}} ID": "{{name}} ID", - "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", - "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", - "| 基于": "| Основано на", - "© {{currentYear}}": "© {{currentYear}}", - "• 视频服务商的跨域限制": "• Ограничения кросс-доменных запросов со стороны видеосервиса", - "• 防盗链保护机制": "• Защита от хотлинков", - "• 需要特定的请求头或认证": "• Требуются специальные заголовки или авторизация", - "「用户可选」决定用户创建令牌时能否自主选择该分组。未勾选的分组只能由管理员在后台分配给用户,不会出现在用户的令牌创建页面中。": "\"User Selectable\" controls whether users can choose this group when creating tokens. Unchecked groups can only be assigned by admin and won't appear in the token creation page.", "一个月": "Один месяц", "一天": "Один день", "一小时": "Один час", "一次调用消耗多少刀,优先级大于模型倍率": "Сколько долларов потребляется за один вызов, приоритет выше чем коэффициент модели", - "一行一个屏蔽词,不需要符号分割": "Одно запрещенное слово на строку, не требуют разделителей", "一行一个,不区分大小写": "Один элемент на строку, без учета регистра", + "一行一个屏蔽词,不需要符号分割": "Одно запрещенное слово на строку, не требуют разделителей", "一键填充到 FluentRead": "Однократное заполнение в FluentRead", "三种操作的区别:": "Differences between the three operations:", "上一个表单块": "Предыдущий блок формы", "上一步": "Предыдущий шаг", + "上传 PNG/JPG/SVG 图片,建议尺寸 ≤ 128×128px": "", + "上传图片": "", "上次保存: ": "Последнее сохранение: ", + "上次检测到可加入模型": "", + "上次检测时间": "", "上游倍率同步": "Синхронизация множителей upstream", "上游模型管理": "Управление моделями апстрима", "上游返回": "Ответ апстрима", "下一个表单块": "Следующий блок формы", + "下一次重置": "Следующий сброс", "下一步": "Следующий шаг", "下午好": "Добрый день", "下载日志": "Download Logs", "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "Ниже показано, какие backend-поля будут записаны после сохранения, чтобы их было удобно сверять с редакторами исходного JSON.", + "下面是带注释的示例,仅用于参考;实际保存时请删除注释。": "", "不会出现": "will not appear", "不再提醒": "Больше не напоминать", "不勾选用户可选": "Not user-selectable", @@ -311,10 +344,10 @@ "两次输入的密码不一致": "Введенные пароли не совпадают", "两次输入的密码不一致!": "Введенные пароли не совпадают!", "两步验证": "Двухфакторная аутентификация", + "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。": "Двухфакторная аутентификация (2FA) предоставляет дополнительную защиту для вашего аккаунта. После включения, при входе потребуется вводить пароль и код подтверждения из приложения аутентификатора.", "两步验证启用成功!": "Двухфакторная аутентификация успешно включена!", "两步验证已禁用": "Двухфакторная аутентификация отключена", "两步验证设置": "Настройки двухфакторной аутентификации", - "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。": "Двухфакторная аутентификация (2FA) предоставляет дополнительную защиту для вашего аккаунта. После включения, при входе потребуется вводить пароль и код подтверждения из приложения аутентификатора.", "个": "шт.", "个GPU": " GPUs", "个人中心": "Личный кабинет", @@ -371,6 +404,7 @@ "仅对自定义模型有效": "Действительно только для пользовательских моделей", "仅当前层": "Только текущий уровень", "仅当自动禁用开启时有效,关闭后不会自动禁用该渠道": "Действительно только при включенном автоматическом отключении, после выключения канал не будет отключаться автоматически", + "仅提交已勾选": "", "仅支持": "Поддерживается только", "仅支持 JSON 对象,必须包含 access_token 与 account_id": "Поддерживаются только JSON-объекты, должны содержать access_token и account_id", "仅支持 JSON 文件": "Поддерживаются только JSON файлы", @@ -378,14 +412,17 @@ "仅支持 OpenAI 接口格式": "Поддерживается только формат интерфейса OpenAI", "仅显示已绑定": "Показать только привязанные", "仅显示矛盾倍率": "Отображать только противоречивые коэффициенты", + "仅检测上游模型更新": "", "仅用于开发环境,生产环境应使用 HTTPS": "Только для среды разработки, в производственной среде следует использовать HTTPS", "仅用于换算,实际保存的是额度": "Только для пересчёта, сохраняется квота", "仅用订阅": "Только подписка", "仅用钱包": "Только кошелек", + "仅管理员可用。开启后,当系统定时检测全部渠道发现上游模型变更或检测异常时,将按你选择的通知方式发送汇总通知;渠道或模型过多时会自动省略部分明细。": "", "仅重置配置": "Только сбросить конфигурацию", "今日关闭": "Закрыть сегодня", "今日已签到": "Зарегистрирован сегодня", "今日已签到,累计签到": "Зарегистрирован сегодня, всего регистраций", + "仍有未处理项": "", "从 0.5 降到 0.3": "reduced from 0.5 to 0.3", "从剪贴板粘贴配置": "Вставить конфигурацию", "从官方模型库同步": "Синхронизировать из официальной библиотеки моделей", @@ -396,10 +433,11 @@ "代理设置": "Настройки прокси", "代码已复制到剪贴板": "Код скопирован в буфер обмена", "令牌": "Токен", + "令牌不存在": "", "令牌分组": "Группа токенов", + "令牌分组,默认为用户的分组": "Группа токенов, по умолчанию используется группа пользователя", "令牌分组设为 auto 时,按以下顺序依次尝试选择可用分组,排在前面的优先级更高": "When token group is set to auto, groups are selected in order of priority, with higher priority groups listed first", "令牌分组设为 auto 时,系统按优先级顺序自动选择一个可用分组。": "When token group is set to auto, the system automatically selects an available group by priority.", - "令牌分组,默认为用户的分组": "Группа токенов, по умолчанию используется группа пользователя", "令牌创建成功,请在列表页面点击复制获取令牌!": "Токен успешно создан, пожалуйста, нажмите копировать на странице списка для получения токена!", "令牌名称": "Имя токена", "令牌已重置并已复制到剪贴板": "Токен сброшен и скопирован в буфер обмена", @@ -411,8 +449,11 @@ "以及": "а также", "仪表盘设置": "Панель управления", "价格": "Цена", + "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "Price: {{symbol}}{{price}} * {{ratioType}}: {{ratio}}", + "价格:${{price}} * {{ratioType}}:{{ratio}}": "Цена: ${{price}} * {{ratioType}}: {{ratio}}", "价格摘要": "Сводка цен", "价格暂时不可用,请稍后重试": "Price temporarily unavailable, please try again later", + "价格模式": "", "价格模式(默认)": "Режим цен (по умолчанию)", "价格计算中...": "Calculating price...", "价格计算失败": "Price calculation failed", @@ -421,8 +462,6 @@ "价格设置方式": "Способ настройки цен", "价格重新计算中...": "Recalculating price...", "价格预估": "Price Estimate", - "价格:${{price}} * {{ratioType}}:{{ratio}}": "Цена: ${{price}} * {{ratioType}}: {{ratio}}", - "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "Price: {{symbol}}{{price}} * {{ratioType}}: {{ratio}}", "任一满足(OR)": "Любое совпадение (OR)", "任务 ID": "ID задачи", "任务ID": "ID задачи", @@ -439,7 +478,6 @@ "余额充值管理": "Управление пополнением баланса", "作废": "Аннулировать", "作废于": "Аннулировано", - "下一次重置": "Следующий сброс", "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "После аннулирования подписка сразу станет недействительной. История не изменится. Продолжить?", "作用域": "Область действия", "作用域:包含分组": "Область действия: включить группу", @@ -447,6 +485,11 @@ "作用域:包含规则名称": "Область действия: включить имя правила", "你似乎并没有修改什么": "Похоже, вы ничего не изменили", "你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "Вы можете добавить их вручную в разделе «Пользовательские названия моделей», нажать «Заполнить», затем отправить или воспользоваться действиями ниже для автоматической обработки.", + "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?_one": "", + "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?_few": "", + "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?_many": "", + "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?_other": "", + "使用 {{name}} 继续": "Продолжить с {{name}}", "使用 Discord 继续": "Продолжить через Discord", "使用 GitHub 继续": "Продолжить с GitHub", "使用 JSON 对象格式,格式为:{\"组名\": [最多请求次数, 最多请求完成次数]}": "Используйте формат объекта JSON, формат: {\"Имя группы\": [Максимальное количество запросов, Максимальное количество выполненных запросов]}", @@ -455,43 +498,44 @@ "使用 Passkey 实现免密且更安全的登录体验": "Используйте Passkey для безпарольного и более безопасного входа", "使用 Passkey 登录": "Войти с Passkey", "使用 Passkey 验证": "Проверить с Passkey", - "使用 {{name}} 继续": "Продолжить с {{name}}", "使用 微信 继续": "Продолжить с WeChat", "使用 用户名 注册": "Зарегистрироваться с именем пользователя", "使用 邮箱或用户名 登录": "Войти с email или именем пользователя", "使用ID排序": "Сортировать по ID", "使用分组": "Using Group", + "使用原生额度输入": "Ввод в исходных единицах", "使用日志": "Журнал использования", "使用模式": "Режим использования", "使用统计": "Статистика использования", - "使用认证器应用扫描二维码": "Отсканировать QR-код с помощью приложения аутентификатора", "使用认证器应用(如 Google Authenticator、Microsoft Authenticator)扫描下方二维码:": "Отсканируйте QR-код ниже с помощью приложения аутентификатора (например, Google Authenticator, Microsoft Authenticator):", + "使用认证器应用扫描二维码": "Отсканировать QR-код с помощью приложения аутентификатора", "使用说明": "Guide", "例如 /var/cache/new-api": "напр.: /var/cache/new-api", - "例如 https://docs.newapi.pro": "Например https://docs.newapi.pro", "例如 €, £, Rp, ₩, ₹...": "Например €, £, Rp, ₩, ₹...", + "例如 https://docs.newapi.pro": "Например https://docs.newapi.pro", + "例如 https://example.com/api/waffo/webhook": "", + "例如 https://example.com/console/topup": "", + "例如:": "например:", "例如: /bin/bash -c \"python app.py\"": "e.g.: /bin/bash -c \"python app.py\"", "例如: nginx:latest": "e.g.: nginx:latest", "例如: socks5://user:pass@host:port": "например: socks5://user:pass@host:port", - "例如发卡网站的购买链接": "например ссылка на покупку на сайте карт", - "例如(全渠道):": "Пример (все каналы):", - "例如(指定渠道):": "Пример (указанные каналы):", - "例如:": "например:", "例如:-c": "e.g.: -c", "例如:/bin/bash": "e.g.: /bin/bash", "例如:0001": "например: 0001", "例如:1000": "например: 1000", "例如:100000": "Например: 100000", - "例如:2000": "например: 2000", "例如:2,就是最低充值2$": "например: 2, это минимальное пополнение 2$", + "例如:2000": "например: 2000", "例如:4.99": "Например: 4.99", "例如:401, 403, 429, 500-599": "напр.: 401, 403, 429, 500-599", "例如:7,就是7元/美金": "например: 7, это 7 юаней/доллар США", - "例如:GitHub Enterprise": "напр.: GitHub Enterprise", + "例如:Credit Card": "", "例如:email": "напр.: email", "例如:example.com": "например: example.com", "例如:github / si:google / https://example.com/logo.png / 🐱": "напр.: github / si:google / https://example.com/logo.png / 🐱", + "例如:GitHub Enterprise": "напр.: GitHub Enterprise", "例如:github-enterprise": "напр.: github-enterprise", + "例如:gpt-4.1-nano,regex:^claude-.*$,regex:^sora-.*$": "", "例如:https://example.com/.well-known/openid-configuration": "напр.: https://example.com/.well-known/openid-configuration", "例如:https://gitea.example.com": "напр.: https://gitea.example.com", "例如:https://yourdomain.com": "например: https://yourdomain.com", @@ -502,9 +546,13 @@ "例如:prod_6I8rBerHpPxyoiU9WK4kot": "Например: prod_6I8rBerHpPxyoiU9WK4kot", "例如:sub、id、data.user.id": "напр.: sub, id, data.user.id", "例如:基础套餐": "Например: базовый пакет", + "例如:清理工具参数,避免上游校验错误": "", "例如:该请求不满足准入策略": "напр.: Этот запрос не соответствует политике допуска", "例如:适合轻度使用": "Например: для легкого использования", "例如:需要等级 {{required}},你当前等级 {{current}}": "напр.: Требуется уровень {{required}}, ваш текущий уровень {{current}}", + "例如(全渠道):": "Пример (все каналы):", + "例如(指定渠道):": "Пример (указанные каналы):", + "例如发卡网站的购买链接": "например ссылка на покупку на сайте карт", "供应商": "Поставщик", "供应商介绍": "Описание поставщика", "供应商信息:": "Информация о поставщике:", @@ -515,6 +563,8 @@ "供应商更新成功!": "Поставщик успешно обновлен!", "侧边栏管理(全局控制)": "Управление боковой панелью (глобальный контроль)", "侧边栏设置保存成功": "Настройки боковой панели успешно сохранены", + "保前缀": "", + "保后缀": "", "保存": "Сохранить", "保存 Discord OAuth 设置": "Сохранить настройки Discord OAuth", "保存 GitHub OAuth 设置": "Сохранить настройки GitHub OAuth", @@ -530,8 +580,8 @@ "保存备用码": "Сохранить резервные коды", "保存备用码以备不时之需": "Сохраните резервные коды на случай необходимости", "保存失败": "Не удалось сохранить", - "保存失败:": "Не удалось сохранить:", "保存失败,请重试": "Не удалось сохранить, попробуйте еще раз", + "保存失败:": "Не удалось сохранить:", "保存屏蔽词过滤设置": "Сохранить настройки фильтрации запрещенных слов", "保存性能设置": "Сохранить настройки производительности", "保存成功": "Успешно сохранено", @@ -549,11 +599,11 @@ "保存预览": "Предпросмотр сохранения", "保存额度设置": "Сохранить настройки лимитов", "保留": "kept", + "保留最近N个文件": "Хранить последние N файлов", + "保留最近N天": "Хранить за последние N дней", "保留原值(目标已有值时不覆盖)": "Сохранить исходное значение (не перезаписывать, если цель уже имеет значение)", "保留天数": "Дней для хранения", "保留文件数": "Файлов для хранения", - "保留最近N个文件": "Хранить последние N файлов", - "保留最近N天": "Хранить за последние N дней", "修复数据库一致性": "Исправить согласованность базы данных", "修改为": "Изменить на", "修改子渠道优先级": "Изменить приоритет дочерних каналов", @@ -565,6 +615,7 @@ "倍率信息": "Информация о коэффициентах", "倍率是为了方便换算不同价格的模型": "Коэффициенты предназначены для удобного пересчета моделей с разными ценами", "倍率模式": "Режим коэффициентов", + "倍率模式(默认)": "", "倍率用于计费乘数,勾选「用户可选」后用户可在创建令牌时选择该分组": "Ratio is the billing multiplier. Check \"User Selectable\" to let users pick this group when creating tokens", "倍率类型": "Тип коэффициента", "假设再加两个分组 default 和 vip,但不勾选用户可选:": "Now add two more groups default and vip, but without checking User Selectable:", @@ -574,12 +625,12 @@ "停用": "Отключить", "允许 AccountFilter 参数": "Разрешить параметр AccountFilter", "允许 HTTP 协议图片请求(适用于自部署代理)": "Разрешить запросы изображений по протоколу HTTP (применимо для самостоятельно развернутых прокси)", - "允许 Turnstile 用户校验": "Разрешить проверку пользователей Turnstile", "允许 inference_geo 透传": "Разрешить передачу inference_geo", "允许 safety_identifier 透传": "Разрешить сквозную передачу safety_identifier", "允许 service_tier 透传": "Разрешить сквозную передачу service_tier", "允许 speed 透传": "Разрешить передачу speed", "允许 stream_options.include_obfuscation 透传": "Разрешить передачу stream_options.include_obfuscation", + "允许 Turnstile 用户校验": "Разрешить проверку пользователей Turnstile", "允许不安全的 Origin(HTTP)": "Разрешить небезопасные Origin (HTTP)", "允许回调(会泄露服务器 IP 地址)": "Разрешить обратные вызовы (может раскрыть IP-адрес сервера)", "允许在 Stripe 支付中输入促销码": "Разрешить ввод промокодов при оплате через Stripe", @@ -600,13 +651,13 @@ "允许重试": "Разрешить повтор", "元": "Юань", "充值": "Пополнить", - "充值价格显示": "Отображение цены пополнения", "充值价格(x元/美金)": "Цена пополнения (x юаней/доллар США)", + "充值价格显示": "Отображение цены пополнения", "充值分组倍率": "Коэффициенты групп пополнения", "充值分组倍率不是合法的 JSON 字符串": "Коэффициенты групп пополнения не являются допустимой JSON строкой", "充值数量": "Количество пополнения", - "充值数量不能小于": "Количество пополнения не может быть меньше", "充值数量,最低 ": "Количество пополнения, минимум ", + "充值数量不能小于": "Количество пополнения не может быть меньше", "充值方式设置": "Настройки способов пополнения", "充值方式设置不是合法的 JSON 字符串": "Настройки способов пополнения не являются допустимой JSON строкой", "充值确认": "Подтверждение пополнения", @@ -622,8 +673,8 @@ "兑换成功!": "Обмен успешен!", "兑换码充值": "Пополнение кодом купона", "兑换码创建成功": "Код купона успешно создан", - "兑换码创建成功!": "Код купона успешно создан!", "兑换码创建成功,是否下载兑换码?": "Код купона успешно создан, скачать код купона?", + "兑换码创建成功!": "Код купона успешно создан!", "兑换码将以文本文件的形式下载,文件名为兑换码的名称。": "Код купона будет загружен в виде текстового файла, имя файла - название кода купона.", "兑换码更新成功!": "Код купона успешно обновлен!", "兑换码生成管理": "Управление генерацией кодов купонов", @@ -632,6 +683,7 @@ "全局控制侧边栏区域和功能显示,管理员隐藏的功能用户无法启用": "Глобальный контроль отображения области и функций боковой панели, пользователи не могут включить функции, скрытые администратором", "全局设置": "Глобальные настройки", "全选": "Выбрать все", + "全选当前列表模型": "", "全部": "Все", "全部供应商": "Все поставщики", "全部分组": "Все группы", @@ -653,18 +705,18 @@ "公告更新失败": "Не удалось обновить объявление", "公告类型": "Тип объявления", "共": "Всего", + "共 {{count}} 个密钥_one": "Всего {{count}} ключ", "共 {{count}} 个密钥_few": "Всего {{count}} ключа", "共 {{count}} 个密钥_many": "Всего {{count}} ключей", - "共 {{count}} 个密钥_one": "Всего {{count}} ключ", "共 {{count}} 个密钥_other": "Всего {{count}} ключей", "共 {{count}} 个模型": "Всего {{count}} моделей", + "共 {{count}} 个模型_one": "{{count}} модель", "共 {{count}} 个模型_few": "{{count}} модели", "共 {{count}} 个模型_many": "{{count}} моделей", - "共 {{count}} 个模型_one": "{{count}} модель", "共 {{count}} 个模型_other": "{{count}} моделей", + "共 {{count}} 条日志_one": "{{count}} log entry", "共 {{count}} 条日志_few": "{{count}} записи журнала", "共 {{count}} 条日志_many": "{{count}} записей журнала", - "共 {{count}} 条日志_one": "{{count}} log entry", "共 {{count}} 条日志_other": "{{count}} log entries", "共 {{total}} 项,当前显示 {{start}}-{{end}} 项": "Всего {{total}} элементов, отображаются {{start}}-{{end}}", "关": "Выкл", @@ -676,8 +728,8 @@ "关闭": "Закрыть", "关闭侧边栏": "Закрыть боковую панель", "关闭公告": "Закрыть объявление", - "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "После закрытия это уведомление больше не будет показываться (только в этом браузере). Закрыть?", "关闭后,此模型将不会被“同步官方”自动覆盖或创建": "После отключения эта модель не будет автоматически перезаписана или создана при \"синхронизации с официальной\"", + "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "После закрытия это уведомление больше не будет показываться (только в этом браузере). Закрыть?", "关闭弹窗,已停止批量测试": "Окно закрыто, массовое тестирование остановлено", "关闭提示": "Закрыть уведомление", "其他": "Другое", @@ -695,11 +747,25 @@ "内置": "Встроенный", "内置 Ollama 镜像": "Built-in Ollama Image", "再次输入部署名称": "Enter Deployment Name Again", + "最低": "Минимум", + "最低充值数量": "", + "最低充值美元数量": "Минимальная сумма пополнения в долларах", + "最后使用时间": "Время последнего использования", + "最后更新": "Last Updated", + "最后请求": "Последний запрос", + "最大GPU数量": "Max Number of GPUs", + "最大可用": "Max Available", + "最大条目数": "Макс. кол-во записей", + "最终抵扣": "Итоговое списание", + "最近一次": "Последний", + "最近事件": "Recent Events", + "最高优先级": "highest priority", "写": "Запись", "准入策略": "Политика допуска", "准入策略 JSON(可选)": "JSON политики допуска (необязательно)", "准备中...": "Preparing...", "准备完成初始化": "Подготовка к инициализации завершена", + "减少": "Уменьшить", "凭证已刷新": "Учётные данные обновлены", "分类名称": "Название категории", "分组": "Группа", @@ -752,17 +818,20 @@ "创建用户": "Создать пользователя", "初始化失败,请重试": "Инициализация не удалась, попробуйте еще раз", "初始化系统": "Инициализация системы", + "删请求头": "", "删除": "Удалить", "删除 Key 来源": "Удалить источник ключа", "删除会彻底移除该订阅记录(含权益明细)。是否继续?": "Удаление полностью удалит запись подписки (включая детали прав). Продолжить?", "删除后无法恢复,确定要删除模型 \"{{name}}\" 吗?": "Cannot be recovered after deletion, are you sure you want to delete model \"{{name}}\"?", "删除失败": "Не удалось удалить", "删除密钥失败": "Не удалось удалить Токен", + "删除已选 {{selected}} / {{total}}": "", "删除成功": "Токен успешно удален", "删除所选": "Удалить выбранное", "删除所选令牌": "Удалить выбранные токены", "删除所选通道": "Удалить выбранные каналы", "删除条件": "Удалить условие", + "删除模型": "", "删除禁用密钥失败": "Не удалось удалить отключенные Токены", "删除禁用通道": "Удалить отключенные каналы", "删除自动禁用密钥": "Удалить автоматически отключенные Токены", @@ -779,19 +848,20 @@ "刷新缓存统计": "Обновить статистику кэша", "刷新缓存统计失败": "Не удалось обновить статистику кэша", "刷新页面": "Обновить страницу", + "前:": "До:", "前往 io.net API Keys": "Go to io.net API Keys", "前往设置": "Go to Settings", "前往设置页面": "Go to Settings Page", "前缀": "Префикс", - "前:": "До:", + "前置": "", + "剪贴板中未检测到连接信息": "Данные подключения не найдены в буфере обмена", + "副本数量": "Number of Replicas", "剩余": "Remaining", "剩余备用码:": "Оставшиеся резервные коды:", "剩余时间": "Remaining Time", "剩余额度": "Оставшаяся квота", - "剩余额度$": "Оставшаяся квота$", "剩余额度/总额度": "Оставшаяся квота/Общая квота", - "剪贴板中未检测到连接信息": "Данные подключения не найдены в буфере обмена", - "副本数量": "Number of Replicas", + "剩余额度$": "Оставшаяся квота$", "功能特性": "Функциональные возможности", "加入渠道": "Join Channel", "加入预填组": "Присоединиться к группе предварительного заполнения", @@ -823,6 +893,7 @@ "区域": "Регион", "升级分组": "Группа повышения", "单GPU小时费率": "Per GPU Hour Rate", + "单价 (USD)": "", "历史消耗": "Историческое потребление", "原价": "Первоначальная цена", "原价,和普通用户一样": "original price, same as regular users", @@ -830,19 +901,23 @@ "原密码": "Старый пароль", "原生格式": "Нативный формат", "原生额度": "Исходный лимит", - "使用原生额度输入": "Ввод в исходных единицах", - "收起原生额度输入": "Скрыть ввод в исходных единицах", + "去前缀": "", + "去后缀": "", + "去处理{{type}}": "", + "去空格": "", "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "Дедупликация завершена: до дедупликации {{before}} ключей, после дедупликации {{after}} ключей", "参与官方同步": "Участвовать в официальной синхронизации", "参数": "Параметры", "参数值": "Значение параметра", "参数覆盖": "Переопределение параметров", "参数覆盖 JSON 已复制": "JSON переопределения параметров скопирован", + "参数覆盖已复制": "", "参数覆盖必须是合法的 JSON 对象": "Переопределение параметров должно быть допустимым JSON-объектом", "参数覆盖必须是合法的 JSON 格式!": "Переопределение параметров должно быть в допустимом формате JSON!", "参数覆盖模板": "Шаблон переопределения параметров", "参数覆盖模板 JSON 格式不正确": "Некорректный формат JSON шаблона переопределения параметров", "参数覆盖模板预览": "Предпросмотр шаблона переопределения параметров", + "参数覆盖详情": "", "参数配置": "Конфигурация параметров", "参数配置有误": "Некорректная конфигурация параметров", "参数错误": "Ошибка параметра", @@ -856,6 +931,7 @@ "取消全选": "Отменить выбор всех", "取消选择": "Deselect", "变换": "Трансформация", + "变更": "Изменение", "变焦": "Масштабирование", "变量值": "Variable Value", "变量名": "Variable Name", @@ -866,6 +942,7 @@ "可以根据用户分组增减令牌可选的分组范围。例如 vip 用户额外开放 premium 分组,或移除某个分组的选择权。": "Adjust the selectable group range based on user group. E.g., grant vip users access to premium, or remove access to a group.", "可以根据用户分组设置不同的计费倍率。例如 vip 用户使用 standard 令牌时倍率从 1.0 降为 0.8。": "Set different billing ratios based on user group. E.g., vip users using standard tokens get ratio 0.8 instead of 1.0.", "可信": "Доверенный", + "可勾选需要执行的变更:新增会加入渠道模型列表,删除会从渠道模型列表移除。": "", "可在设置页面设置关于内容,支持 HTML & Markdown": "Можно установить содержимое страницы \"О нас\" на странице настроек, поддерживается HTML и Markdown", "可手动填写,多个 scope 用空格分隔": "Можно заполнить вручную, несколько scope разделяются пробелами", "可用": "Доступно", @@ -881,14 +958,15 @@ "可视化": "Визуализация", "可视化倍率设置": "Визуальные настройки коэффициента", "可视化编辑": "Визуальное редактирование", - "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。": "Необязательно. Сопоставление User-Agent входящего запроса; любая строка, совпадающая как подстрока (без учёта регистра), считается совпадением.", - "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。": "Необязательно. Проверка извлечённого ключа аффинити по regex; пустое поле — без проверки.", - "可选。对请求路径进行匹配;不填表示匹配所有路径。": "Необязательно. Сопоставление пути запроса; пустое поле — совпадение со всеми путями.", - "可选值": "Дополнительные значения", + "可空": "", "可选,公告的补充说明": "Необязательно, дополнительное описание объявления", "可选,用于复现结果": "Необязательно, для воспроизводимых результатов", "可选:基于用户信息 JSON 做组合条件准入,条件不满足时返回自定义提示": "Необязательно: Допуск на основе комбинированных условий из JSON пользовательской информации; при несоответствии условиям возвращается пользовательское сообщение", "可选:用于自动生成端点或 Discovery URL": "Необязательно: Используется для автоматической генерации конечных точек или Discovery URL", + "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。": "Необязательно. Сопоставление User-Agent входящего запроса; любая строка, совпадающая как подстрока (без учёта регистра), считается совпадением.", + "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。": "Необязательно. Проверка извлечённого ключа аффинити по regex; пустое поле — без проверки.", + "可选。对请求路径进行匹配;不填表示匹配所有路径。": "Необязательно. Сопоставление пути запроса; пустое поле — совпадение со всеми путями.", + "可选值": "Дополнительные значения", "合计:{{total}}": "Итого: {{total}}", "合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "Итого: текстовая часть {{textTotal}} + аудиочасть {{audioTotal}} = {{total}}", "同时重置消息": "Одновременно сбросить сообщения", @@ -896,6 +974,7 @@ "同步到渠道": "Sync to Channel", "同步向导": "Мастер синхронизации", "同步失败": "Синхронизация не удалась", + "同步字段": "", "同步成功": "Синхронизация успешна", "同步接口": "Интерфейс синхронизации", "同步渠道失败": "Failed to sync channel", @@ -905,10 +984,11 @@ "名称+密钥": "Название+ключ", "名称不能为空": "Название не может быть пустым", "名称匹配类型": "Тип соответствия названия", + "后:": "После:", + "后端固定": "", "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "Фиксированный backend-коэффициент: {{ratio}}. Это поле только показывает вычисленную цену.", "后端请求失败": "Запрос к бэкенду не удался", "后缀": "Суффикс", - "后:": "После:", "向右展开": "Развернуть вправо", "向左展开": "Развернуть влево", "否": "Нет", @@ -921,17 +1001,17 @@ "启动部署失败": "Failed to start deployment", "启动配置": "Startup Configuration", "启用": "Включить", - "启用 Prompt 检查": "Включить проверку Prompt", "启用 io.net 部署": "Enable io.net Deployment", "启用 io.net 部署开关": "Enable io.net Deployment Switch", "启用 io.net 部署时必须填写 API Key": "API Key is required when enabling io.net deployment", + "启用 Prompt 检查": "Включить проверку Prompt", + "启用 Waffo": "", "启用2FA失败": "Не удалось включить 2FA", "启用Claude思考适配(-thinking后缀)": "Включить адаптацию мышления Claude (суффикс -thinking)", "启用FunctionCall思维签名填充": "Включить автозаполнение thoughtSignature для FunctionCall", "启用Gemini思考后缀适配": "Включить адаптацию суффикса мышления Gemini", "启用Ping间隔": "Включить интервал Ping", "启用SMTP SSL": "Включить SMTP SSL", - "强制使用 AUTH LOGIN": "Принудительно AUTH LOGIN", "启用SSRF防护(推荐开启以保护服务器安全)": "Включить защиту SSRF (рекомендуется включить для защиты безопасности сервера)", "启用供应商": "Включить поставщика", "启用全部": "Включить все", @@ -940,6 +1020,7 @@ "启用后套餐将在用户端展示。是否继续?": "После включения план будет отображаться пользователям. Продолжить?", "启用后将优先复用上一次成功的渠道(粘滞选路)。": "При включении последний успешный канал будет использоваться повторно в приоритетном порядке (прилипающая маршрутизация).", "启用后将使用 Creem Test Mode": "После включения будет использован тестовый режим Creem", + "启用后将使用 Waffo 沙盒环境": "", "启用密钥失败": "Не удалось включить ключ", "启用屏蔽词过滤功能": "Включить функцию фильтрации запрещённых слов", "启用性能监控": "Включить мониторинг производительности", @@ -969,10 +1050,14 @@ "响应缺少凭据": "В ответе отсутствуют учётные данные", "响应缺少授权链接": "В ответе отсутствует ссылка авторизации", "商品价格 ID": "ID цены товара", + "商户 ID": "", "回答内容": "Содержание ответа", "回调 URL 填": "URL обратного вызова", "回调 URL 格式": "Формат URL обратного вызова", "回调地址": "Адрес обратного вызова", + "回调支付方式": "Способ оплаты в callback", + "回调调用者IP": "IP вызывающего callback", + "回调通知地址": "", "固定价格": "Фиксированная цена", "固定价格(每次)": "Фиксированная цена (за каждый раз)", "固定价格值": "Значение фиксированной цены", @@ -980,23 +1065,25 @@ "图标": "Значок", "图标使用 react-icons(Simple Icons)或 URL/emoji,例如:github、gitlab、si:google": "Иконка: react-icons (Simple Icons) или URL/emoji, напр.: github, gitlab, si:google", "图标使用@lobehub/icons库,如:OpenAI、Claude.Color,支持链式参数:OpenAI.Avatar.type={'platform'}、OpenRouter.Avatar.shape={'square'},查询所有可用图标请 ": "Используйте библиотеку @lobehub/icons, например: OpenAI, Claude.Color, поддерживаются цепочечные параметры: OpenAI.Avatar.type={'platform'}, OpenRouter.Avatar.shape={'square'}, для просмотра всех доступных иконок, пожалуйста, ", + "图标文件不能超过 100KB,请压缩后重新上传": "", "图混合": "Смешивание изображений", "图片倍率 {{imageRatio}}": "Коэффициент изображения {{imageRatio}}", "图片功能在自定义请求体模式下不可用": "Функция изображений недоступна в режиме пользовательского запроса", "图片地址": "URL изображения", "图片已添加": "Изображение добавлено", - "图片生成调用:{{symbol}}{{price}} / 1次": "Вызов генерации изображения: {{symbol}}{{price}} / 1 раз", "图片生成:1 次 * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Генерация изображения: 1 вызов * цена за единицу {{price}} * {{ratioType}} {{ratio}} = {{amount}}", + "图片生成调用:{{symbol}}{{price}} / 1次": "Вызов генерации изображения: {{symbol}}{{price}} / 1 раз", "图片输入 {{price}} / 1M tokens": "Ввод изображения {{price}} / 1M tokens", "图片输入: {{imageRatio}}": "Ввод изображения: {{imageRatio}}", + "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Ввод изображения: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент изображения {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "图片输入价格": "Цена входного изображения", "图片输入价格 {{symbol}}{{price}} / 1M tokens": "Цена входного изображения {{symbol}}{{price}} / 1M tokens", "图片输入价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (图片倍率: {{imageRatio}})": "Цена ввода изображения: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M токенов (коэффициент изображения: {{imageRatio}})", "图片输入价格:{{symbol}}{{price}} / 1M tokens": "Цена входного изображения: {{symbol}}{{price}} / 1M tokens", "图片输入价格:{{symbol}}{{total}} / 1M tokens": "Цена входного изображения: {{symbol}}{{total}} / 1M tokens", + "图片输入倍率": "", "图片输入倍率(仅部分模型支持该计费)": "Коэффициент ввода изображения (только некоторые модели поддерживают эту тарификацию)", "图片输入相关的倍率设置,键为模型名称,值为倍率,仅部分模型支持该计费": "Настройки коэффициента, связанные с вводом изображения, ключ - название модели, значение - коэффициент, только некоторые модели поддерживают эту тарификацию", - "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Ввод изображения: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент изображения {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "图生文": "Изображение в текст", "图生视频": "Изображение в видео", "在Gotify服务器创建应用后获得的令牌,用于发送通知": "Токен, полученный после создания приложения на сервере Gotify, используется для отправки уведомлений", @@ -1027,12 +1114,12 @@ "填充新模板": "Заполнить новый шаблон", "填充旧模板": "Заполнить старый шаблон", "填充模板": "Заполнить шаблон", - "填充模板(全渠道)": "Заполнить шаблон (все каналы)", - "填充模板(指定渠道)": "Заполнить шаблон (выбранные каналы)", "填充模板:等级+激活": "Заполнить шаблон: Уровень + Активация", "填充模板:等级提示": "Заполнить шаблон: Промпт уровня", "填充模板:组织或角色": "Заполнить шаблон: Организация или роль", "填充模板:组织提示": "Заполнить шаблон: Промпт организации", + "填充模板(全渠道)": "Заполнить шаблон (все каналы)", + "填充模板(指定渠道)": "Заполнить шаблон (выбранные каналы)", "填入": "Заполнить", "填入 CC Switch": "Заполнить CC Switch", "填入所有模型": "Заполнить все модели", @@ -1050,7 +1137,9 @@ "填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议": "После заполнения содержимого пользовательского соглашения, пользователям потребуется отметить, что они прочитали пользовательское соглашение при регистрации", "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "После заполнения содержимого политики конфиденциальности, пользователям потребуется отметить, что они прочитали политику конфиденциальности при регистрации", "填写音频补全价格前,需要先填写音频输入价格。": "Перед указанием цены аудио-завершения сначала задайте цену аудио-ввода.", + "处理上游模型更新": "", "处理中": "Processing", + "处理全部渠道上游更新": "", "备份支持": "Поддержка резервного копирования", "备份状态": "Состояние резервного копирования", "备注": "Примечания", @@ -1060,6 +1149,7 @@ "复制": "Копировать", "复制代码": "Копировать код", "复制令牌": "Копировать токен", + "复制令牌失败": "", "复制全部": "Копировать всё", "复制名称": "Копировать название", "复制失败": "Не удалось скопировать", @@ -1078,6 +1168,7 @@ "复制渠道的所有信息": "Копировать всю информацию о канале", "复制版本号": "Copy Version", "复制生成的密钥并粘贴到此处": "Copy the generated key and paste it here", + "复制请求头": "", "复制连接信息": "Копировать данные подключения", "复制链接": "Скопировать ссылку", "外接设备": "Внешнее устройство", @@ -1101,13 +1192,13 @@ "套餐标题": "Название плана", "套餐标题不能为空": "Название тарифа не может быть пустым", "套餐的基本信息和定价": "Основная информация и цена плана", + "如:大带宽批量分析图片推荐": "Например: рекомендуется для пакетного анализа изображений с большой пропускной способностью", + "如:香港线路": "Например: Гонконгская линия", "如果亲和到的渠道失败,重试到其他渠道成功后,将亲和更新到成功的渠道。": "Если канал аффинити не сработал, после успешного повтора на другом канале аффинити будет обновлена на успешный канал.", "如果你对接的是上游One API或者New API等转发项目,请使用OpenAI类型,不要使用此类型,除非你知道你在做什么。": "Если вы интегрируетесь с восходящими проектами пересылки, такими как One API или New API, используйте тип OpenAI, не используйте этот тип, если вы не знаете, что делаете.", "如果用户请求中包含系统提示词,则使用此设置拼接到用户的系统提示词前面": "Если запрос пользователя содержит системный промпт, используйте эту настройку для добавления перед системным промптом пользователя", "如果镜像为私有,请填写密码或Token": "If the image is private, please fill in the password or token", "如果镜像为私有,请填写用户名": "If the image is private, please fill in the username", - "如:大带宽批量分析图片推荐": "Например: рекомендуется для пакетного анализа изображений с большой пропускной способностью", - "如:香港线路": "Например: Гонконгская линия", "始终使用浅色主题": "Всегда использовать светлую тему", "始终使用深色主题": "Всегда использовать темную тему", "字段映射": "Сопоставление полей", @@ -1175,6 +1266,7 @@ "密钥": "Ключ", "密钥 JSON 必须包含 access_token": "JSON ключа должен содержать access_token", "密钥 JSON 必须包含 account_id": "JSON ключа должен содержать account_id", + "密钥(编辑模式下,保存的密钥不会显示)": "Токен (в режиме редактирования сохраненные токены не отображаются)", "密钥去重": "Удаление дубликатов ключей", "密钥将以Bearer方式添加到请求头中,用于验证webhook请求的合法性": "Ключ будет добавлен в заголовок запроса методом Bearer для проверки легитимности webhook-запросов", "密钥已删除": "Ключ удален", @@ -1192,7 +1284,6 @@ "密钥获取成功": "Ключ успешно получен", "密钥输入方式": "Способ ввода ключа", "密钥预览": "Предпросмотр ключа", - "密钥(编辑模式下,保存的密钥不会显示)": "Токен (в режиме редактирования сохраненные токены не отображаются)", "对于官方渠道,new-api已经内置地址,除非是第三方代理站点或者Azure的特殊接入地址,否则不需要填写": "Для официальных каналов new-api уже имеет встроенные адреса, если это не сторонние прокси-сайты или специальные адреса доступа Azure, заполнять не нужно", "对免费模型启用预消耗": "Включить предварительное списание для бесплатных моделей", "对域名启用 IP 过滤(推荐开启)": "Включить IP-фильтрацию для доменов (рекомендуется)", @@ -1217,6 +1308,10 @@ "将只保留最近 {{value}} 个日志文件,其余将被删除。": "Будут сохранены только последние {{value}} файлов журналов; остальные будут удалены.", "将大请求体临时存储到磁盘": "Временное сохранение больших тел запросов на диск", "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "Ценовая конфигурация редактируемой модели {{name}} будет применена к {{count}} выбранным моделям.", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。_one": "", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。_few": "", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。_many": "", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。_other": "", "将清除所有保存的配置并恢复默认设置,此操作不可撤销。是否继续?": "Будут очищены все сохраненные конфигурации и восстановлены настройки по умолчанию, эта операция необратима. Продолжить?", "将清除选定时间之前的所有日志": "Будут очищены все логи до выбранного времени", "将追加 2 条规则到现有规则列表。": "2 правила будут добавлены к существующему списку правил.", @@ -1231,11 +1326,11 @@ "展示价格": "Отображаемая цена", "嵌套映射:用户分组 → 使用分组 → 倍率": "Nested mapping: user group → using group → ratio", "左侧边栏个人设置": "Персональные настройки левой боковой панели", - "已为 ${count} 个渠道设置标签!": "Установлены метки для ${count} каналов!", + "已为 {{count}} 个模型设置{{type}}_one": "Установлено {{type}} для {{count}} модели", "已为 {{count}} 个模型设置{{type}}_few": "Установлено {{type}} для {{count}} моделей", "已为 {{count}} 个模型设置{{type}}_many": "Установлено {{type}} для {{count}} моделей", - "已为 {{count}} 个模型设置{{type}}_one": "Установлено {{type}} для {{count}} модели", "已为 {{count}} 个模型设置{{type}}_other": "Установлено {{type}} для {{count}} моделей", + "已为 ${count} 个渠道设置标签!": "Установлены метки для ${count} каналов!", "已从 Discovery 自动填充配置": "Конфигурация автозаполнена из Discovery", "已从 Discovery 获取配置,可继续手动修改所有字段。": "Конфигурация получена из Discovery. Все поля можно продолжить редактировать вручную.", "已作废": "Аннулировано", @@ -1250,20 +1345,24 @@ "已切换至最优倍率视图,每个模型使用其最低倍率分组": "Переключено на оптимальный вид множителей, каждая модель использует свою группу с минимальным множителем", "已初始化": "Инициализировано", "已删除": "Удалено", - "已删除 ${data} 个通道!": "Удалено ${data} каналов!", "已删除 {{count}} 个令牌!": "Удалено {{count}} токенов!", + "已删除 {{count}} 个令牌!_one": "Удалён {{count}} токен!", "已删除 {{count}} 个令牌!_few": "Удалено {{count}} токена!", "已删除 {{count}} 个令牌!_many": "Удалено {{count}} токенов!", - "已删除 {{count}} 个令牌!_one": "Удалён {{count}} токен!", "已删除 {{count}} 个令牌!_other": "Удалено {{count}} токенов!", + "已删除 {{count}} 条失效兑换码_one": "Удален {{count}} недействительный код купона", "已删除 {{count}} 条失效兑换码_few": "Удалено {{count}} недействительных кода купона", "已删除 {{count}} 条失效兑换码_many": "Удалено {{count}} недействительных кодов купонов", - "已删除 {{count}} 条失效兑换码_one": "Удален {{count}} недействительный код купона", "已删除 {{count}} 条失效兑换码_other": "Удалено {{count}} недействительных кодов купонов", + "已删除 ${data} 个通道!": "Удалено ${data} каналов!", "已删除所有禁用渠道,共计 ${data} 个": "Удалены все отключенные каналы, всего ${data}", "已删除消息及其回复": "Сообщение и его ответы удалены", "已勾选": "Выбрано", "已勾选 {{count}} 个模型": "Выбрано моделей: {{count}}", + "已勾选 {{count}} 个模型_one": "", + "已勾选 {{count}} 个模型_few": "", + "已勾选 {{count}} 个模型_many": "", + "已勾选 {{count}} 个模型_other": "", "已发起支付": "Оплата инициирована", "已发送到 Fluent": "Отправлено в Fluent", "已取消 Passkey 注册": "Регистрация Passkey отменена", @@ -1275,10 +1374,13 @@ "已填充提示模板": "Шаблон промпта заполнен", "已填充模版": "Шаблон заполнен", "已填充策略模板": "Шаблон политики заполнен", + "已处理上游模型更新:加入 {{added}} 个,删除 {{removed}} 个,本次忽略 {{ignored}} 个,当前已忽略模型 {{totalIgnored}} 个": "", "已备份": "Резервная копия создана", "已复制": "Скопировано", "已复制 ${count} 个模型": "Скопировано ${count} моделей", "已复制 ID 到剪贴板": "ID copied to clipboard", + "已复制:": "Скопировано: ", + "已复制:{{name}}": "Скопировано: {{name}}", "已复制全部数据": "Все данные скопированы", "已复制到剪切板": "Скопировано в буфер обмена", "已复制到剪贴板": "Скопировано в буфер обмена", @@ -1287,19 +1389,23 @@ "已复制模型名称": "Название модели скопировано", "已复制版本号": "Version copied", "已复制自动生成的 API Key": "Auto-generated API Key copied", - "已复制:": "Скопировано: ", - "已复制:{{name}}": "Скопировано: {{name}}", "已完成": "Completed", "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "Ценовая конфигурация модели {{name}} массово применена к {{count}} моделям", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型_one": "", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型_few": "", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型_many": "", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型_other": "", "已开启全局请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "Глобальная сквозная передача запросов включена. Встроенные возможности NewAPI, такие как переопределение параметров, перенаправление моделей и адаптация канала, будут отключены. Это не является лучшей практикой. Если из-за этого возникнут проблемы, пожалуйста, не создавайте issue.", + "已忽略模型": "", "已成功开始测试所有已启用通道,请刷新页面查看结果。": "Успешно начато тестирование всех включенных каналов, обновите страницу для просмотра результатов.", "已打开授权页面": "Страница авторизации открыта", "已打开支付页面": "Страница оплаты открыта", + "已批量处理上游模型更新:渠道 {{channels}} 个,加入 {{added}} 个,删除 {{removed}} 个,失败 {{fails}} 个": "", "已提交": "Отправлено", "已支付金额": "Amount Paid", + "已新增 {{count}} 个模型:{{list}}_one": "Добавлена {{count}} модель: {{list}}", "已新增 {{count}} 个模型:{{list}}_few": "Добавлено {{count}} модели: {{list}}", "已新增 {{count}} 个模型:{{list}}_many": "Добавлено {{count}} моделей: {{list}}", - "已新增 {{count}} 个模型:{{list}}_one": "Добавлена {{count}} модель: {{list}}", "已新增 {{count}} 个模型:{{list}}_other": "Добавлено {{count}} моделей: {{list}}", "已更新完毕所有已启用通道余额!": "Балансы всех включенных каналов обновлены!", "已有保存的配置": "Сохраненные конфигурации уже существуют", @@ -1309,12 +1415,16 @@ "已服务": "Served", "已注销": "Выход выполнен", "已添加": "Добавлено", + "已添加 {{count}} 个模板_one": "Добавлен {{count}} шаблон", "已添加 {{count}} 个模板_few": "Добавлено {{count}} шаблона", "已添加 {{count}} 个模板_many": "Добавлено {{count}} шаблонов", - "已添加 {{count}} 个模板_one": "Добавлен {{count}} шаблон", "已添加 {{count}} 个模板_other": "Добавлено {{count}} шаблонов", "已添加到白名单": "Добавлено в белый список", "已清理 {{count}} 个日志文件,释放 {{size}}": "Очищено {{count}} файлов журналов, освобождено {{size}}", + "已清理 {{count}} 个日志文件,释放 {{size}}_one": "", + "已清理 {{count}} 个日志文件,释放 {{size}}_few": "", + "已清理 {{count}} 个日志文件,释放 {{size}}_many": "", + "已清理 {{count}} 个日志文件,释放 {{size}}_other": "", "已清空": "Очищено", "已清空测试结果": "Результаты тестов очищены", "已生成授权凭据": "Учётные данные авторизации сгенерированы", @@ -1333,15 +1443,16 @@ "已达到购买上限": "Достигнут лимит покупок", "已过期": "Просрочено", "已运行时间": "Uptime", - "已选择 ${count} 个渠道": "Выбрано ${count} каналов", + "已选择 {{count}} 个模型_one": "Выбрана {{count}} модель", "已选择 {{count}} 个模型_few": "Выбрано {{count}} модели", "已选择 {{count}} 个模型_many": "Выбрано {{count}} моделей", - "已选择 {{count}} 个模型_one": "Выбрана {{count}} модель", "已选择 {{count}} 个模型_other": "Выбрано {{count}} моделей", "已选择 {{selected}} / {{total}}": "Выбрано {{selected}} / {{total}}", + "已选择 ${count} 个渠道": "Выбрано ${count} каналов", "已重置为默认配置": "Сброшено на конфигурацию по умолчанию", "已销毁": "Destroyed", "币种": "Валюта", + "帐号信息": "", "常用上下文 Key(用于 context_*)": "Часто используемые ключи контекста (для context_*)", "常见问答": "Часто задаваемые вопросы", "常见问答管理,为用户提供常见问题的答案(最多50个,前端显示最新20条)": "Управление часто задаваемыми вопросами, предоставление ответов на распространенные вопросы пользователям (максимум 50, на интерфейсе отображаются последние 20)", @@ -1360,19 +1471,16 @@ "延长容器时长将会产生额外费用,请确认您有足够的账户余额。": "Extending container duration will incur additional charges, please ensure you have sufficient account balance.", "延长操作一旦确认无法撤销,费用将立即扣除。": "Once confirmed, the extension operation cannot be undone, and charges will be deducted immediately.", "延长时长": "Extension Duration", + "延长时长(小时)": "Extension Duration (hours)", "延长时长不能超过720小时(30天)": "Extension duration cannot exceed 720 hours (30 days)", "延长时长失败": "Failed to extend duration", "延长时长至少为1小时": "Extension duration must be at least 1 hour", - "延长时长(小时)": "Extension Duration (hours)", "建立连接时发生错误": "Ошибка при установке соединения", "建议在生产环境中使用 MySQL 或 PostgreSQL 数据库,或确保 SQLite 数据库文件已映射到宿主机的持久化存储。": "Рекомендуется использовать базы данных MySQL или PostgreSQL в производственной среде, или убедиться, что файл базы данных SQLite сопоставлен с постоянным хранилищем хоста.", "开": "Вкл", "开启「默认使用 auto 分组」后,新建令牌和初始令牌都会自动设为 auto。": "After enabling \"Default to auto group\", new tokens and initial tokens will be set to auto.", "开启之后会清除用户提示词中的": "После включения будет очищено в промптах пользователя:", "开启之后将上游地址替换为服务器地址": "После включения адреса восходящих каналов будут заменены на адрес сервера", - "开启后不限制:必须设置模型倍率": "После включения без ограничений: необходимо установить множители моделей", - "开启后创建令牌默认选择auto分组,初始令牌也将设为auto": "When enabled, new tokens default to auto group, initial tokens are also set to auto", - "开启后未登录用户无法访问模型广场": "После включения незарегистрированные пользователи не смогут получить доступ к площади моделей", "开启后,using_group 会参与 cache key(不同分组隔离)。": "При включении using_group будет частью ключа кэша (изоляция по группам).", "开启后,仅\"消费\"和\"错误\"日志将记录您的客户端IP地址": "После включения, только логи \"потребление\" и \"ошибки\" будут записывать IP-адрес вашего клиента", "开启后,对免费模型(倍率为0,或者价格为0)的模型也会预消耗额度": "После включения бесплатные модели (коэффициент 0 или цена 0) тоже будут предварительно расходовать квоту", @@ -1384,14 +1492,21 @@ "开启后,规则名称会参与 cache key(不同规则隔离)。": "При включении имя правила будет частью ключа кэша (изоляция по правилам).", "开启后,该渠道请求 Claude 时将强制追加 ?beta=true(无需客户端手动传参)": "При включении запросы к Claude через этот канал будут принудительно дополнены ?beta=true (клиенту не нужно передавать этот параметр вручную)", "开启后,违规请求将额外扣费。": "При включении за нарушающие запросы будет взиматься дополнительная плата.", + "开启后不限制:必须设置模型倍率": "После включения без ограничений: необходимо установить множители моделей", + "开启后创建令牌默认选择auto分组,初始令牌也将设为auto": "When enabled, new tokens default to auto group, initial tokens are also set to auto", + "开启后未登录用户无法访问模型广场": "После включения незарегистрированные пользователи не смогут получить доступ к площади моделей", + "开启后检测到新增模型会自动加入当前渠道模型列表": "", + "开启后由后端定时任务检测该渠道上游模型变化": "", "开启批量操作": "Включить пакетные операции", "开始": "Начало", "开始同步": "Начать синхронизацию", "开始批量测试 ${count} 个模型,已清空上次结果...": "Начало пакетного тестирования ${count} моделей, предыдущие результаты очищены...", "开始时间": "Время начала", + "异常": "", "异步任务退款": "Возврат асинхронной задачи", "张图片": "изображений", "弱变换": "Слабое преобразование", + "强制使用 AUTH LOGIN": "Принудительно AUTH LOGIN", "强制将响应格式化为 OpenAI 标准格式(只适用于OpenAI渠道类型)": "Принудительно форматировать ответ в стандартный формат OpenAI (только для типов каналов OpenAI)", "强制格式化": "Принудительное форматирование", "强制要求": "Обязательное требование", @@ -1429,6 +1544,7 @@ "当前设置类型: ": "Текущий тип настроек: ", "当前跟随系统": "Следовать системе", "当前配置无法连接到 io.net。": "Unable to connect to io.net with current configuration.", + "当前额度": "Текущая квота", "当某个分组的用户使用另一个分组的令牌时,可设置特殊倍率覆盖基础倍率。例如:vip 分组的用户使用 default 分组时倍率为 0.5": "When a user in one group uses a token from another group, a special ratio can override the base ratio. E.g., vip users using default group get ratio 0.5", "当模型没有设置价格时仍接受调用,仅当您信任该网站时使用,可能会产生高额费用": "Принимать вызовы моделей без установленной цены, использовать только если вы доверяете сайту, могут возникнуть высокие расходы", "当运行通道全部测试时,超过此时间将自动禁用通道": "При тестировании всех работающих каналов, превышение этого времени автоматически отключит канал", @@ -1508,10 +1624,13 @@ "批量删除失败": "Пакетное удаление не удалось", "批量删除成功": "Batch deletion successful", "批量删除模型": "Пакетное удаление моделей", + "批量处理失败": "", "批量应用当前模型价格": "Массово применить цену текущей модели", "批量操作": "Пакетные операции", "批量操作失败": "Batch operation failed", "批量操作完成: {{success}}个成功, {{failed}}个失败": "Batch operation completed: {{success}} succeeded, {{failed}} failed", + "批量检测失败": "", + "批量检测完成:渠道 {{channels}} 个,新增 {{add}} 个,删除 {{remove}} 个,失败 {{fails}} 个": "", "批量测试${count}个模型": "Пакетное тестирование ${count} моделей", "批量测试完成!成功: ${success}, 失败: ${fail}, 总计: ${total}": "Пакетное тестирование завершено! Успешно: ${success}, Неудачно: ${fail}, Всего: ${total}", "批量测试已停止": "Пакетное тестирование остановлено", @@ -1533,28 +1652,29 @@ "按倍率设置": "Настроить по множителю", "按次": "За запрос", "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "За запрос {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "按次计费": "Оплата за запрос", "按次:{{symbol}}{{price}}": "За запрос: {{symbol}}{{price}}", "按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "За запрос: {{symbol}}{{price}} * {{ratioType}}: {{ratio}} = {{symbol}}{{total}}", + "按次计费": "Оплата за запрос", "按照如下格式输入:AccessKey|SecretAccessKey|Region": "Введите в формате: AccessKey|SecretAccessKey|Region", "按量计费": "Оплата по объему", "按量计费下需要先填写输入价格,才能保存其它价格项。": "При тарификации по объему сначала нужно указать входную цену, чтобы сохранить остальные ценовые поля.", "按顺序替换content中的变量占位符": "Последовательно заменять переменные-заполнители в content", "换脸": "Замена лица", - "授权失败": "Авторизация не удалась", "授权,需在遵守": "Авторизация, необходимо соблюдать", + "授权失败": "Авторизация не удалась", "排序": "Порядок", "排队中": "В очереди", "接受未设置价格模型": "Принимать модели без установленной цены", "接口凭证": "Учетные данные интерфейса", "接口密钥已过期": "API key has expired", + "接收上游模型更新通知": "", "控制台": "Консоль", "控制台区域": "Область консоли", "控制输出的随机性和创造性": "Управляет случайностью и креативностью вывода", "控制顶栏模块显示状态,全局生效": "Управление состоянием отображения модулей верхней панели, действует глобально", "推荐": "Рекомендуется", - "推荐使用(用户可选)": "Рекомендуется использовать (по выбору пользователя)", "推荐:用户可以选择是否使用指纹等验证": "Рекомендуется: пользователи могут выбирать, использовать ли проверку по отпечатку пальца и другие методы", + "推荐使用(用户可选)": "Рекомендуется использовать (по выбору пользователя)", "描述": "Описание", "提交": "Отправить", "提交时间": "Время отправки", @@ -1564,14 +1684,14 @@ "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "Промпт {{input}} токенов / 1M токенов * {{symbol}}{{price}}", "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Ввод {{input}} токенов / 1M токенов * {{symbol}}{{price}} + Вывод {{completion}} токенов / 1M токенов * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Ввод {{nonCacheInput}} токенов / 1M токенов * {{symbol}}{{price}} + Кэш {{cacheInput}} токенов / 1M токенов * {{symbol}}{{cachePrice}} + Создание кэша {{cacheCreationInput}} токенов / 1M токенов * {{symbol}}{{cacheCreationPrice}} + Вывод {{completion}} токенов / 1M токенов * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "提示价格:{{symbol}}{{price}} / 1M tokens": "Цена промпта: {{symbol}}{{price}} / 1M токенов", - "提示缓存倍率": "Коэффициент кэша промптов", "提示:如需备份数据,只需复制上述目录即可": "Промпт: для резервного копирования данных просто скопируйте указанный выше каталог", "提示:此处配置仅用于控制「模型广场」对用户的展示效果,不会影响模型的实际调用与路由。若需配置真实调用行为,请前往「渠道管理」进行设置。": "Примечание: эта настройка влияет только на отображение моделей в «Маркетплейсе моделей» и не влияет на фактический вызов или маршрутизацию. Чтобы настроить реальное поведение вызовов, перейдите в «Управление каналами».", "提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。": "Примечание: сопоставление эндпоинтов используется только для отображения в «Маркетплейсе моделей» и не влияет на реальный вызов. Чтобы настроить реальное поведение вызовов, перейдите в «Управление каналами».", "提示:该功能为测试版,未来配置结构与功能行为可能发生变更,请勿在生产环境使用。": "Примечание: это бета-функция. Структура конфигурации и поведение могут измениться в будущем. Не используйте в продакшене.", "提示:语言偏好会同步到您登录的所有设备,并影响API返回的错误消息语言。": "Подсказка: Языковые настройки синхронизируются на всех ваших устройствах и влияют на язык сообщений об ошибках API.", "提示:链接中的{key}将被替换为API密钥,{address}将被替换为服务器地址": "Промпт: {key} в ссылке будет заменен на API-ключ, {address} будет заменен на адрес сервера", + "提示价格:{{symbol}}{{price}} / 1M tokens": "Цена промпта: {{symbol}}{{price}} / 1M токенов", + "提示缓存倍率": "Коэффициент кэша промптов", "搜索供应商": "Поиск поставщиков", "搜索关键字": "Поиск по ключевым словам", "搜索失败": "Search failed", @@ -1585,6 +1705,7 @@ "搜索模型失败": "Поиск моделей не удался", "搜索渠道名称或地址": "Поиск по названию или адресу канала", "搜索聊天应用名称": "Поиск по названию чат-приложения", + "搜索规则(描述 / 类型 / 路径 / 来源 / 目标)": "", "搜索规则(类型 / 路径 / 来源 / 目标)": "Поиск правил (тип / путь / источник / цель)", "搜索部署名称": "Search deployment name", "操作": "Операции", @@ -1598,13 +1719,16 @@ "支付": "Оплатить", "支付地址": "Адрес оплаты", "支付失败": "Оплата не удалась", + "支付完成后用户跳转的页面,留空则自动使用 服务器地址 + /console/topup": "", "支付宝": "Alipay", "支付方式": "Способ оплаты", "支付方式名称": "Название метода оплаты", + "支付方式名称不能为空": "", "支付方式类型": "Тип метода оплаты", "支付渠道": "Платежные каналы", "支付设置": "Оплата", "支付请求失败": "Запрос на оплату не удался", + "支付返回地址": "", "支付金额": "Сумма оплаты", "支持 Ctrl+V 粘贴图片": "Поддержка Ctrl+V для вставки изображения", "支持6位TOTP验证码或8位备用码,可到`个人设置-安全设置-两步验证设置`配置或查看。": "Поддерживает 6-значные TOTP коды подтверждения или 8-значные резервные коды, можно настроить или просмотреть в `Личные настройки-Настройки безопасности-Настройки двухфакторной аутентификации`.", @@ -1621,6 +1745,7 @@ "支持拉取 Ollama 官方模型库中的所有模型,拉取过程可能需要几分钟时间": "Supports pulling all models from the Ollama official model library, the pulling process may take a few minutes", "支持搜索用户的 ID、用户名、显示名称和邮箱地址": "Поддерживает поиск по ID пользователя, имени пользователя, отображаемому имени и адресу электронной почты", "支持的图像模型": "Поддерживаемые модели изображений", + "支持精确匹配;使用 regex: 开头可按正则匹配。": "", "支持通配符格式,如:example.com, *.api.example.com": "Поддерживает формат с подстановочными знаками, например: example.com, *.api.example.com", "支持逻辑 and/or 与嵌套 groups;操作符支持 eq/ne/gt/gte/lt/lte/in/not_in/contains/exists": "Поддерживает логику and/or с вложенными группами; операторы: eq/ne/gt/gte/lt/lte/in/not_in/contains/exists", "收益": "Доход", @@ -1628,6 +1753,7 @@ "收起": "Свернуть", "收起侧边栏": "Свернуть боковую панель", "收起内容": "Свернуть содержимое", + "收起原生额度输入": "Скрыть ввод в исходных единицах", "放大": "Увеличить", "放大编辑": "Увеличить и редактировать", "敏感信息不会发送到前端显示": "Конфиденциальная информация не будет отправляться для отображения на frontend", @@ -1644,9 +1770,9 @@ "数据看板默认时间粒度": "Временная гранулярность панели данных по умолчанию", "数据管理和日志查看": "Управление данными и просмотр журналов", "文件上传": "Загрузка файла", + "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Поиск файлов: {{count}} / 1K * цена за единицу {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "文件搜索价格:{{symbol}}{{price}} / 1K 次": "Цена поиска файлов: {{symbol}}{{price}} / 1K запросов", "文件搜索调用 {{fileSearchCallCount}} 次": "Поиск файлов вызван {{fileSearchCallCount}} раз", - "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Поиск файлов: {{count}} / 1K * цена за единицу {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "Текстовый ввод {{input}} токенов / 1M токенов * {{symbol}}{{price}} + Текстовый вывод {{completion}} токенов / 1M токенов * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Текстовый промпт {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + Текстовое дополнение {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + Аудио промпт {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + Аудио дополнение {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "文字提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "Текстовый ввод {{nonCacheInput}} токенов / 1M токенов * {{symbol}}{{price}} + Кэш {{cacheInput}} токенов / 1M токенов * {{symbol}}{{cachePrice}} + Текстовый вывод {{completion}} токенов / 1M токенов * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", @@ -1661,8 +1787,11 @@ "新增 Key 来源": "Добавить источник ключа", "新增供应商": "Добавить поставщика", "新增失败": "Не удалось добавить", + "新增已选 {{selected}} / {{total}}": "", "新增成功": "Успешно добавлено", + "新增支付方式": "", "新增条件": "Добавить условие", + "新增模型": "", "新增规则": "Добавить правило", "新增订阅": "Добавить подписку", "新密码": "Новый пароль", @@ -1673,9 +1802,9 @@ "新建容器部署": "Create Container Deployment", "新建数量": "Количество для создания", "新建组": "Создать группу", - "新格式模板": "Шаблон нового формата", "新格式(支持条件判断与json自定义):": "Новый формат (поддерживает условные суждения и пользовательскую настройку json):", "新格式(规则 + 条件)": "Новый формат (Правила + Условия)", + "新格式模板": "Шаблон нового формата", "新版本": "Новая версия", "新用户使用邀请码奖励额度": "Квота вознаграждения для новых пользователей, использующих приглашение", "新用户初始额度": "Начальная квота для новых пользователей", @@ -1712,10 +1841,10 @@ "日志类型": "Тип журнала", "日志设置": "Настройки журнала", "日志详情": "Детали журнала", - "旧格式必须是 JSON 对象": "Устаревший формат должен быть JSON-объектом", - "旧格式模板": "Шаблон старого формата", "旧格式(JSON 对象)": "Устаревший формат (JSON-объект)", "旧格式(直接覆盖):": "Старый формат (прямая перезапись):", + "旧格式必须是 JSON 对象": "Устаревший формат должен быть JSON-объектом", + "旧格式模板": "Шаблон старого формата", "旧的备用码已失效,请保存新的备用码": "Старые резервные коды больше не действительны, пожалуйста, сохраните новые резервные коды", "早上好": "Доброе утро", "时间": "Время", @@ -1728,16 +1857,18 @@ "是否为企业账户": "Является ли корпоративным аккаунтом", "是否同时重置对话消息?选择\"是\"将清空所有对话记录并恢复默认示例;选择\"否\"将保留当前对话记录。": "Одновременно сбросить сообщения диалога? Выбор \"Да\" очистит все записи диалогов и восстановит примеры по умолчанию; выбор \"Нет\" сохранит текущие записи диалогов.", "是否将该订单标记为成功并为用户入账?": "Отметить этот заказ как успешный и зачислить средства пользователю?", + "是否检测上游模型更新": "", "是否确认充值?": "Подтвердить пополнение?", + "是否自动同步上游模型更新": "", "是否自动禁用": "Автоматически отключать", "是否要求指纹/面容等生物识别": "Требовать биометрическую аутентификацию (отпечаток пальца/лицо и т.д.)", "显示倍率": "Отображать коэффициент", + "显示最新20条": "Отображать последние 20 записей", "显示名称": "Отображаемое имя", "显示名称字段(可选)": "Поле отображаемого имени (необязательно)", "显示完整内容": "Отображать полное содержимое", "显示操作项": "Отображать элементы операций", "显示更多": "Отображать больше", - "显示最新20条": "Отображать последние 20 записей", "显示第": "Отображать", "显示设置": "Настройки отображения", "显示调试": "Отображать отладку", @@ -1758,6 +1889,7 @@ "暂无公告": "Нет объявлений", "暂无分组,点击下方按钮添加": "No groups yet. Click the button below to add one.", "暂无匹配模型": "Нет соответствующих моделей", + "暂无参数覆盖记录": "", "暂无可复制 JSON": "Нет доступного JSON для копирования", "暂无可复制的版本信息": "No version information to copy", "暂无可展示数据": "Нет данных для отображения", @@ -1771,6 +1903,7 @@ "暂无已绑定项": "Нет привязанных элементов", "暂无常见问答": "Нет часто задаваемых вопросов", "暂无成功模型": "Нет успешных моделей", + "暂无支付方式,点击上方按钮新增": "", "暂无数据": "Нет данных", "暂无数据,点击下方按钮添加键值对": "Нет данных, нажмите кнопку ниже, чтобы добавить пару ключ-значение", "暂无日志": "No logs", @@ -1799,6 +1932,7 @@ "更新": "Обновить", "更新 Creem 设置": "Обновить настройки Creem", "更新 Stripe 设置": "Обновить настройки Stripe", + "更新 Waffo 设置": "", "更新SSRF防护设置": "Обновить настройки защиты SSRF", "更新Worker设置": "Обновить настройки Worker", "更新令牌信息": "Обновить информацию о токене", @@ -1821,18 +1955,7 @@ "更新配置后,容器可能需要重启以应用新的设置。请确保您了解这些更改的影响。": "After updating the configuration, the container may need to restart to apply the new settings. Please ensure you understand the impact of these changes.", "更新配置失败": "Failed to update configuration", "更新预填组": "Обновить предварительно заполненную группу", - "最低": "Минимум", - "最低充值美元数量": "Минимальная сумма пополнения в долларах", - "最后使用时间": "Время последнего использования", - "最后更新": "Last Updated", - "最后请求": "Последний запрос", - "最大GPU数量": "Max Number of GPUs", - "最大可用": "Max Available", - "最大条目数": "Макс. кол-во записей", - "最终抵扣": "Итоговое списание", - "最近一次": "Последний", - "最近事件": "Recent Events", - "最高优先级": "highest priority", + "替换": "", "月": "мес.", "有 Reasoning": "Есть рассуждение", "有序字符串数组": "Ordered string array", @@ -1842,6 +1965,7 @@ "有效期设置": "Настройки срока действия", "服务可用性": "Доступность сервиса", "服务商": "Service Provider", + "服务器IP": "IP сервера", "服务器地址": "Адрес сервера", "服务器日志功能未启用(未配置日志目录)": "Ведение журнала сервера не включено (каталог журналов не настроен)", "服务器日志管理": "Управление журналами сервера", @@ -1888,8 +2012,8 @@ "本地数据存储": "Локальное хранение данных", "本地计费": "Локальная тарификация", "本月获得": "В этом месяце", - "本设备内置": "Встроенное в это устройство", "本设备:手机指纹/面容,外接:USB安全密钥": "Это устройство: отпечаток пальца/лицо телефона, внешнее: USB-ключ безопасности", + "本设备内置": "Встроенное в это устройство", "本项目根据": "Этот проект основан на", "机密环境变量": "Secret Environment Variables", "机密环境变量将被加密存储,适用于存储密码、API密钥等敏感信息。": "Secret environment variables will be stored encrypted, suitable for storing passwords, API keys and other sensitive information.", @@ -1898,19 +2022,21 @@ "权限设置": "Настройки прав доступа", "条": "запись", "条 - 第": "запись -", + "条,共": "записей, всего", "条件取反": "Инвертировать условие", "条件数": "Условия", "条件规则": "Правила условий", "条件项设置": "Настройки элементов условий", "条日志已清理!": "записей журнала очищено!", "条规则": "rules", - "条,共": "записей, всего", "来源": "Источник", "来源于 IO.NET 部署": "From IO.NET Deployment", "来源端点": "Конечная точка источника", "来自模型重定向,尚未加入模型列表": "Из перенаправления модели, ещё не добавлен в список моделей", "某些配置更改可能需要几分钟才能生效。": "Some configuration changes may take a few minutes to take effect.", "查看": "Просмотр", + "查看 Codex 帐号信息与用量": "", + "查看 JSON 示例": "", "查看关联部署": "View Associated Deployment", "查看图片": "Просмотр изображения", "查看密钥": "Просмотр ключа", @@ -1942,10 +2068,13 @@ "格式示例:": "Пример формата: ", "格式错误": "Недействительный формат", "检查更新": "Проверить обновления", + "检测全部渠道上游更新": "", "检测到 FluentRead(流畅阅读)": "Обнаружен FluentRead (плавное чтение)", "检测到剪贴板中的连接信息": "В буфере обмена обнаружены данные подключения", "检测到多个密钥,您可以单独复制每个密钥,或点击复制全部获取完整内容。": "Обнаружено несколько ключей, вы можете скопировать каждый ключ отдельно или нажать \"Копировать все\" для получения полного содержимого.", "检测到该消息后有AI回复,是否删除后续回复并重新生成?": "Обнаружен ответ ИИ после этого сообщения, удалить ли последующие ответы и сгенерировать заново?", + "检测失败": "", + "检测完成:新增 {{add}} 个,删除 {{remove}} 个": "", "检测必须等待绘图成功才能进行放大等操作": "Обнаружение должно ждать успешного вывода рисования для выполнения операций увеличения и т.д.", "概览": "Overview", "模型": "Модель", @@ -1961,6 +2090,7 @@ "模型价格:{{symbol}}{{price}}": "Цена модели: {{symbol}}{{price}}", "模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Цена модели: {{symbol}}{{price}} * {{ratioType}}: {{ratio}} = {{symbol}}{{total}}", "模型价格:{{symbol}}{{price}} / 次": "Цена модели: {{symbol}}{{price}} / запрос", + "模型价格未配置": "Цена модели не настроена", "模型倍率": "Коэффициент модели", "模型倍率 {{modelRatio}}": "Коэффициент модели {{modelRatio}}", "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "Коэффициент модели {{modelRatio}}, коэффициент кэша {{cacheRatio}}, коэффициент вывода {{completionRatio}}, {{ratioType}} {{ratio}}", @@ -1985,8 +2115,8 @@ "模型名称已存在": "Название модели уже существует", "模型固定价格": "Фиксированная цена модели", "模型图标": "Иконка модели", - "模型定价设置": "Model Pricing", "模型定价,需要登录访问": "Ценообразование моделей, требуется вход для доступа", + "模型定价设置": "Model Pricing", "模型广场": "Площадка моделей", "模型拉取失败: {{error}}": "Failed to pull model: {{error}}", "模型排行": "Рейтинг моделей", @@ -1996,8 +2126,8 @@ "模型更新成功!": "Модель успешно обновлена!", "模型未加入列表,可能无法调用": "Модель не добавлена в список, вызовы могут не работать", "模型正则": "Regex модели", - "模型正则不能为空": "Regex модели не может быть пустым", "模型正则(每行一个)": "Regex модели (по одному в строке)", + "模型正则不能为空": "Regex модели не может быть пустым", "模型消耗分布": "Распределение потребления моделей", "模型消耗趋势": "Тенденции потребления моделей", "模型版本": "Версия модели", @@ -2028,17 +2158,24 @@ "次": "запрос", "欢迎使用,请完成以下设置以开始使用系统": "Добро пожаловать, пожалуйста, выполните следующие настройки, чтобы начать использовать систему", "欧元": "Евро", + "正则替换": "", "正在加载可用部署位置...": "Loading available deployment locations...", "正在加载签到状态...": "Загрузка статуса регистрации...", + "正在处理,请稍候": "", "正在处理大内容...": "Обработка большого содержимого...", + "正在批量处理,请稍候": "", + "正在批量检测,请稍候": "", "正在提交": "Отправка...", "正在构造请求体预览...": "Создание предварительного просмотра тела запроса...", "正在检查 io.net 连接...": "Checking io.net connection...", + "正在检测,请稍候": "", "正在测试第 ${current} - ${end} 个模型 (共 ${total} 个)": "Тестирование моделей с ${current} по ${end} (всего ${total})", "正在跟随最新日志": "Following latest logs", "正在跳转 GitHub...": "Перенаправление на GitHub...", "正在跳转...": "Переход...", + "正常": "", "正常情况下,令牌的计费倍率由令牌所选的分组决定。特殊倍率可以根据「用户所在分组」进一步覆盖这个倍率。": "Normally, a token's billing ratio is determined by its selected group. Special ratios can override this based on the user's group.", + "正数为增加,负数为减少": "Положительное для увеличения, отрицательное для уменьшения", "此代理仅用于图片请求转发,Webhook通知发送等,AI API请求仍然由服务器直接发出,可在渠道设置中单独配置代理": "Этот прокси используется только для пересылки изображений, отправки уведомлений Webhook и т.д., AI API запросы по-прежнему отправляются напрямую сервером, прокси можно настроить отдельно в настройках канала", "此修改将不可逆": "Это изменение будет необратимым", "此操作不可恢复,请仔细确认时间后再操作!": "Эта операция необратима, пожалуйста, внимательно подтвердите время перед выполнением!", @@ -2065,6 +2202,7 @@ "此项可选,用于覆盖请求参数。不支持覆盖 stream 参数": "Этот параметр необязательный, используется для переопределения параметров запроса. Не поддерживает переопределение параметра stream", "此项可选,用于覆盖请求头参数": "Этот параметр необязательный, используется для переопределения параметров заголовка запроса", "此项可选,用于通过自定义API地址来进行 API 调用,末尾不要带/v1和/": "Этот параметр необязательный, используется для выполнения API вызовов через пользовательский адрес API, в конце не должно быть /v1 и /", + "每个充值单位对应的 USD 金额,默认 1.0": "", "每个分组代表一个价格档位。管理员创建分组后,可以选择哪些档位对用户开放自选。": "Each group represents a pricing tier. After creating groups, admins can choose which tiers are open for user self-selection.", "每个用户最多可创建的令牌数量,默认 1000,设置过大可能会影响性能": "Максимальное количество токенов, которое может создать каждый пользователь, по умолчанию 1000. Слишком большое значение может повлиять на производительность", "每周": "Еженедельно", @@ -2080,7 +2218,9 @@ "永久删除您的两步验证设置": "Окончательно удалить настройки двухфакторной аутентификации", "永久删除所有备用码(包括未使用的)": "Окончательно удалить все резервные коды (включая неиспользованные)", "汇率": "Обменный курс", + "沙盒模式": "", "沙盒环境 RSA 私钥 Base64 (PKCS#8 DER)": "RSA закрытый ключ Base64 (PKCS#8 DER) песочницы", + "沙盒环境 Waffo API 密钥": "", "沙盒环境 Waffo 公钥 Base64 (X.509 DER)": "Открытый ключ Waffo Base64 (X.509 DER) песочницы", "没有匹配的字段": "Нет совпадающих полей", "没有匹配的日志条目": "No matching log entries", @@ -2097,8 +2237,8 @@ "注册": "Регистрация", "注册 Passkey": "Регистрация Passkey", "注意": "Внимание", - "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "Внимание: это не Chat API, обязательно укажите правильный адрес API, иначе это может привести к невозможности использования", "注意:JSON中重复的键只会保留最后一个同名键的值": "Внимание: в JSON повторяющиеся ключи сохранят только значение последнего ключа с тем же именем", + "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "Внимание: это не Chat API, обязательно укажите правильный адрес API, иначе это может привести к невозможности использования", "注销": "Выйти", "注销成功!": "Выход выполнен успешно!", "活跃文件": "Активные файлы", @@ -2107,7 +2247,9 @@ "流式": "Стриминг", "流式响应完成": "Поток завершён", "流式输出": "Потоковый вывод", + "流状态": "", "流量端口": "Traffic Port", + "流错误详情": "", "浅色": "Светлая", "浅色模式": "Светлый режим", "测活": "Health Check", @@ -2161,14 +2303,6 @@ "添加键值对": "Добавить пару ключ-значение", "添加问答": "Добавить вопрос-ответ", "添加额度": "Добавить лимит", - "减少": "Уменьшить", - "覆盖": "Заменить", - "调整额度": "Скорректировать квоту", - "调整额度成功": "Квота успешно скорректирована", - "当前额度": "Текущая квота", - "变更": "Изменение", - "预计结果": "Ожидаемый результат", - "正数为增加,负数为减少": "Положительное для увеличения, отрицательное для уменьшения", "清理不活跃缓存": "Очистить неактивный кэш", "清理失败": "Ошибка очистки", "清理方式": "Режим очистки", @@ -2177,6 +2311,7 @@ "清空全部缓存": "Очистить весь кэш", "清空该规则缓存": "Очистить кэш этого правила", "清空重定向": "Очистить перенаправление", + "清除": "", "清除历史日志": "Очистить историю логов", "清除失效兑换码": "Очистить недействительные коды обмена", "清除所有模型": "Очистить все модели", @@ -2184,8 +2319,8 @@ "渠道 ID": "ID канала", "渠道ID,名称,密钥,API地址": "ID Канала, имя, Токен, адрес API", "渠道亲和性": "Аффинитет канала", - "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "Аффинити канала повторно использует последний успешный канал на основе ключей, извлечённых из контекста запроса или JSON body.", "渠道亲和性:上游缓存命中": "Аффинити канала: попадание в кэш вышестоящего", + "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "Аффинити канала повторно использует последний успешный канал на основе ключей, извлечённых из контекста запроса или JSON body.", "渠道优先级": "Приоритет канала", "渠道信息": "Информация о канале", "渠道创建成功!": "Канал создан успешно!", @@ -2213,6 +2348,8 @@ "点击\"确认延长\"后将立即扣除费用并延长容器运行时间": "After clicking \"Confirm Extension\", the fee will be deducted immediately and the container runtime will be extended", "点击上传文件或拖拽文件到这里": "Нажмите для загрузки файла или перетащите файл сюда", "点击下方按钮通过 Telegram 完成绑定": "Нажмите кнопку ниже для вывода привязки через Telegram", + "点击处理删除模型": "", + "点击处理新增模型": "", "点击复制ID": "Click to copy ID", "点击复制模型名称": "Нажмите для копирования имени модели", "点击查看差异": "Нажмите для просмотра различий", @@ -2231,6 +2368,7 @@ "状态页面Slug": "Slug страницы статуса", "环境变量": "Environment Variables", "生产环境 RSA 私钥 Base64 (PKCS#8 DER)": "RSA закрытый ключ Base64 (PKCS#8 DER) производственной среды", + "生产环境 Waffo API 密钥": "", "生产环境 Waffo 公钥 Base64 (X.509 DER)": "Открытый ключ Waffo Base64 (X.509 DER) производственной среды", "生成令牌": "Сгенерировать токен", "生成并填入": "Сгенерировать и заполнить", @@ -2262,6 +2400,7 @@ "用户信息": "Информация о пользователе", "用户信息更新成功!": "Информация о пользователе обновлена успешно!", "用户信息缺失": "Информация о пользователе отсутствует", + "用户最大令牌数量": "Максимальное количество токенов на пользователя", "用户分组": "Группы пользователей", "用户分组和额度管理": "Управление группами пользователей и лимитами", "用户分组的联动作用": "How user group affects other features", @@ -2278,9 +2417,9 @@ "用户名字段(可选)": "Поле имени пользователя (необязательно)", "用户名或邮箱": "Имя пользователя или email", "用户名称": "Имя пользователя", + "用户在充值页面看到的支付方式名称,例如:Credit Card": "", "用户控制面板,管理账户": "Панель управления пользователя, управление аккаунтом", "用户新建令牌时可选的分组,格式为 JSON 字符串,例如:{\"vip\": \"VIP 用户\", \"test\": \"测试\"},表示用户可以选择 vip 分组和 test 分组": "Группы, доступные для выбора при создании токена пользователем, формат JSON строки, например: {\"vip\": \"VIP пользователь\", \"test\": \"тест\"}, означает, что пользователь может выбрать группу vip и группу test", - "用户最大令牌数量": "Максимальное количество токенов на пользователя", "用户每周期最多请求完成次数": "Максимальное количество выполненных запросов пользователя за период", "用户每周期最多请求次数": "Максимальное количество запросов пользователя за период", "用户注册时看到的网站名称,比如'我的网站'": "Название сайта, которое видят пользователи при регистрации, например 'Мой сайт'", @@ -2301,6 +2440,7 @@ "留空则使用账号绑定的邮箱": "Если оставить пустым, будет использован email, привязанный к аккаунту", "留空则使用默认端点;支持 {path, method}": "Если оставить пустым, будет использоваться конечная точка по умолчанию; поддерживает {path, method}", "留空则保持原有密钥": "Оставьте пустым для сохранения существующего ключа", + "留空则自动使用 服务器地址 + /api/waffo/webhook": "", "留空则默认使用服务器地址,注意不能携带http://或者https://": "Если оставить пустым, по умолчанию будет использоваться адрес сервера, обратите внимание, что нельзя указывать http:// или https://", "登 录": "ВОЙТИ", "登录": "Войти", @@ -2327,6 +2467,7 @@ "硬件类型": "Hardware Type", "硬件配置": "Hardware Configuration", "确定": "Подтвердить", + "确定?": "Подтвердить?", "确定删除此组?": "Удалить эту группу?", "确定导入": "Подтвердить импорт", "确定是否要修复数据库一致性?": "Подтвердить, нужно ли восстановить согласованность базы данных?", @@ -2339,18 +2480,19 @@ "确定是否要复制此渠道?": "Подтвердить, нужно ли скопировать этот канал?", "确定是否要注销此用户?": "Подтвердить, нужно ли деактивировать этого пользователя?", "确定清除所有失效兑换码?": "Подтвердить очистку всех недействительных кодов купонов?", + "确定要仅检测全部渠道上游模型更新吗?(不执行新增/删除)": "", "确定要修改所有子渠道优先级为 ": "Подтвердить изменение приоритета всех дочерних каналов на ", "确定要修改所有子渠道权重为 ": "Подтвердить изменение веса всех дочерних каналов на ", "确定要充值 $": "Подтвердить пополнение на $", "确定要删除供应商 \"{{name}}\" 吗?此操作不可撤销。": "Подтвердить удаление поставщика \"{{name}}\"? Это действие нельзя отменить.", "确定要删除所有已自动禁用的密钥吗?": "Подтвердить удаление всех автоматически отключенных ключей?", + "确定要删除所选的 {{count}} 个令牌吗?_one": "Подтвердить удаление выбранного {{count}} токена?", "确定要删除所选的 {{count}} 个令牌吗?_few": "Подтвердить удаление выбранных {{count}} токенов?", "确定要删除所选的 {{count}} 个令牌吗?_many": "Подтвердить удаление выбранных {{count}} токенов?", - "确定要删除所选的 {{count}} 个令牌吗?_one": "Подтвердить удаление выбранного {{count}} токена?", "确定要删除所选的 {{count}} 个令牌吗?_other": "Подтвердить удаление выбранных {{count}} токенов?", + "确定要删除所选的 {{count}} 个模型吗?_one": "Подтвердить удаление выбранной {{count}} модели?", "确定要删除所选的 {{count}} 个模型吗?_few": "Подтвердить удаление выбранных {{count}} моделей?", "确定要删除所选的 {{count}} 个模型吗?_many": "Подтвердить удаление выбранных {{count}} моделей?", - "确定要删除所选的 {{count}} 个模型吗?_one": "Подтвердить удаление выбранной {{count}} модели?", "确定要删除所选的 {{count}} 个模型吗?_other": "Подтвердить удаление выбранных {{count}} моделей?", "确定要删除此 OAuth 提供商吗?": "Вы уверены, что хотите удалить этого OAuth-провайдера?", "确定要删除此API信息吗?": "Подтвердить удаление этой информации API?", @@ -2362,6 +2504,7 @@ "确定要删除选中的": "Are you sure you want to delete the selected", "确定要启用所有密钥吗?": "Подтвердить включение всех ключей?", "确定要启用此用户吗?": "Подтвердить включение этого пользователя?", + "确定要对全部渠道执行上游模型更新吗?": "", "确定要提升此用户吗?": "Подтвердить повышение этого пользователя?", "确定要更新所有已启用通道余额吗?": "Подтвердить обновление баланса всех включенных каналов?", "确定要测试所有未手动禁用渠道吗?": "Вы уверены, что хотите протестировать все каналы, кроме отключенных вручную?", @@ -2372,15 +2515,14 @@ "确定要降级此用户吗?": "Подтвердить понижение этого пользователя?", "确定重置": "Подтвердить сброс", "确定重置模型倍率吗?": "Подтвердить сброс коэффициента модели?", - "确定?": "Подтвердить?", "确认": "Подтверждение", "确认作废": "Подтвердить аннулирование", "确认关闭提示": "Подтвердить закрытие", "确认冲突项修改": "Подтвердить изменение конфликтующих элементов", "确认删除": "Подтвердить удаление", "确认删除模型": "Confirm Delete Model", - "确认删除该分组的所有规则?": "Delete all rules for this group?", "确认删除该分组?": "Confirm delete this group?", + "确认删除该分组的所有规则?": "Delete all rules for this group?", "确认删除该规则?": "Confirm delete this rule?", "确认取消密码登录": "Подтвердить отмену входа по паролю", "确认启用": "Подтвердить включение", @@ -2408,13 +2550,16 @@ "磁盘使用率超过此值时拒绝请求": "Отклонять запросы, когда использование диска превышает это значение", "磁盘可用空间小于缓存最大总量设置": "Доступное дисковое пространство меньше настройки максимального размера кэша", "磁盘命中": "Попадания на диск", + "磁盘缓存最大总量 (MB)": "Максимальный объём дискового кэша (МБ)", "磁盘缓存占用的最大空间": "Максимальное пространство, занимаемое дисковым кэшем", "磁盘缓存已清理": "Дисковый кэш очищен", - "磁盘缓存最大总量 (MB)": "Максимальный объём дискового кэша (МБ)", "磁盘缓存设置(磁盘换内存)": "Настройки дискового кэша (обмен диска/памяти)", "磁盘缓存阈值 (MB)": "Порог дискового кэша (МБ)", "示例": "Пример", "示例:{\"default\": [200, 100], \"vip\": [0, 1000]}。": "Пример: {\"default\": [200, 100], \"vip\": [0, 1000]}.", + "视频": "Видео", + "视频Remix": "Видео ремикс", + "视频无法在当前浏览器中播放,这可能是由于:": "Видео нельзя воспроизвести в этом браузере, возможные причины:", "禁用": "Отключить", "禁用 store 透传": "Отключить сквозную передачу store", "禁用2FA失败": "Ошибка отключения 2FA", @@ -2432,10 +2577,12 @@ "私有镜像仓库的密码": "Password for private image registry", "私有镜像仓库的用户名": "Username for private image registry", "秒": "секунда", + "移动": "", + "移动请求头": "", "移除": "Remove", "移除 (-:)": "Remove (-:)", - "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "Удаление авторских знаков One API требует предварительного разрешения, поддержка проекта требует больших усилий, если этот проект важен для вас, пожалуйста, поддержите его", "移除 functionResponse.id 字段": "Удалить поле functionResponse.id", + "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "Удаление авторских знаков One API требует предварительного разрешения, поддержка проекта требует больших усилий, если этот проект важен для вас, пожалуйста, поддержите его", "空": "Пусто", "窗口处理": "Обработка окна", "窗口等待": "Ожидание окна", @@ -2468,19 +2615,19 @@ "等待中": "Ожидание", "等待获取邮箱信息...": "Ожидание получения информации об email...", "筛选": "Фильтр", + "签到最大额度": "Максимальная квота регистрации", + "签到最小额度": "Минимальная квота регистрации", "签到功能允许用户每日签到获取随机额度奖励": "Функция регистрации позволяет пользователям регистрироваться ежедневно для получения случайных наград в виде квоты", "签到失败": "Регистрация не удалась", "签到奖励将直接添加到您的账户余额": "Награды за регистрацию будут напрямую добавлены на баланс вашего счета", "签到奖励的最大额度": "Максимальная квота для наград за регистрацию", "签到奖励的最小额度": "Минимальная квота для наград за регистрацию", "签到成功!获得": "Регистрация успешна! Получено", - "签到最大额度": "Максимальная квота регистрации", - "签到最小额度": "Минимальная квота регистрации", "签到设置": "Настройки регистрации", "简单来说:同一个令牌分组,不同等级的用户可以享受不同的价格。": "In short: same token group, different user tiers can enjoy different prices.", "简洁": "Простой", - "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。": "Простой режим возвращает только сообщение; код состояния и тип ошибки будут использовать системные значения по умолчанию.", "简洁模式:按 type 全量清理对象,例如 redacted_thinking。": "Простой режим: очистка всех объектов по типу, напр. redacted_thinking.", + "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。": "Простой режим возвращает только сообщение; код состояния и тип ошибки будут использовать системные значения по умолчанию.", "管理": "Управление", "管理 Ollama 模型的拉取和删除": "Manage Ollama model pulling and deletion", "管理你的 LinuxDO OAuth App": "Управление вашим LinuxDO OAuth App", @@ -2491,8 +2638,8 @@ "管理员暂时未设置任何关于内容": "Администратор пока не установил никакой информации о проекте", "管理员未开启 Creem 充值!": "Администратор не включил пополнение через Creem!", "管理员未开启Stripe充值!": "Администратор не включил пополнение через Stripe!", - "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "Администратор не включил функцию онлайн пополнения, свяжитесь с администратором для включения или используйте коды купонов для пополнения.", "管理员未开启在线充值!": "Администратор не включил онлайн пополнение!", + "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "Администратор не включил функцию онлайн пополнения, свяжитесь с администратором для включения или используйте коды купонов для пополнения.", "管理员未开启在线支付功能,请联系管理员配置。": "Онлайн-оплата не включена администратором. Пожалуйста, свяжитесь с администратором.", "管理员未设置用户可选分组": "Администратор не установил доступные для выбора группы пользователей", "管理员给用户分配的分组(如 vip)不仅决定用户身份,还会影响后续两个功能:": "The group assigned to a user (e.g., vip) not only determines their identity, but also affects the following two features:", @@ -2532,14 +2679,16 @@ "系统数据统计": "Статистика системных данных", "系统文档和帮助信息": "Системная документация и справочная информация", "系统消息": "Системные сообщения", + "系统版本": "Версия системы", "系统管理功能": "Функции системного управления", "系统设置": "Система", "系统访问令牌": "Токен доступа к системе", + "约": "Приблизительно", "索引": "Индекс", "紧凑列表": "Компактный список", + "纯字符串会直接覆盖整条请求头,或者点击“查看 JSON 示例”按 token 规则处理。": "", "累计签到": "Всего регистраций", "累计获得": "Всего получено", - "约": "Приблизительно", "线路描述": "Описание маршрута", "组列表": "Список групп", "组名": "Имя группы", @@ -2569,44 +2718,45 @@ "统计次数": "Статистика количества", "统计额度": "Статистика лимитов", "继续": "Продолжить", - "缓存 Tokens": "Кэширование токенов", "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Кэш {{tokens}} токенов / 1M токенов * {{symbol}}{{price}}", "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Кэш {{tokens}} токенов / 1M токенов * {{symbol}}{{price}} (множитель: {{ratio}})", + "缓存 Tokens": "Кэширование токенов", "缓存: {{cacheRatio}}": "Кэш: {{cacheRatio}}", "缓存价格:{{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "Цена кэша: {{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M токенов (коэффициент кэширования: {{cacheRatio}})", "缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "Цена кэша: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M токенов (коэффициент кэширования: {{cacheRatio}})", "缓存倍率": "Коэффициент кэширования", "缓存倍率 {{cacheRatio}}": "Коэффициент кэша {{cacheRatio}}", "缓存写": "Запись в кэш", - "缓存创建 Tokens": "Создание кэша токенов", "缓存创建 {{price}} / 1M tokens": "Создание кэша {{price}} / 1M tokens", "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Создание кэша {{tokens}} токенов / 1M токенов * {{symbol}}{{price}}", "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Создание кэша {{tokens}} токенов / 1M токенов * {{symbol}}{{price}} (множитель: {{ratio}})", + "缓存创建 Tokens": "Создание кэша токенов", + "缓存创建: {{cacheCreationRatio}}": "Создание кэша: {{cacheCreationRatio}}", "缓存创建: 1h {{cacheCreationRatio1h}}": "Создание кэша: 1h {{cacheCreationRatio1h}}", "缓存创建: 5m {{cacheCreationRatio5m}}": "Создание кэша: 5m {{cacheCreationRatio5m}}", "缓存创建: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Создание кэша: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存创建: {{cacheCreationRatio}}": "Создание кэша: {{cacheCreationRatio}}", + "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Создание кэша: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент создания кэша {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存创建价格": "Цена создания входного кеша", "缓存创建价格 {{symbol}}{{price}} / 1M tokens": "Цена создания кеша {{symbol}}{{price}} / 1M tokens", - "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "Итого цена создания кэша: 5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M токенов", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "Цена создания кэша: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M токенов (коэффициент создания кэша: {{cacheCreationRatio}})", "缓存创建价格:{{symbol}}{{price}} / 1M tokens": "Цена создания кеша: {{symbol}}{{price}} / 1M tokens", + "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "Итого цена создания кэша: 5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M токенов", "缓存创建倍率": "Коэффициент создания кэша", + "缓存创建倍率 {{cacheCreationRatio}}": "Коэффициент создания кэша {{cacheCreationRatio}}", "缓存创建倍率 1h {{cacheCreationRatio1h}}": "Множитель создания кэша 1h {{cacheCreationRatio1h}}", "缓存创建倍率 5m {{cacheCreationRatio5m}}": "Множитель создания кэша 5m {{cacheCreationRatio5m}}", "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Коэффициент создания кэша 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存创建倍率 {{cacheCreationRatio}}": "Коэффициент создания кэша {{cacheCreationRatio}}", - "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Создание кэша: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент создания кэша {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存条目数": "Количество записей кэша", "缓存目录": "Директория кэша", "缓存目录磁盘空间": "Дисковое пространство директории кэша", "缓存读": "Чтение кэша", "缓存读 {{price}} / 1M tokens": "Чтение кеша {{price}} / 1M tokens", + "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Чтение кэша: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент кэша {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存读取价格": "Цена чтения входного кеша", "缓存读取价格 {{symbol}}{{price}} / 1M tokens": "Цена чтения кеша {{symbol}}{{price}} / 1M tokens", "缓存读取价格:{{symbol}}{{price}} / 1M tokens": "Цена чтения кеша: {{symbol}}{{price}} / 1M tokens", "缓存读取价格:{{symbol}}{{total}} / 1M tokens": "Цена чтения кеша: {{symbol}}{{total}} / 1M tokens", - "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Чтение кэша: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент кэша {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "缓存读取倍率": "", "缓存输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Кэшированный ввод: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент кэша {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "编辑": "Редактировать", "编辑 OAuth 提供商": "Редактировать OAuth-провайдера", @@ -2617,6 +2767,7 @@ "编辑公告内容": "Редактировать содержимое объявления", "编辑分类": "Редактировать категорию", "编辑成功": "Редактирование выполнено успешно", + "编辑支付方式": "", "编辑方式": "Режим редактирования", "编辑标签": "Редактировать тег", "编辑模型": "Редактировать модель", @@ -2686,11 +2837,12 @@ "若你的 OIDC Provider 支持 Discovery Endpoint,你可以仅填写 OIDC Well-Known URL,系统会自动获取 OIDC 配置": "Если ваш OIDC Provider поддерживает Discovery Endpoint, вы можете указать только OIDC Well-Known URL, и система автоматически получит OIDC конфигурацию", "获取 Discovery 配置": "Получить конфигурацию Discovery", "获取 Discovery 配置失败:": "Не удалось получить конфигурацию Discovery: ", + "获取 io.net API Key": "Get io.net API Key", "获取 OIDC 配置失败,请检查网络状况和 Well-Known URL 是否正确": "Не удалось получить OIDC конфигурацию, проверьте состояние сети и правильность Well-Known URL", "获取 OIDC 配置成功!": "OIDC конфигурация успешно получена!", "获取 Ollama 版本失败": "Failed to get Ollama version", - "获取 io.net API Key": "Get io.net API Key", "获取2FA状态失败": "Не удалось получить статус 2FA", + "获取令牌密钥失败": "", "获取充值配置失败": "Не удалось получить конфигурацию пополнения", "获取充值配置异常": "Ошибка конфигурации пополнения", "获取初始化状态失败": "Не удалось получить статус инициализации", @@ -2722,9 +2874,9 @@ "补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "Дополнение {{completion}} токенов * коэффициент вывода {{completionRatio}}", "补全 {{completion}} tokens / 1M tokens * {{symbol}}{{price}}": "Дополнение {{completion}} токенов / 1M токенов * {{symbol}}{{price}}", "补全价格": "Цена завершения", - "补全价格已锁定": "Цена завершения заблокирована", "补全价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "Цена вывода: {{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M токенов (коэффициент вывода: {{completionRatio}})", "补全价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens": "Цена вывода: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M токенов", + "补全价格已锁定": "Цена завершения заблокирована", "补全倍率": "Коэффициент вывода", "补全倍率 {{completionRatio}}": "Коэффициент вывода {{completionRatio}}", "补全倍率值": "Значение коэффициента вывода", @@ -2733,6 +2885,7 @@ "补单成功": "Заказ успешно дополнен", "表单引用错误,请刷新页面重试": "Ошибка ссылки формы, обновите страницу и попробуйте снова", "表格视图": "Табличное представление", + "覆盖": "Заменить", "覆盖模式:将完全替换现有的所有密钥": "Режим перезаписи: полностью заменит все существующие ключи", "覆盖模板": "Шаблон переопределения", "覆盖现有密钥": "Перезаписать существующие ключи", @@ -2743,10 +2896,8 @@ "规则为 JSON 数组;可视化与 JSON 模式共用同一份数据。": "Правила представляют собой JSON-массив; визуальный и JSON режимы используют одни и те же данные.", "规则名称(可读性更好,也会出现在管理侧日志中)。": "Имя правила (для лучшей читаемости, также отображается в журналах администрирования).", "规则导航": "Навигация по правилам", + "规则描述(可选)": "", "规则未找到,请刷新后重试": "Правило не найдено, обновите страницу и попробуйте снова", - "视频": "Видео", - "视频Remix": "Видео ремикс", - "视频无法在当前浏览器中播放,这可能是由于:": "Видео нельзя воспроизвести в этом браузере, возможные причины:", "角色": "Роль", "解析响应数据时发生错误": "Произошла ошибка при разборе данных ответа", "解析密钥文件失败: {{msg}}": "Не удалось разобрать файл ключа: {{msg}}", @@ -2755,21 +2906,20 @@ "解绑 Passkey": "Отвязать Passkey", "解绑后将无法使用 Passkey 登录,确定要继续吗?": "После отвязки невозможно будет использовать Passkey для входа, продолжить?", "解绑成功": "Успешно отвязано", - "警告": "Предупреждение", - "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔": "Предупреждение: после включения поддержания активности, если канал выдаёт ошибку после записи данных поддержания активности, система не может повторить попытку, если необходимо включить, рекомендуется установить максимально возможный интервал Ping", - "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "Предупреждение: отключение двухфакторной аутентификации навсегда удалит ваши настройки проверки и все резервные коды, эта операция необратима!", "计价币种": "Pricing Currency", "计算中": "Calculating", "计算成本": "Calculate Cost", "计算费用中...": "Calculating fees...", "计费乘数,倍率越低费用越低。例如倍率 0.5 表示半价。": "Billing multiplier — the lower the ratio, the lower the cost. E.g., ratio 0.5 means half price.", "计费开始": "Billing Start", + "计费摘要": "", "计费方式": "Режим тарификации", "计费显示模式": "Режим отображения тарификации", "计费模式": "Режим тарификации", "计费类型": "Тип выставления счёта", "计费过程": "Процесс выставления счёта", "订单号": "Номер заказа", + "订单支付方式": "Способ оплаты заказа", "订阅": "Подписка", "订阅剩余": "Остаток подписки", "订阅套餐": "Планы подписки", @@ -2784,6 +2934,7 @@ "记录请求与错误日志IP": "Записывать IP запросов и логов ошибок", "设备": "Device", "设备类型偏好": "Предпочтения типа устройства", + "设置": "", "设置 Logo": "Установить Logo", "设置2FA失败": "Ошибка настройки 2FA", "设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}": "Установить скидки для разных сумм пополнения, ключ - сумма пополнения, значение - ставка скидки, например: {\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}", @@ -2813,6 +2964,7 @@ "设置默认地区和特定模型的专用地区": "Установить регион по умолчанию и специальные регионы для конкретных моделей", "设计与开发由": "Дизайн и разработка", "设计版本": "b80c3466cb6feafeb3990c7820e10e50", + "设请求头": "", "访问 io.net 控制台的 API Keys 页面": "Visit the API Keys page of the io.net console", "访问容器": "Access Container", "访问模型部署功能需要先启用 io.net 部署服务": "Accessing model deployment features requires enabling the io.net deployment service first", @@ -2828,6 +2980,8 @@ "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "Коэффициент завершения для этой модели зафиксирован на уровне {{ratio}} на бэкенде. Цену завершения нельзя изменить здесь.", "该渠道已开启请求透传,参数覆写、模型重定向等 NewAPI 内置功能将失效,非最佳实践。": "Для этого канала включена сквозная передача запросов; встроенные функции NewAPI, такие как переопределение параметров и перенаправление моделей, будут отключены. Это не является лучшей практикой.", "该渠道已开启请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "Для этого канала включена сквозная передача запросов. Встроенные возможности NewAPI, такие как переопределение параметров, перенаправление моделей и адаптация канала, будут отключены. Это не является лучшей практикой. Если из-за этого возникнут проблемы, пожалуйста, не создавайте issue.", + "该渠道暂无可处理的上游模型更新": "", + "该渠道未开启上游模型更新检测": "", "该规则未启用“作用域:包含规则名称”,无法按规则清空缓存。": "У этого правила не включена «Область действия: включить имя правила», очистка кэша по правилу невозможна.", "该规则未设置参数覆盖模板": "У этого правила не задан шаблон переопределения параметров", "该规则的缓存保留时长;0 表示使用默认 TTL:": "Время хранения кэша для этого правила; 0 — использовать TTL по умолчанию: ", @@ -2839,10 +2993,10 @@ "语音输入": "Голосовой ввод", "语音输出": "Голосовой вывод", "说明": "Описание", - "说明信息": "Информация об описании", "说明:": "Описание:", "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。": "Примечание: тесты на этой странице используют нестриминговые запросы. Если канал поддерживает только стриминговые ответы, тест может завершиться неудачей. Ориентируйтесь на реальное использование.", "说明:生成结果是可直接粘贴到渠道密钥里的 JSON(包含 access_token / refresh_token / account_id)。": "Примечание: Результат — это JSON, который можно вставить непосредственно в ключ канала (содержит access_token / refresh_token / account_id).", + "说明信息": "Информация об описании", "请上传密钥文件": "Пожалуйста, загрузите файл ключа", "请上传密钥文件!": "Пожалуйста, загрузите файл ключа!", "请为渠道命名": "Пожалуйста, назовите канал", @@ -2855,6 +3009,7 @@ "请先填写 Ollama API 地址": "Please fill in Ollama API address first", "请先填写服务器地址": "Пожалуйста, сначала заполните адрес сервера", "请先开启并填写音频输入价格。": "Сначала включите и заполните цену аудио-ввода.", + "请先新增模型或从左侧列表选择一个模型": "", "请先粘贴回调 URL": "Сначала вставьте URL обратного вызова", "请先输入密钥": "Пожалуйста, сначала введите ключ", "请先选择一个作为模板的模型": "Сначала выберите модель-шаблон", @@ -2870,6 +3025,7 @@ "请再次输入新密码": "Пожалуйста, введите новый пароль ещё раз", "请前往个人设置 → 安全设置进行配置。": "Пожалуйста, перейдите в Личные настройки → Настройки безопасности для конфигурации.", "请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "Не доверяйте этой функции чрезмерно, IP может быть подделан, используйте её вместе с nginx и CDN и другими шлюзами", + "请在 Waffo 后台获取 API 密钥、商户 ID 以及 RSA 密钥对,并配置回调地址。": "", "请在系统设置页面编辑分组倍率以添加新的分组:": "Пожалуйста, отредактируйте коэффициенты групп на странице системных настроек для добавления новой группы:", "请填写完整的产品信息": "Пожалуйста, заполните всю информацию о продукте", "请填写完整的管理员账号信息": "Пожалуйста, заполните полную информацию об учётной записи администратора", @@ -2888,7 +3044,6 @@ "请求参数无效": "Invalid request parameters", "请求发生错误": "Произошла ошибка запроса", "请求发生错误: ": "Произошла ошибка запроса: ", - "模型价格未配置": "Цена модели не настроена", "请求后端接口失败:": "Не удалось запросить внутренний интерфейс:", "请求失败": "Запрос не удался", "请求头覆盖": "Переопределение заголовков запроса", @@ -2919,18 +3074,18 @@ "请至少选择一个渠道": "Пожалуйста, выберите хотя бы один канал", "请输入 API Key,一行一个,格式:APIKey|Region": "Введите API Key, по одному в строке, формат: APIKey|Region", "请输入 API Key,格式:APIKey|Region": "Введите API Key в формате: APIKey|Region", - "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com": "Пожалуйста, введите AZURE_OPENAI_ENDPOINT, например: https://docs-test-001.openai.azure.com", "请输入 Authorization Endpoint": "Введите Authorization Endpoint", + "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com": "Пожалуйста, введите AZURE_OPENAI_ENDPOINT, например: https://docs-test-001.openai.azure.com", "请输入 Client ID": "Введите Client ID", "请输入 Client Secret": "Введите Client Secret", + "请输入 io.net API Key": "Please enter io.net API Key", + "请输入 io.net API Key(敏感信息不显示)": "Please enter io.net API Key (sensitive information not displayed)", "请输入 JSON 格式的 OAuth 凭据,例如:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}": "Введите учётные данные OAuth в формате JSON, напр.:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}", "请输入 JSON 格式的密钥内容,例如:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}": "Пожалуйста, введите содержимое ключа в формате JSON, например:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}", "请输入 OIDC 的 Well-Known URL": "Пожалуйста, введите Well-Known URL OIDC", "请输入 Slug": "Введите Slug", "请输入 Token Endpoint": "Введите Token Endpoint", "请输入 User Info Endpoint": "Введите User Info Endpoint", - "请输入 io.net API Key": "Please enter io.net API Key", - "请输入 io.net API Key(敏感信息不显示)": "Please enter io.net API Key (sensitive information not displayed)", "请输入6位验证码或8位备用码": "Пожалуйста, введите 6-значный код подтверждения или 8-значный резервный код", "请输入API地址": "Пожалуйста, введите адрес API", "请输入API地址!": "Пожалуйста, введите адрес API!", @@ -2940,9 +3095,9 @@ "请输入Gotify服务器地址": "Пожалуйста, введите адрес сервера Gotify", "请输入Gotify服务器地址,例如: https://gotify.example.com": "Пожалуйста, введите адрес сервера Gotify, например: https://gotify.example.com", "请输入JSON数组,如 [\"model-a\",\"model-b\"]": "Введите JSON-массив, например [\"model-a\",\"model-b\"]", - "请输入URL链接": "Пожалуйста, введите URL-ссылку", "请输入Uptime Kuma地址": "Пожалуйста, введите адрес Uptime Kuma", "请输入Uptime Kuma服务地址,如:https://status.example.com": "Пожалуйста, введите адрес службы Uptime Kuma, например: https://status.example.com", + "请输入URL链接": "Пожалуйста, введите URL-ссылку", "请输入Webhook地址": "Пожалуйста, введите адрес Webhook", "请输入Webhook地址,例如: https://example.com/webhook": "Пожалуйста, введите адрес Webhook, например: https://example.com/webhook", "请输入你的账户名以确认删除!": "Пожалуйста, введите имя вашей учётной записи для подтверждения удаления!", @@ -2967,14 +3122,14 @@ "请输入备注(仅管理员可见)": "Пожалуйста, введите примечание (видимо только администратору)", "请输入套餐标题": "Введите название плана", "请输入完整的 JSON 格式密钥内容": "Пожалуйста, введите полное содержимое ключа в формате JSON", - "请输入完整的URL链接": "Пожалуйста, введите полную URL-ссылку", "请输入完整的URL,例如:https://api.openai.com/v1/chat/completions": "Пожалуйста, введите полный URL, например: https://api.openai.com/v1/chat/completions", + "请输入完整的URL链接": "Пожалуйста, введите полную URL-ссылку", "请输入容器名称": "Please enter container name", "请输入密码": "Пожалуйста, введите пароль", "请输入密钥": "Пожалуйста, введите ключ", - "请输入密钥!": "Пожалуйста, введите ключ!", "请输入密钥,一行一个": "Пожалуйста, введите ключи, по одному в строке", "请输入密钥,一行一个,格式:AccessKey|SecretAccessKey|Region": "Введите ключи по одному в строке в формате: AccessKey|SecretAccessKey|Region", + "请输入密钥!": "Пожалуйста, введите ключ!", "请输入延长时长": "Please enter extension duration", "请输入总额度": "Введите общий лимит", "请输入您的密码": "Пожалуйста, введите ваш пароль", @@ -3027,9 +3182,9 @@ "请输入认证器验证码或备用码": "Пожалуйста, введите код подтверждения аутентификатора или резервный код", "请输入说明": "Пожалуйста, введите описание", "请输入运行时长": "Please enter runtime duration", + "请输入邮箱!": "Пожалуйста, введите адрес электронной почты!", "请输入邮箱地址": "Пожалуйста, введите адрес электронной почты", "请输入邮箱验证码!": "Пожалуйста, введите код подтверждения электронной почты!", - "请输入邮箱!": "Пожалуйста, введите адрес электронной почты!", "请输入部署名称": "Please enter deployment name", "请输入部署名称以完成二次确认": "Enter deployment name to complete secondary confirmation", "请输入部署地区,例如:us-central1\n支持使用模型映射格式\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}": "Пожалуйста, введите регион развертывания, например: us-central1\nПоддерживается формат сопоставления моделей\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}", @@ -3072,12 +3227,17 @@ "请选择该渠道所支持的模型,留空则不更改": "Пожалуйста, выберите модели, поддерживаемые этим каналом, оставьте пустым для без изменений", "请选择过期时间": "Пожалуйста, выберите время истечения", "请选择通知方式": "Пожалуйста, выберите способ уведомления", + "调整额度": "Скорректировать квоту", + "调整额度成功": "Квота успешно скорректирована", "调用次数": "Количество вызовов", "调用次数分布": "Распределение количества вызовов", "调用次数排行": "Рейтинг количества вызовов", "调用趋势": "Тенденция вызовов", "调试信息": "Отладочная информация", "谨慎": "Осторожно", + "警告": "Предупреждение", + "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔": "Предупреждение: после включения поддержания активности, если канал выдаёт ошибку после записи данных поддержания активности, система не может повторить попытку, если необходимо включить, рекомендуется установить максимально возможный интервал Ping", + "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "Предупреждение: отключение двухфакторной аутентификации навсегда удалит ваши настройки проверки и все резервные коды, эта операция необратима!", "豆包": "Doubao", "账单": "Счёт", "账户充值": "Пополнение счёта", @@ -3112,15 +3272,19 @@ "路径正则": "Regex пути", "路径正则(每行一个)": "Regex пути (по одному в строке)", "跳转": "Перейти", + "转大写": "", + "转小写": "", "轮询": "Опрос", "轮询模式": "Режим опроса", "轮询模式必须搭配Redis和内存缓存功能使用,否则性能将大幅降低,并且无法实现轮询功能": "Режим опроса должен использоваться вместе с функциями Redis и кэширования памяти, иначе производительность значительно снизится, и функция опроса не будет реализована", + "软错误": "", "输入": "Ввод", + "输入 {{price}} / 1M tokens": "Вход {{price}} / 1M tokens", + "输入 $/1M tokens": "", "输入 OIDC 的 Authorization Endpoint": "Введите Authorization Endpoint OIDC", "输入 OIDC 的 Client ID": "Введите Client ID OIDC", "输入 OIDC 的 Token Endpoint": "Введите Token Endpoint OIDC", "输入 OIDC 的 Userinfo Endpoint": "Введите Userinfo Endpoint OIDC", - "输入 {{price}} / 1M tokens": "Вход {{price}} / 1M tokens", "输入IP地址后回车,如:8.8.8.8": "Введите IP-адрес и нажмите Enter, например: 8.8.8.8", "输入JSON对象": "Введите JSON-объект", "输入价格": "Цена ввода", @@ -3129,6 +3293,7 @@ "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "Цена ввода: {{symbol}}{{price}} / 1M tokens{{audioPrice}}", "输入你注册的 LinuxDO OAuth APP 的 ID": "Введите ID вашего зарегистрированного LinuxDO OAuth APP", "输入你的账户名{{username}}以确认删除": "Введите имя вашей учётной записи {{username}} для подтверждения удаления", + "输入倍率": "", "输入域名后回车": "Введите доменное имя и нажмите Enter", "输入域名后回车,如:example.com": "Введите доменное имя и нажмите Enter, например: example.com", "输入密码,最短 8 位,最长 20 位": "Введите пароль, минимум 8 символов, максимум 20 символов", @@ -3137,6 +3302,7 @@ "输入模型倍率": "Введите коэффициент модели", "输入模型名称,例如 gpt-4.1": "Введите имя модели, например gpt-4.1", "输入每次价格": "Введите цену за использование", + "输入每次调用价格": "", "输入端口后回车,如:80 或 8000-8999": "Введите порт и нажмите Enter, например: 80 или 8000-8999", "输入系统提示词,用户的系统提示词将优先于此设置": "Введите системный промпт, системные промпты пользователя будут иметь приоритет над этой настройкой", "输入自定义模型名称": "Введите имя пользовательской модели", @@ -3152,14 +3318,14 @@ "输入验证码完成设置": "Введите код подтверждения для вывода настройки", "输出": "Вывод", "输出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}": "Вывод {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Вывод: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент завершения {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Вывод: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент вывода {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "输出价格": "Цена вывода", "输出价格 {{symbol}}{{price}} / 1M tokens": "Цена вывода {{symbol}}{{price}} / 1M tokens", "输出价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "Цена вывода: {{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M токенов (коэффициент вывода: {{completionRatio}})", "输出价格:{{symbol}}{{price}} / 1M tokens": "Цена вывода: {{symbol}}{{price}} / 1M tokens", "输出价格:{{symbol}}{{total}} / 1M tokens": "Цена вывода: {{symbol}}{{total}} / 1M tokens", "输出倍率 {{completionRatio}}": "Коэффициент вывода {{completionRatio}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Вывод: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент завершения {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Вывод: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * коэффициент вывода {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "边栏设置": "Настройки боковой панели", "过期于": "Истекает", "过期时间": "Время истечения", @@ -3173,6 +3339,7 @@ "运行时长(小时)": "Runtime Duration (hours)", "返回修改": "Вернуться и исправить", "返回登录": "Вернуться к входу", + "返回错误": "", "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "В этом интерфейсе значения по умолчанию задаются через цены, а при сохранении они автоматически преобразуются в JSON коэффициентов, требуемый backend.", "这些价格都是可选项,不填也可以。": "Все эти цены необязательны и могут быть оставлены пустыми.", "这将删除超过 10 分钟未使用的临时缓存文件": "Это удалит временные файлы кэша, которые не использовались более 10 минут", @@ -3232,6 +3399,7 @@ "选择部署位置(可多选)": "Select deployment location(s) (multiple selections allowed)", "选择预设模板(可选)": "Выберите предустановленный шаблон (необязательно)", "透传请求体": "Прямая передача тела запроса", + "透传请求头": "", "递归": "Рекурсия", "递归策略": "Стратегия рекурсии", "通义千问": "Tongyi Qianwen", @@ -3301,7 +3469,9 @@ "配置 SMTP": "Настроить SMTP", "配置 Telegram 登录": "Настроить вход через Telegram", "配置 Turnstile": "Настроить Turnstile", + "配置 Waffo 充值时可用的支付方式,保存后在充值页面展示给用户。": "", "配置 WeChat Server": "Настроить WeChat Server", + "配置:": "Конфиг:", "配置后的效果:": "After configuration:", "配置和消息已全部重置": "Конфигурация и сообщения полностью сброшены", "配置套餐的有效时长": "Настроить срок действия плана", @@ -3320,13 +3490,13 @@ "配置自定义 OAuth 提供商,支持 GitHub Enterprise、GitLab、Gitea、Nextcloud、Keycloak、ORY 等兼容 OAuth 2.0 协议的身份提供商": "Настройте пользовательских OAuth-провайдеров, поддерживаются GitHub Enterprise, GitLab, Gitea, Nextcloud, Keycloak, ORY и другие провайдеры идентификации, совместимые с OAuth 2.0", "配置说明": "Описание конфигурации", "配置邮箱域名白名单": "Настроить белый список доменов электронной почты", - "配置:": "Конфиг:", "重启部署失败": "Failed to restart deployment", "重命名部署": "Rename Deployment", "重复提交": "Повторная отправка", "重复的键名": "Повторяющееся имя ключа", "重复的键名,此值将被后面的同名键覆盖": "Повторяющееся имя ключа, это значение будет перезаписано последующим ключом с тем же именем", "重定向 URL 填": "Заполнить URL перенаправления", + "重新上传": "", "重新发送": "Отправить снова", "重新生成": "Сгенерировать заново", "重新生成备用码": "Сгенерировать резервные коды заново", @@ -3380,15 +3550,15 @@ "限制周期统一使用上方配置的“限制周期”值。": "Период ограничения равномерно использует значение 'Период ограничения', настроенное выше.", "限流": "Ограничение скорости", "限购": "Лимит", - "随机": "Случайный", - "随机模式": "Случайный режим", - "随机种子 (留空为随机)": "Случайное зерно (оставьте пустым для случайного)", "隐私政策": "Политика конфиденциальности", "隐私政策已更新": "Политика конфиденциальности обновлена", "隐私政策更新失败": "Не удалось обновить политику конфиденциальности", "隐私设置": "Настройки конфиденциальности", "隐藏操作项": "Скрыть элементы операций", "隐藏调试": "Скрыть отладку", + "随机": "Случайный", + "随机模式": "Случайный режим", + "随机种子 (留空为随机)": "Случайное зерно (оставьте пустым для случайного)", "零一万物": "01.AI", "需要安全验证": "Требуется проверка безопасности", "需要添加的额度(支持负数)": "Квота для добавления (поддерживаются отрицательные значения)", @@ -3406,13 +3576,15 @@ "音频补全价格": "Цена завершения аудио", "音频补全价格:{{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (音频补全倍率: {{audioCompRatio}})": "Цена аудиовывода: {{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M токенов (коэффициент аудиовывода: {{audioCompRatio}})", "音频补全价格:{{symbol}}{{price}} / 1M tokens": "Цена завершения аудио: {{symbol}}{{price}} / 1M tokens", + "音频补全倍率": "", "音频补全倍率(仅部分模型支持该计费)": "Коэффициент аудиовывода (только некоторые модели поддерживают эту тарификацию)", + "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Аудиоввод: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * аудио-коэффициент {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "音频输入价格": "Цена входного аудио", "音频输入价格:{{symbol}}{{price}} / 1M tokens": "Цена входного аудио: {{symbol}}{{price}} / 1M tokens", + "音频输入倍率": "", "音频输入相关的倍率设置,键为模型名称,值为倍率": "Настройки коэффициентов, связанные с аудиовводом, ключ - имя модели, значение - коэффициент", - "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Аудиоввод: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * аудио-коэффициент {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "Настройки коэффициентов, связанные с аудиовыводом и завершением, ключ - имя модели, значение - коэффициент", "音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Аудиовывод: {{tokens}} / 1M * коэффициент модели {{modelRatio}} * аудио-коэффициент {{audioRatio}} * коэффициент аудиозавершения {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "Настройки коэффициентов, связанные с аудиовыводом и завершением, ключ - имя модели, значение - коэффициент", "页脚": "Подвал", "页面未找到,请检查您的浏览器地址是否正确": "Страница не найдена, пожалуйста, проверьте правильность адреса в браузере", "页面渲染出错,请刷新页面重试": "Произошла ошибка при отрисовке страницы. Пожалуйста, обновите страницу и попробуйте снова.", @@ -3430,9 +3602,10 @@ "预览更新": "Обновление предварительного просмотра", "预览模板": "Предпросмотр шаблона", "预览请求体": "Предварительный просмотр тела запроса", - "预警阈值必须为正数": "Порог предупреждения должен быть положительным числом", "预计结束": "Estimated End", + "预计结果": "Ожидаемый результат", "预设模板": "Предустановленный шаблон", + "预警阈值必须为正数": "Порог предупреждения должен быть положительным числом", "频率惩罚,减少重复词汇的出现": "Штраф за частоту, уменьшает повторение слов", "频率限制的周期(分钟)": "Период ограничения частоты (минуты)", "颜色": "Цвет", @@ -3481,10 +3654,6 @@ "默认折叠侧边栏": "Сворачивать боковую панель по умолчанию", "默认测试模型": "Модель для тестирования по умолчанию", "默认用户消息": "Здравствуйте", - "默认补全倍率": "Коэффициент завершения по умолчанию", - "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(В настоящее время поддерживается только интерфейс YiPay, по умолчанию используется адрес сервера выше в качестве адреса обратного вызова!)", - ",当前无生效订阅,将自动使用钱包": ", нет активной подписки, автоматически будет использоваться кошелек.", - ",时间:": ", время: ", - ",点击更新": ", нажмите для обновления" + "默认补全倍率": "Коэффициент завершения по умолчанию" } } diff --git a/web/src/i18n/locales/vi.json b/web/src/i18n/locales/vi.json index dc3de89f..42d33c18 100644 --- a/web/src/i18n/locales/vi.json +++ b/web/src/i18n/locales/vi.json @@ -9,26 +9,48 @@ " 吗?": " không?", " 秒": " giây", " 秒。": " giây.", - "$/1M tokens": "$/1M tokens", + ",当前无生效订阅,将自动使用钱包": ", hiện không có gói đăng ký hiệu lực, sẽ tự động dùng ví.", + ",时间:": ", thời gian:", + ",点击更新": ", nhấn để cập nhật", + "(共 {{total}} 个,省略 {{omit}} 个)": "", + "(共 {{total}} 个)": "", + "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(Hiện tại chỉ hỗ trợ giao diện Epay, địa chỉ máy chủ phía trên được sử dụng làm địa chỉ gọi lại theo mặc định!)", "(筛选后显示 {{count}} 条)_other": "(Showing {{count}} items after filtering)", "(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(Đầu vào {{input}} tokens / 1M tokens * {{symbol}}{{price}}", "(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(Đầu vào {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + Đầu vào âm thanh {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}", "(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(Đầu vào {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + Bộ nhớ đệm {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}", "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(Đầu vào {{nonImageInput}} tokens + Đầu vào hình ảnh {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}", + "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "Giá trị tối đa của [Số lần yêu cầu tối đa] và [Số lần hoàn thành yêu cầu tối đa] là 2147483647.", + "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[Số lần yêu cầu tối đa] phải lớn hơn hoặc bằng 0, [Số lần hoàn thành yêu cầu tối đa] phải lớn hơn hoặc bằng 1.", + "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", + "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", + "{{count}} 项操作_other": "", + "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", + "{{name}} ID": "{{name}} ID", + "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", + "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", + "「用户可选」决定用户创建令牌时能否自主选择该分组。未勾选的分组只能由管理员在后台分配给用户,不会出现在用户的令牌创建页面中。": "\"User Selectable\" controls whether users can choose this group when creating tokens. Unchecked groups can only be assigned by admin and won't appear in the token creation page.", + "• 视频服务商的跨域限制": "• Cross-origin limitations from the video provider", + "• 防盗链保护机制": "• Hotlink protection mechanisms", + "• 需要特定的请求头或认证": "• Specific headers or authentication are required", + "© {{currentYear}}": "© {{currentYear}}", + "| 基于": " | Dựa trên ", + "$/1M tokens": "$/1M tokens", + "$/次": "", "0 - 最低": "0 - Thấp nhất", "0 表示不限": "0 nghĩa là không giới hạn", "0.002-1之间的小数": "Số thập phân giữa 0.002-1", "0.1以上的小数": "Số thập phân trên 0.1", - "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。": "1) Nhấn \"Mở trang xác thực\" để đăng nhập; 2) Trình duyệt sẽ chuyển hướng đến localhost (không sao nếu trang không mở được); 3) Sao chép URL đầy đủ từ thanh địa chỉ và dán vào bên dưới; 4) Nhấn \"Tạo và điền\".", "1. 管理员在此创建分组并设置倍率": "1. Admin creates groups and sets ratios here", + "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。": "1) Nhấn \"Mở trang xác thực\" để đăng nhập; 2) Trình duyệt sẽ chuyển hướng đến localhost (không sao nếu trang không mở được); 3) Sao chép URL đầy đủ từ thanh địa chỉ và dán vào bên dưới; 4) Nhấn \"Tạo và điền\".", "10 - 最高": "10 - Cao nhất", "1h缓存创建 {{price}} / 1M tokens": "Tạo bộ nhớ đệm 1h {{price}} / 1M tokens", "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Tạo bộ nhớ đệm 1h {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "1h cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio: {{ratio}})", + "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "Tạo bộ nhớ đệm 1h: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số tạo bộ nhớ đệm 1h {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", "1h缓存创建价格 {{symbol}}{{price}} / 1M tokens": "Giá tạo bộ nhớ đệm 1h {{symbol}}{{price}} / 1M tokens", "1h缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h缓存创建倍率: {{cacheCreationRatio1h}})": "1h cache creation price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h cache creation ratio: {{cacheCreationRatio1h}})", "1h缓存创建价格:{{symbol}}{{price}} / 1M tokens": "Giá tạo bộ nhớ đệm 1h: {{symbol}}{{price}} / 1M tokens", - "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "Tạo bộ nhớ đệm 1h: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số tạo bộ nhớ đệm 1h {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", "2 - 低": "2 - Thấp", "2. 管理员在用户管理中将用户分配到对应分组(如 vip)": "2. Admin assigns users to groups in User Management (e.g., vip)", "2025年5月10日后添加的渠道,不需要再在部署的时候移除模型名称中的\".\"": "Các kênh được thêm sau ngày 10 tháng 5 năm 2025 không cần xóa dấu chấm trong tên mô hình khi triển khai", @@ -39,22 +61,25 @@ "5m缓存创建 {{price}} / 1M tokens": "Tạo bộ nhớ đệm 5m {{price}} / 1M tokens", "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Tạo bộ nhớ đệm 5m {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "5m cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio: {{ratio}})", + "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "Tạo bộ nhớ đệm 5m: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số tạo bộ nhớ đệm 5m {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", "5m缓存创建价格 {{symbol}}{{price}} / 1M tokens": "Giá tạo bộ nhớ đệm 5m {{symbol}}{{price}} / 1M tokens", "5m缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m缓存创建倍率: {{cacheCreationRatio5m}})": "5m cache creation price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m cache creation ratio: {{cacheCreationRatio5m}})", "5m缓存创建价格:{{symbol}}{{price}} / 1M tokens": "Giá tạo bộ nhớ đệm 5m: {{symbol}}{{price}} / 1M tokens", - "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "Tạo bộ nhớ đệm 5m: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số tạo bộ nhớ đệm 5m {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", "8 - 高": "8 - Cao", "AGPL v3.0协议": "Giấy phép AGPL v3.0", "AI 对话": "Trò chuyện AI", "AI模型测试环境": "Môi trường thử nghiệm mô hình AI", "AI模型配置": "Cấu hình mô hình AI", "AK/SK 模式:使用 AccessKey 和 SecretAccessKey;API Key 模式:使用 API Key": "AK/SK mode uses AccessKey and SecretAccessKey; API Key mode uses an API Key", + "anthropic-beta JSON 示例": "", "API Key": "API Key", "API Key 模式下不支持批量创建": "Không hỗ trợ tạo hàng loạt trong chế độ API Key", "API Key 验证失败": "API Key verification failed", "API Key 验证成功!连接到 io.net 服务正常": "API Key verification successful! Connection to io.net service is normal", "API 地址和相关配置": "Địa chỉ API và cấu hình liên quan", "API 密钥": "Khóa API", + "API 密钥 (沙盒)": "", + "API 密钥 (生产)": "", "API 文档": "Tài liệu API", "API 配置": "Cấu hình API", "API令牌管理": "Quản lý mã thông báo API", @@ -64,14 +89,13 @@ "API地址": "Base URL", "API渠道配置": "Cấu hình kênh API", "API端点": "Điểm cuối API", - "Authorization Endpoint": "Điểm cuối ủy quyền", "Authorization callback URL 填": "Điền URL gọi lại ủy quyền", + "Authorization Endpoint": "Điểm cuối ủy quyền", + "auto分组调用链路": "chuỗi gọi nhóm tự động", "Bark推送URL": "URL đẩy Bark", "Bark推送URL必须以http://或https://开头": "URL đẩy Bark phải bắt đầu bằng http:// hoặc https://", "Bark通知": "Thông báo Bark", "Basic Auth 头": "Header Basic Auth", - "CPU 使用率超过此值时拒绝请求": "Từ chối yêu cầu khi sử dụng CPU vượt quá giá trị này", - "CPU 阈值 (%)": "Ngưỡng CPU (%)", "Cached tokens": "Cached tokens", "Cached tokens 占比口径由后端返回:Claude 语义按 cached/(prompt+cached),其余按 cached/prompt。": "Tỷ lệ cached tokens được trả về từ backend: ngữ nghĩa Claude tính theo cached/(prompt+cached), còn lại tính theo cached/prompt.", "Changing batch type to:": "Đang thay đổi loại hàng loạt thành:", @@ -87,13 +111,20 @@ "Client Secret": "Client Secret", "Codex 授权": "Xác thực Codex", "Codex 渠道不支持批量创建": "Kênh Codex không hỗ trợ tạo hàng loạt", + "common.changeLanguage": "Thay đổi ngôn ngữ", "Completion tokens": "Completion tokens", "Configuration": "Cấu hình", + "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。": "context_int/context_string đọc từ context yêu cầu; gjson đọc từ JSON body yêu cầu đầu vào theo gjson path.", + "CPU 使用率超过此值时拒绝请求": "Từ chối yêu cầu khi sử dụng CPU vượt quá giá trị này", + "CPU 阈值 (%)": "Ngưỡng CPU (%)", "Creem API 密钥,敏感信息不显示": "Khóa API Creem, thông tin nhạy cảm không được hiển thị", "Creem Setting Tips": "Creem chỉ hỗ trợ các sản phẩm có số tiền cố định được thiết lập sẵn. Các sản phẩm này và giá của chúng cần được tạo và cấu hình trước trên trang web Creem, vì vậy việc nạp tiền số tiền động tùy chỉnh không được hỗ trợ. Cấu hình tên sản phẩm và giá trên Creem, lấy ID sản phẩm, sau đó điền vào sản phẩm bên dưới. Đặt số tiền nạp và giá hiển thị cho sản phẩm này trong API mới.", "Creem 介绍": "Creem là đối tác thanh toán mà bạn luôn xứng đáng có được, chúng tôi phấn đấu cho sự đơn giản và thẳng thắn trên các API của mình.", "Creem 充值": "Nạp tiền Creem", "Creem 设置": "Cài đặt Creem", + "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。": "default and vip can only be assigned to users by admin in \"User Management\". Suitable for tiered pricing, internal testing, or other scenarios where user self-selection is not desired.", + "default为默认设置,可单独设置每个分类的安全等级": "\"default\" là cài đặt mặc định, và mỗi danh mục có thể được đặt riêng", + "default为默认设置,可单独设置每个模型的版本": "\"default\" là cài đặt mặc định, và mỗi mô hình có thể được đặt riêng", "Dify渠道只适配chatflow和agent,并且agent不支持图片!": "Kênh Dify chỉ hỗ trợ chatflow và agent, và agent không hỗ trợ hình ảnh!", "Discord": "Discord", "Discord Client ID": "Discord Client ID", @@ -103,11 +134,10 @@ "Discovery scopes": "Discovery scopes", "Discovery 建议 scopes:": "Discovery scopes được đề xuất:", "EUR (欧元)": "EUR (Euro)", + "false": "sai", "GC 已执行": "GC đã thực thi", "GC 执行失败": "Thực thi GC thất bại", "GC 次数": "Số lần GC", - "GPU/容器": "GPU/Container", - "GPU数量": "Number of GPUs", "Gemini安全设置": "Cài đặt an toàn Gemini", "Gemini思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Thích ứng tư duy Gemini BudgetTokens = MaxTokens * Tỷ lệ phần trăm BudgetTokens", "Gemini思考适配设置": "Cài đặt thích ứng tư duy Gemini", @@ -122,10 +152,14 @@ "Gotify服务器地址": "Địa chỉ máy chủ Gotify", "Gotify服务器地址必须以http://或https://开头": "Địa chỉ máy chủ Gotify phải bắt đầu bằng http:// hoặc https://", "Gotify通知": "Thông báo Gotify", + "GPU/容器": "GPU/Container", + "GPU数量": "Number of GPUs", "Grok设置": "Cài đặt Grok", "Haiku 模型": "Model Haiku", "Homepage URL 填": "Điền URL trang chủ", "ID": "ID", + "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护": "include_obfuscation kiểm soát trường làm mờ trong luồng Responses. Mặc định tắt để tránh client vô hiệu hóa bảo vệ bảo mật này", + "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "Trường inference_geo kiểm soát vùng lưu trữ dữ liệu suy luận của Claude. Mặc định tắt để ngăn truyền thông tin địa lý trái phép", "IP": "IP", "IP白名单": "IP Whitelist", "IP白名单(支持CIDR表达式)": "Danh sách trắng IP (hỗ trợ biểu thức CIDR)", @@ -153,8 +187,8 @@ "LinuxDO": "LinuxDO", "LinuxDO ID": "LinuxDO ID", "Logo 图片地址": "Địa chỉ hình ảnh Logo", - "MIT许可证": "Giấy phép MIT", "Midjourney 任务记录": "Hồ sơ tác vụ Midjourney", + "MIT许可证": "Giấy phép MIT", "New API项目仓库地址:": "Địa chỉ kho dự án New API: ", "NewAPI 默认不会将入口请求的 User-Agent 透传到上游渠道;该条件仅用于识别访问本站点的客户端。": "NewAPI mặc định không truyền User-Agent của yêu cầu đến kênh upstream; điều kiện này chỉ dùng để nhận diện client truy cập trang web này.", "OAuth Client ID": "OAuth Client ID", @@ -165,7 +199,6 @@ "Ollama 模型管理": "Ollama Model Management", "Ollama 版本信息": "Ollama Version Info", "Opus 模型": "Model Opus", - "POST 参数": "Tham số POST", "Passkey": "Passkey", "Passkey 已解绑": "Đã xóa Passkey", "Passkey 已重置": "Passkey đã được đặt lại", @@ -173,51 +206,72 @@ "Passkey 注册失败,请重试": "Đăng ký Passkey thất bại. Vui lòng thử lại.", "Passkey 注册成功": "Đăng ký Passkey thành công", "Passkey 登录": "Đăng nhập Passkey", + "Pay Method Name": "", + "Pay Method Type": "", "Ping间隔(秒)": "Khoảng thời gian Ping (giây)", + "POST 参数": "Tham số POST", + "price_xxx 的商品价格 ID,新建产品后可获得": "ID giá sản phẩm cho price_xxx, có sẵn sau khi tạo sản phẩm mới", "Prompt cache hit tokens": "Prompt cache hit tokens", "Prompt tokens": "Prompt tokens", "Reasoning Effort": "Nỗ lực suy luận", "Recharge Quota": "Hạn ngạch nạp tiền", "Request ID": "Request ID", + "RSA 私钥 (沙盒)": "", + "RSA 私钥 (生产)": "", + "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "Trường safety_identifier giúp OpenAI xác định người dùng ứng dụng có thể vi phạm chính sách sử dụng. Tắt theo mặc định để bảo vệ quyền riêng tư của người dùng", + "Scopes(可选)": "Scopes (tùy chọn)", + "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "Trường service_tier được sử dụng để chỉ định cấp độ dịch vụ. Cho phép truyền qua có thể dẫn đến việc tính phí thực tế cao hơn dự kiến. Tắt theo mặc định để tránh phí bổ sung", + "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "Khóa Stripe cho sk_xxx hoặc rk_xxx, thông tin nhạy cảm không được hiển thị", "SMTP 发送者邮箱": "Email người gửi SMTP", "SMTP 服务器地址": "Địa chỉ máy chủ SMTP", "SMTP 端口": "Cổng SMTP", "SMTP 访问凭证": "Thông tin xác thực truy cập SMTP", "SMTP 账户": "Tài khoản SMTP", + "Sonnet 模型": "Model Sonnet", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "Trường speed kiểm soát chế độ tốc độ suy luận Claude. Mặc định tắt để tránh vô tình chuyển sang chế độ fast", "SSE 事件": "Sự kiện SSE", "SSE数据流": "Luồng dữ liệu SSE", "SSRF防护开关详细说明": "Công tắc chính kiểm soát xem bảo vệ SSRF có được bật hay không. Khi tắt, tất cả các kiểm tra SSRF sẽ bị bỏ qua, cho phép truy cập vào bất kỳ URL nào. ⚠️ Chỉ tắt tính năng này trong môi trường hoàn toàn tin cậy.", "SSRF防护设置": "Cài đặt bảo vệ SSRF", "SSRF防护详细说明": "Bảo vệ SSRF ngăn chặn người dùng độc hại sử dụng máy chủ của bạn để truy cập tài nguyên mạng nội bộ. Cấu hình danh sách trắng cho các tên miền/IP đáng tin cậy và hạn chế các cổng được phép. Áp dụng cho tải xuống tệp, webhook và thông báo.", - "Scopes(可选)": "Scopes (tùy chọn)", - "Sonnet 模型": "Model Sonnet", + "standard 已被移除,vip 用户看不到": "standard has been removed, vip users cannot see it", + "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "Trường store ủy quyền cho OpenAI lưu trữ dữ liệu yêu cầu để đánh giá và tối ưu hóa sản phẩm. Tắt theo mặc định. Bật có thể khiến Codex hoạt động không chính xác", "Stripe 设置": "Cài đặt Stripe", "Stripe/Creem 商品ID(可选)": "ID sản phẩm Stripe/Creem (tùy chọn)", "Stripe/Creem 需在第三方平台创建商品并填入 ID": "Sản phẩm Stripe/Creem phải được tạo trên nền tảng bên thứ ba và điền ID", - "TTL(秒)": "TTL (giây)", - "TTL(秒,0 表示默认)": "TTL (giây, 0 là mặc định)", "Telegram": "Telegram", "Telegram Bot Token": "Telegram Bot Token", "Telegram Bot 名称": "Tên Telegram Bot", "Telegram ID": "Telegram ID", "Token Endpoint": "Token Endpoint", + "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。": "Token được quy đổi thành hạn mức/số lần theo tỷ lệ. Sau khi yêu cầu hoàn tất, chênh lệch sẽ được quyết toán (trừ thêm/hoàn trả).", "Total tokens": "Total tokens", + "true": "đúng", + "TTL(秒,0 表示默认)": "TTL (giây, 0 là mặc định)", + "TTL(秒)": "TTL (giây)", "Turnstile Secret Key": "Turnstile Secret Key", "Turnstile Site Key": "Turnstile Site Key", - "URL 标识,只能包含小写字母、数字和连字符": "Định danh URL, chỉ cho phép chữ thường, số và dấu gạch ngang", - "URL链接": "Liên kết URL", - "USD (美元)": "USD (Đô la Mỹ)", "Unix时间戳": "Dấu thời gian Unix", "Uptime Kuma地址": "Địa chỉ Uptime Kuma", "Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)": "Quản lý danh mục giám sát Uptime Kuma, bạn có thể cấu hình nhiều danh mục giám sát để hiển thị trạng thái dịch vụ (tối đa 20)", + "URL 标识,只能包含小写字母、数字和连字符": "Định danh URL, chỉ cho phép chữ thường, số và dấu gạch ngang", + "URL链接": "Liên kết URL", + "USD (美元)": "USD (Đô la Mỹ)", "User Info Endpoint": "User Info Endpoint", "User-Agent include(每行一个,可不写)": "User-Agent include (mỗi dòng một mục, tùy chọn)", "Value 正则": "Regex giá trị", "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AI không hỗ trợ trường functionResponse.id. Khi bật, trường này sẽ tự động bị xóa", - "WeChat Server 服务器地址": "Địa chỉ máy chủ WeChat Server", - "WeChat Server 访问凭证": "Thông tin xác thực truy cập WeChat Server", - "Web 搜索调用 {{webSearchCallCount}} 次": "Đã gọi tìm kiếm Web {{webSearchCallCount}} lần", + "Waffo API 参数,可空,例如:CREDITCARD,DEBITCARD(最多64位)": "", + "Waffo API 参数,可空(最多64位)": "", + "Waffo 充值": "", + "Waffo 充值的最低数量,默认 1": "", + "Waffo 公钥 (沙盒)": "", + "Waffo 公钥 (生产)": "", + "Waffo 商户 ID": "", + "Waffo 是一个支付聚合平台,支持多种支付方式。": "", + "Waffo 设置": "", "Web 搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Tìm kiếm Web: {{count}} / 1K * đơn giá {{price}} * {{ratioType}} {{ratio}} = {{amount}}", + "Web 搜索调用 {{webSearchCallCount}} 次": "Đã gọi tìm kiếm Web {{webSearchCallCount}} lần", "Webhook 密钥": "Khóa Webhook", "Webhook 签名密钥": "Khóa chữ ký Webhook", "Webhook地址": "URL Webhook", @@ -225,62 +279,38 @@ "Webhook请求结构说明": "Mô tả cấu trúc yêu cầu Webhook", "Webhook通知": "Thông báo Webhook", "Web搜索价格:{{symbol}}{{price}} / 1K 次": "Giá tìm kiếm Web: {{symbol}}{{price}} / 1K yêu cầu", + "WeChat Server 服务器地址": "Địa chỉ máy chủ WeChat Server", + "WeChat Server 访问凭证": "Thông tin xác thực truy cập WeChat Server", "Well-Known URL": "Well-Known URL", "Well-Known URL 必须以 http:// 或 https:// 开头": "Well-Known URL phải bắt đầu bằng http:// hoặc https://", + "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示": "Khóa chữ ký Webhook cho whsec_xxx, thông tin nhạy cảm không được hiển thị", "Worker地址": "Địa chỉ Worker", "Worker密钥": "Khóa Worker", - "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "Giá trị tối đa của [Số lần yêu cầu tối đa] và [Số lần hoàn thành yêu cầu tối đa] là 2147483647.", - "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[Số lần yêu cầu tối đa] phải lớn hơn hoặc bằng 0, [Số lần hoàn thành yêu cầu tối đa] phải lớn hơn hoặc bằng 1.", - "auto分组调用链路": "chuỗi gọi nhóm tự động", - "common.changeLanguage": "Thay đổi ngôn ngữ", - "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。": "context_int/context_string đọc từ context yêu cầu; gjson đọc từ JSON body yêu cầu đầu vào theo gjson path.", - "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。": "default and vip can only be assigned to users by admin in \"User Management\". Suitable for tiered pricing, internal testing, or other scenarios where user self-selection is not desired.", - "default为默认设置,可单独设置每个分类的安全等级": "\"default\" là cài đặt mặc định, và mỗi danh mục có thể được đặt riêng", - "default为默认设置,可单独设置每个模型的版本": "\"default\" là cài đặt mặc định, và mỗi mô hình có thể được đặt riêng", - "false": "sai", - "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护": "include_obfuscation kiểm soát trường làm mờ trong luồng Responses. Mặc định tắt để tránh client vô hiệu hóa bảo vệ bảo mật này", - "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "Trường inference_geo kiểm soát vùng lưu trữ dữ liệu suy luận của Claude. Mặc định tắt để ngăn truyền thông tin địa lý trái phép", - "price_xxx 的商品价格 ID,新建产品后可获得": "ID giá sản phẩm cho price_xxx, có sẵn sau khi tạo sản phẩm mới", - "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "Trường safety_identifier giúp OpenAI xác định người dùng ứng dụng có thể vi phạm chính sách sử dụng. Tắt theo mặc định để bảo vệ quyền riêng tư của người dùng", - "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "Trường service_tier được sử dụng để chỉ định cấp độ dịch vụ. Cho phép truyền qua có thể dẫn đến việc tính phí thực tế cao hơn dự kiến. Tắt theo mặc định để tránh phí bổ sung", - "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "Trường speed kiểm soát chế độ tốc độ suy luận Claude. Mặc định tắt để tránh vô tình chuyển sang chế độ fast", - "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "Khóa Stripe cho sk_xxx hoặc rk_xxx, thông tin nhạy cảm không được hiển thị", - "standard 已被移除,vip 用户看不到": "standard has been removed, vip users cannot see it", - "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "Trường store ủy quyền cho OpenAI lưu trữ dữ liệu yêu cầu để đánh giá và tối ưu hóa sản phẩm. Tắt theo mặc định. Bật có thể khiến Codex hoạt động không chính xác", - "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。": "Token được quy đổi thành hạn mức/số lần theo tỷ lệ. Sau khi yêu cầu hoàn tất, chênh lệch sẽ được quyết toán (trừ thêm/hoàn trả).", - "true": "đúng", - "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示": "Khóa chữ ký Webhook cho whsec_xxx, thông tin nhạy cảm không được hiển thị", - "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", - "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", - "{{name}} ID": "{{name}} ID", - "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", - "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", - "| 基于": " | Dựa trên ", - "© {{currentYear}}": "© {{currentYear}}", - "• 视频服务商的跨域限制": "• Cross-origin limitations from the video provider", - "• 防盗链保护机制": "• Hotlink protection mechanisms", - "• 需要特定的请求头或认证": "• Specific headers or authentication are required", - "「用户可选」决定用户创建令牌时能否自主选择该分组。未勾选的分组只能由管理员在后台分配给用户,不会出现在用户的令牌创建页面中。": "\"User Selectable\" controls whether users can choose this group when creating tokens. Unchecked groups can only be assigned by admin and won't appear in the token creation page.", "一个月": "Một tháng", "一天": "Một ngày", "一小时": "Một giờ", "一次调用消耗多少刀,优先级大于模型倍率": "Một cuộc gọi tốn bao nhiêu USD, ưu tiên hơn tỷ lệ mô hình", - "一行一个屏蔽词,不需要符号分割": "Mỗi dòng một từ bị chặn, không cần ký hiệu phân cách", "一行一个,不区分大小写": "Mỗi dòng một cái, không phân biệt chữ hoa chữ thường", + "一行一个屏蔽词,不需要符号分割": "Mỗi dòng một từ bị chặn, không cần ký hiệu phân cách", "一键填充到 FluentRead": "Điền vào FluentRead bằng một cú nhấp chuột", "三种操作的区别:": "Differences between the three operations:", "上一个表单块": "Khối biểu mẫu trước", "上一步": "Trước", + "上传 PNG/JPG/SVG 图片,建议尺寸 ≤ 128×128px": "", + "上传图片": "", "上次保存: ": "Lần lưu cuối: ", + "上次检测到可加入模型": "", + "上次检测时间": "", "上游倍率同步": "Đồng bộ hóa tỷ lệ thượng nguồn", "上游模型管理": "Quản lý mô hình thượng nguồn", "上游返回": "Upstream response", "下一个表单块": "Khối biểu mẫu tiếp theo", + "下一次重置": "Đặt lại tiếp theo", "下一步": "Tiếp theo", "下午好": "Chào buổi chiều", "下载日志": "Download Logs", "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "Bên dưới hiển thị các trường backend sẽ được ghi sau khi lưu, giúp bạn dễ đối chiếu với ô chỉnh sửa JSON gốc.", + "下面是带注释的示例,仅用于参考;实际保存时请删除注释。": "", "不会出现": "will not appear", "不再提醒": "Không nhắc lại", "不勾选用户可选": "Not user-selectable", @@ -305,10 +335,10 @@ "两次输入的密码不一致": "Hai mật khẩu đã nhập không khớp", "两次输入的密码不一致!": "Mật khẩu nhập hai lần không nhất quán!", "两步验证": "Xác thực hai yếu tố", + "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。": "Xác thực hai yếu tố (2FA) cung cấp bảo vệ bảo mật bổ sung cho tài khoản của bạn. Sau khi bật, bạn cần nhập mật khẩu và mã xác minh được tạo bởi ứng dụng xác thực khi đăng nhập.", "两步验证启用成功!": "Đã bật xác thực hai yếu tố thành công!", "两步验证已禁用": "Xác thực hai yếu tố đã bị vô hiệu hóa", "两步验证设置": "Cài đặt xác thực hai yếu tố", - "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。": "Xác thực hai yếu tố (2FA) cung cấp bảo vệ bảo mật bổ sung cho tài khoản của bạn. Sau khi bật, bạn cần nhập mật khẩu và mã xác minh được tạo bởi ứng dụng xác thực khi đăng nhập.", "个": "cái", "个GPU": " GPUs", "个人中心": "Trung tâm cá nhân", @@ -365,6 +395,7 @@ "仅对自定义模型有效": "Chỉ hiệu quả đối với các mô hình tùy chỉnh", "仅当前层": "Chỉ cấp hiện tại", "仅当自动禁用开启时有效,关闭后不会自动禁用该渠道": "Chỉ hiệu quả khi bật tự động vô hiệu hóa, sau khi đóng, kênh sẽ không bị tự động vô hiệu hóa", + "仅提交已勾选": "", "仅支持": "Chỉ hỗ trợ", "仅支持 JSON 对象,必须包含 access_token 与 account_id": "Chỉ hỗ trợ đối tượng JSON, phải bao gồm access_token và account_id", "仅支持 JSON 文件": "Chỉ hỗ trợ tệp JSON", @@ -372,14 +403,17 @@ "仅支持 OpenAI 接口格式": "Chỉ hỗ trợ định dạng giao diện OpenAI", "仅显示已绑定": "Chỉ hiển thị đã liên kết", "仅显示矛盾倍率": "Chỉ hiển thị tỷ lệ mâu thuẫn", + "仅检测上游模型更新": "", "仅用于开发环境,生产环境应使用 HTTPS": "Chỉ dành cho phát triển, sử dụng HTTPS trong sản xuất", "仅用于换算,实际保存的是额度": "Chỉ dùng để quy đổi, giá trị lưu thực tế là hạn ngạch", "仅用订阅": "Chỉ dùng đăng ký", "仅用钱包": "Chỉ dùng ví", + "仅管理员可用。开启后,当系统定时检测全部渠道发现上游模型变更或检测异常时,将按你选择的通知方式发送汇总通知;渠道或模型过多时会自动省略部分明细。": "", "仅重置配置": "Chỉ đặt lại cấu hình", "今日关闭": "Đóng hôm nay", "今日已签到": "Đã đăng nhập hôm nay", "今日已签到,累计签到": "Đã đăng nhập hôm nay, tổng số lần đăng nhập", + "仍有未处理项": "", "从 0.5 降到 0.3": "reduced from 0.5 to 0.3", "从剪贴板粘贴配置": "Dán cấu hình", "从官方模型库同步": "Đồng bộ từ thư viện mô hình chính thức", @@ -390,10 +424,11 @@ "代理设置": "Cài đặt proxy", "代码已复制到剪贴板": "Mã đã được sao chép vào khay nhớ tạm", "令牌": "Mã thông báo", + "令牌不存在": "", "令牌分组": "Nhóm mã thông báo", + "令牌分组,默认为用户的分组": "Nhóm mã thông báo, mặc định là nhóm của bạn", "令牌分组设为 auto 时,按以下顺序依次尝试选择可用分组,排在前面的优先级更高": "When token group is set to auto, groups are selected in order of priority, with higher priority groups listed first", "令牌分组设为 auto 时,系统按优先级顺序自动选择一个可用分组。": "When token group is set to auto, the system automatically selects an available group by priority.", - "令牌分组,默认为用户的分组": "Nhóm mã thông báo, mặc định là nhóm của bạn", "令牌创建成功,请在列表页面点击复制获取令牌!": "Tạo mã thông báo thành công, vui lòng nhấp vào sao chép trên trang danh sách để lấy mã thông báo!", "令牌名称": "Tên mã thông báo", "令牌已重置并已复制到剪贴板": "Mã thông báo đã được đặt lại và sao chép vào khay nhớ tạm", @@ -405,8 +440,11 @@ "以及": "và", "仪表盘设置": "Bảng điều khiển", "价格": "Giá cả", + "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "Price: {{symbol}}{{price}} * {{ratioType}}: {{ratio}}", + "价格:${{price}} * {{ratioType}}:{{ratio}}": "Giá: ${{price}} * {{ratioType}}: {{ratio}}", "价格摘要": "Tóm tắt giá", "价格暂时不可用,请稍后重试": "Price temporarily unavailable, please try again later", + "价格模式": "", "价格模式(默认)": "Chế độ giá (mặc định)", "价格计算中...": "Calculating price...", "价格计算失败": "Price calculation failed", @@ -415,8 +453,6 @@ "价格设置方式": "Phương thức cấu hình giá", "价格重新计算中...": "Recalculating price...", "价格预估": "Price Estimate", - "价格:${{price}} * {{ratioType}}:{{ratio}}": "Giá: ${{price}} * {{ratioType}}: {{ratio}}", - "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "Price: {{symbol}}{{price}} * {{ratioType}}: {{ratio}}", "任一满足(OR)": "Bất kỳ khớp (OR)", "任务 ID": "ID tác vụ", "任务ID": "ID tác vụ", @@ -433,7 +469,6 @@ "余额充值管理": "Quản lý nạp tiền số dư", "作废": "Vô hiệu", "作废于": "Vô hiệu vào", - "下一次重置": "Đặt lại tiếp theo", "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "Sau khi vô hiệu, đăng ký sẽ mất hiệu lực ngay. Lịch sử không bị ảnh hưởng. Tiếp tục?", "作用域": "Phạm vi", "作用域:包含分组": "Phạm vi: Bao gồm nhóm", @@ -441,6 +476,8 @@ "作用域:包含规则名称": "Phạm vi: Bao gồm tên quy tắc", "你似乎并没有修改什么": "Bạn dường như không sửa đổi gì cả", "你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "You can manually add them under “Custom model names”, click Fill and submit, or use the actions below to handle them automatically.", + "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?_other": "", + "使用 {{name}} 继续": "Tiếp tục với {{name}}", "使用 Discord 继续": "Continue with Discord", "使用 GitHub 继续": "Tiếp tục với GitHub", "使用 JSON 对象格式,格式为:{\"组名\": [最多请求次数, 最多请求完成次数]}": "Sử dụng định dạng đối tượng JSON, định dạng: {\"group_name\": [max_requests, max_completions]}", @@ -449,43 +486,44 @@ "使用 Passkey 实现免密且更安全的登录体验": "Sử dụng Passkey để trải nghiệm đăng nhập không cần mật khẩu và an toàn hơn", "使用 Passkey 登录": "Đăng nhập bằng Passkey", "使用 Passkey 验证": "Xác minh bằng Passkey", - "使用 {{name}} 继续": "Tiếp tục với {{name}}", "使用 微信 继续": "Tiếp tục với WeChat", "使用 用户名 注册": "Đăng ký bằng Tên người dùng", "使用 邮箱或用户名 登录": "Đăng nhập bằng Email hoặc Tên người dùng", "使用ID排序": "Sắp xếp theo ID", "使用分组": "Using Group", + "使用原生额度输入": "Nhập hạn mức gốc", "使用日志": "Nhật ký sử dụng", "使用模式": "Chế độ sử dụng", "使用统计": "Thống kê sử dụng", - "使用认证器应用扫描二维码": "Quét mã QR bằng ứng dụng xác thực", "使用认证器应用(如 Google Authenticator、Microsoft Authenticator)扫描下方二维码:": "Sử dụng ứng dụng xác thực (như Google Authenticator, Microsoft Authenticator) để quét mã QR bên dưới:", + "使用认证器应用扫描二维码": "Quét mã QR bằng ứng dụng xác thực", "使用说明": "Guide", "例如 /var/cache/new-api": "VD: /var/cache/new-api", - "例如 https://docs.newapi.pro": "Ví dụ, https://docs.newapi.pro", "例如 €, £, Rp, ₩, ₹...": "Ví dụ, €, £, Rp, ₩, ₹...", + "例如 https://docs.newapi.pro": "Ví dụ, https://docs.newapi.pro", + "例如 https://example.com/api/waffo/webhook": "", + "例如 https://example.com/console/topup": "", + "例如:": "Ví dụ:", "例如: /bin/bash -c \"python app.py\"": "e.g.: /bin/bash -c \"python app.py\"", "例如: nginx:latest": "e.g.: nginx:latest", "例如: socks5://user:pass@host:port": "ví dụ: socks5://user:pass@host:port", - "例如发卡网站的购买链接": "Ví dụ, liên kết mua hàng từ trang web phát hành thẻ", - "例如(全渠道):": "Ví dụ (tất cả kênh):", - "例如(指定渠道):": "Ví dụ (kênh chỉ định):", - "例如:": "Ví dụ:", "例如:-c": "e.g.: -c", "例如:/bin/bash": "e.g.: /bin/bash", "例如:0001": "ví dụ: 0001", "例如:1000": "ví dụ: 1000", "例如:100000": "Ví dụ: 100000", - "例如:2000": "ví dụ: 2000", "例如:2,就是最低充值2$": "ví dụ: 2, nghĩa là nạp tối thiểu $2", + "例如:2000": "ví dụ: 2000", "例如:4.99": "Ví dụ: 4.99", "例如:401, 403, 429, 500-599": "VD: 401, 403, 429, 500-599", "例如:7,就是7元/美金": "ví dụ: 7, nghĩa là 7 tệ mỗi USD", - "例如:GitHub Enterprise": "VD: GitHub Enterprise", + "例如:Credit Card": "", "例如:email": "VD: email", "例如:example.com": "ví dụ: example.com", "例如:github / si:google / https://example.com/logo.png / 🐱": "VD: github / si:google / https://example.com/logo.png / 🐱", + "例如:GitHub Enterprise": "VD: GitHub Enterprise", "例如:github-enterprise": "VD: github-enterprise", + "例如:gpt-4.1-nano,regex:^claude-.*$,regex:^sora-.*$": "", "例如:https://example.com/.well-known/openid-configuration": "VD: https://example.com/.well-known/openid-configuration", "例如:https://gitea.example.com": "VD: https://gitea.example.com", "例如:https://yourdomain.com": "ví dụ: https://yourdomain.com", @@ -496,9 +534,13 @@ "例如:prod_6I8rBerHpPxyoiU9WK4kot": "Ví dụ: prod_6I8rBerHpPxyoiU9WK4kot", "例如:sub、id、data.user.id": "VD: sub, id, data.user.id", "例如:基础套餐": "Ví dụ: Gói cơ bản", + "例如:清理工具参数,避免上游校验错误": "", "例如:该请求不满足准入策略": "VD: Yêu cầu này không đáp ứng chính sách tiếp nhận", "例如:适合轻度使用": "Ví dụ: Phù hợp dùng nhẹ", "例如:需要等级 {{required}},你当前等级 {{current}}": "VD: Yêu cầu cấp {{required}}, cấp hiện tại của bạn là {{current}}", + "例如(全渠道):": "Ví dụ (tất cả kênh):", + "例如(指定渠道):": "Ví dụ (kênh chỉ định):", + "例如发卡网站的购买链接": "Ví dụ, liên kết mua hàng từ trang web phát hành thẻ", "供应商": "Nhà cung cấp", "供应商介绍": "Giới thiệu nhà cung cấp", "供应商信息:": "Thông tin nhà cung cấp:", @@ -509,6 +551,8 @@ "供应商更新成功!": "Cập nhật nhà cung cấp thành công!", "侧边栏管理(全局控制)": "Quản lý thanh bên (Kiểm soát toàn cầu)", "侧边栏设置保存成功": "Đã lưu cài đặt thanh bên thành công", + "保前缀": "", + "保后缀": "", "保存": "Lưu", "保存 Discord OAuth 设置": "Save Discord OAuth Settings", "保存 GitHub OAuth 设置": "Lưu cài đặt GitHub OAuth", @@ -524,8 +568,8 @@ "保存备用码": "Lưu mã dự phòng", "保存备用码以备不时之需": "Lưu mã dự phòng cho trường hợp khẩn cấp", "保存失败": "Lưu thất bại", - "保存失败:": "Lưu thất bại:", "保存失败,请重试": "Lưu thất bại, vui lòng thử lại", + "保存失败:": "Lưu thất bại:", "保存屏蔽词过滤设置": "Lưu cài đặt lọc từ bị chặn", "保存性能设置": "Lưu cài đặt hiệu suất", "保存成功": "Lưu thành công", @@ -543,11 +587,11 @@ "保存预览": "Xem trước khi lưu", "保存额度设置": "Lưu cài đặt hạn ngạch", "保留": "kept", + "保留最近N个文件": "Giữ lại N tệp gần nhất", + "保留最近N天": "Giữ lại N ngày gần nhất", "保留原值(目标已有值时不覆盖)": "Giữ giá trị gốc (không ghi đè nếu mục tiêu đã có giá trị)", "保留天数": "Số ngày giữ lại", "保留文件数": "Số tệp giữ lại", - "保留最近N个文件": "Giữ lại N tệp gần nhất", - "保留最近N天": "Giữ lại N ngày gần nhất", "修复数据库一致性": "Sửa chữa tính nhất quán của cơ sở dữ liệu", "修改为": "Sửa đổi thành", "修改子渠道优先级": "Sửa đổi ưu tiên kênh phụ", @@ -559,6 +603,7 @@ "倍率信息": "Thông tin tỷ lệ", "倍率是为了方便换算不同价格的模型": "Độ phóng đại là để tạo điều kiện chuyển đổi các mô hình có giá khác nhau.", "倍率模式": "Chế độ tỷ lệ", + "倍率模式(默认)": "", "倍率用于计费乘数,勾选「用户可选」后用户可在创建令牌时选择该分组": "Ratio is the billing multiplier. Check \"User Selectable\" to let users pick this group when creating tokens", "倍率类型": "Loại tỷ lệ", "假设再加两个分组 default 和 vip,但不勾选用户可选:": "Now add two more groups default and vip, but without checking User Selectable:", @@ -568,12 +613,12 @@ "停用": "Vô hiệu hóa", "允许 AccountFilter 参数": "Cho phép tham số AccountFilter", "允许 HTTP 协议图片请求(适用于自部署代理)": "Cho phép yêu cầu hình ảnh giao thức HTTP (đối với proxy tự triển khai)", - "允许 Turnstile 用户校验": "Cho phép xác minh người dùng Turnstile", "允许 inference_geo 透传": "Cho phép truyền inference_geo", "允许 safety_identifier 透传": "Cho phép safety_identifier truyền qua", "允许 service_tier 透传": "Cho phép service_tier truyền qua", "允许 speed 透传": "Cho phép truyền speed", "允许 stream_options.include_obfuscation 透传": "Cho phép truyền stream_options.include_obfuscation", + "允许 Turnstile 用户校验": "Cho phép xác minh người dùng Turnstile", "允许不安全的 Origin(HTTP)": "Cho phép Origin không an toàn (HTTP)", "允许回调(会泄露服务器 IP 地址)": "Cho phép gọi lại (sẽ làm lộ địa chỉ IP máy chủ)", "允许在 Stripe 支付中输入促销码": "Cho phép nhập mã khuyến mãi khi thanh toán Stripe", @@ -594,13 +639,13 @@ "允许重试": "Cho phép thử lại", "元": "CNY", "充值": "Nạp tiền", - "充值价格显示": "Giá nạp", "充值价格(x元/美金)": "Giá nạp (x tệ/đô la)", + "充值价格显示": "Giá nạp", "充值分组倍率": "Tỷ lệ nhóm nạp tiền", "充值分组倍率不是合法的 JSON 字符串": "Tỷ lệ nhóm nạp tiền không phải là chuỗi JSON hợp lệ", "充值数量": "Số lượng nạp", - "充值数量不能小于": "Số tiền nạp không được nhỏ hơn", "充值数量,最低 ": "Số lượng nạp, tối thiểu", + "充值数量不能小于": "Số tiền nạp không được nhỏ hơn", "充值方式设置": "Cài đặt phương thức nạp tiền", "充值方式设置不是合法的 JSON 字符串": "Cài đặt phương thức nạp tiền không phải là chuỗi JSON hợp lệ", "充值确认": "Xác nhận nạp tiền", @@ -616,8 +661,8 @@ "兑换成功!": "Đổi thành công!", "兑换码充值": "Nạp tiền bằng mã đổi thưởng", "兑换码创建成功": "Đã tạo mã đổi thưởng", - "兑换码创建成功!": "Tạo mã đổi thưởng thành công!", "兑换码创建成功,是否下载兑换码?": "Tạo mã đổi thưởng thành công. Bạn có muốn tải xuống không?", + "兑换码创建成功!": "Tạo mã đổi thưởng thành công!", "兑换码将以文本文件的形式下载,文件名为兑换码的名称。": "Mã đổi thưởng sẽ được tải xuống dưới dạng tệp văn bản, với tên tệp là tên mã đổi thưởng.", "兑换码更新成功!": "Cập nhật mã đổi thưởng thành công!", "兑换码生成管理": "Quản lý tạo mã đổi thưởng", @@ -626,6 +671,7 @@ "全局控制侧边栏区域和功能显示,管理员隐藏的功能用户无法启用": "Kiểm soát toàn cầu các khu vực và chức năng thanh bên, người dùng không thể bật các chức năng bị quản trị viên ẩn", "全局设置": "Cài đặt toàn cầu", "全选": "Chọn tất cả", + "全选当前列表模型": "", "全部": "Tất cả", "全部供应商": "Tất cả nhà cung cấp", "全部分组": "Tất cả các nhóm", @@ -662,8 +708,8 @@ "关闭": "Đóng", "关闭侧边栏": "Đóng thanh bên", "关闭公告": "Đóng thông báo", - "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "Sau khi đóng, thông báo này sẽ không còn hiển thị nữa (chỉ với trình duyệt này). Bạn có chắc muốn đóng không?", "关闭后,此模型将不会被“同步官方”自动覆盖或创建": "Sau khi đóng, mô hình này sẽ không tự động bị ghi đè hoặc tạo bởi \"Đồng bộ chính thức\"", + "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "Sau khi đóng, thông báo này sẽ không còn hiển thị nữa (chỉ với trình duyệt này). Bạn có chắc muốn đóng không?", "关闭弹窗,已停止批量测试": "Đã đóng hộp thoại, đã dừng kiểm tra hàng loạt", "关闭提示": "Đóng thông báo", "其他": "Khác", @@ -681,11 +727,25 @@ "内置": "Tích hợp sẵn", "内置 Ollama 镜像": "Built-in Ollama Image", "再次输入部署名称": "Enter Deployment Name Again", + "最低": "thấp nhất", + "最低充值数量": "", + "最低充值美元数量": "Số tiền nạp đô la tối thiểu", + "最后使用时间": "Thời gian sử dụng cuối cùng", + "最后更新": "Last Updated", + "最后请求": "Yêu cầu cuối cùng", + "最大GPU数量": "Max Number of GPUs", + "最大可用": "Max Available", + "最大条目数": "Số mục tối đa", + "最终抵扣": "Khấu trừ cuối cùng", + "最近一次": "Lần gần nhất", + "最近事件": "Recent Events", + "最高优先级": "highest priority", "写": "Ghi", "准入策略": "Chính sách tiếp nhận", "准入策略 JSON(可选)": "JSON chính sách tiếp nhận (tùy chọn)", "准备中...": "Preparing...", "准备完成初始化": "Sẵn sàng hoàn tất khởi tạo", + "减少": "Giảm", "凭证已刷新": "Thông tin xác thực đã được làm mới", "分类名称": "Tên danh mục", "分组": "Nhóm", @@ -738,17 +798,20 @@ "创建用户": "Tạo người dùng", "初始化失败,请重试": "Khởi tạo thất bại, vui lòng thử lại", "初始化系统": "Khởi tạo hệ thống", + "删请求头": "", "删除": "Xóa", "删除 Key 来源": "Xóa nguồn Key", "删除会彻底移除该订阅记录(含权益明细)。是否继续?": "Xóa sẽ loại bỏ hoàn toàn bản ghi đăng ký (bao gồm chi tiết quyền lợi). Tiếp tục?", "删除后无法恢复,确定要删除模型 \"{{name}}\" 吗?": "Cannot be recovered after deletion, are you sure you want to delete model \"{{name}}\"?", "删除失败": "Xóa thất bại", "删除密钥失败": "Xóa khóa thất bại", + "删除已选 {{selected}} / {{total}}": "", "删除成功": "Xóa thành công", "删除所选": "Xóa đã chọn", "删除所选令牌": "Xóa mã thông báo đã chọn", "删除所选通道": "Xóa các kênh đã chọn", "删除条件": "Xóa điều kiện", + "删除模型": "", "删除禁用密钥失败": "Xóa khóa bị vô hiệu hóa thất bại", "删除禁用通道": "Xóa kênh bị vô hiệu hóa", "删除自动禁用密钥": "Xóa khóa tự động bị vô hiệu hóa", @@ -765,19 +828,20 @@ "刷新缓存统计": "Làm mới thống kê bộ nhớ đệm", "刷新缓存统计失败": "Làm mới thống kê bộ nhớ đệm thất bại", "刷新页面": "Tải lại trang", + "前:": "Trước:", "前往 io.net API Keys": "Go to io.net API Keys", "前往设置": "Go to Settings", "前往设置页面": "Go to Settings Page", "前缀": "Tiền tố", - "前:": "Trước:", + "前置": "", + "剪贴板中未检测到连接信息": "Không tìm thấy thông tin kết nối trong bộ nhớ tạm", + "副本数量": "Number of Replicas", "剩余": "Remaining", "剩余备用码:": "Mã dự phòng còn lại: ", "剩余时间": "Remaining Time", "剩余额度": "Hạn ngạch còn lại", - "剩余额度$": "Hạn ngạch còn lại $", "剩余额度/总额度": "Còn lại/Tổng cộng", - "剪贴板中未检测到连接信息": "Không tìm thấy thông tin kết nối trong bộ nhớ tạm", - "副本数量": "Number of Replicas", + "剩余额度$": "Hạn ngạch còn lại $", "功能特性": "Tính năng", "加入渠道": "Join Channel", "加入预填组": "Tham gia nhóm điền sẵn", @@ -809,6 +873,7 @@ "区域": "Khu vực", "升级分组": "Nhóm nâng cấp", "单GPU小时费率": "Per GPU Hour Rate", + "单价 (USD)": "", "历史消耗": "Tiêu thụ", "原价": "Giá gốc", "原价,和普通用户一样": "original price, same as regular users", @@ -816,19 +881,23 @@ "原密码": "Mật khẩu cũ", "原生格式": "Định dạng gốc", "原生额度": "Hạn mức gốc", - "使用原生额度输入": "Nhập hạn mức gốc", - "收起原生额度输入": "Ẩn nhập hạn mức gốc", + "去前缀": "", + "去后缀": "", + "去处理{{type}}": "", + "去空格": "", "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "Hoàn tất loại bỏ trùng lặp: {{before}} khóa trước khi loại bỏ, {{after}} khóa sau khi loại bỏ", "参与官方同步": "Tham gia đồng bộ chính thức", "参数": "tham số", "参数值": "Giá trị tham số", "参数覆盖": "Ghi đè tham số", "参数覆盖 JSON 已复制": "JSON ghi đè tham số đã được sao chép", + "参数覆盖已复制": "", "参数覆盖必须是合法的 JSON 对象": "Ghi đè tham số phải là đối tượng JSON hợp lệ", "参数覆盖必须是合法的 JSON 格式!": "Ghi đè tham số phải ở định dạng JSON hợp lệ!", "参数覆盖模板": "Mẫu ghi đè tham số", "参数覆盖模板 JSON 格式不正确": "Định dạng JSON mẫu ghi đè tham số không chính xác", "参数覆盖模板预览": "Xem trước mẫu ghi đè tham số", + "参数覆盖详情": "", "参数配置": "Cấu hình tham số", "参数配置有误": "Cấu hình tham số không hợp lệ", "参数错误": "Lỗi tham số", @@ -842,6 +911,7 @@ "取消全选": "Bỏ chọn tất cả", "取消选择": "Deselect", "变换": "Biến đổi", + "变更": "Thay đổi", "变焦": "thu phóng", "变量值": "Variable Value", "变量名": "Variable Name", @@ -852,6 +922,7 @@ "可以根据用户分组增减令牌可选的分组范围。例如 vip 用户额外开放 premium 分组,或移除某个分组的选择权。": "Adjust the selectable group range based on user group. E.g., grant vip users access to premium, or remove access to a group.", "可以根据用户分组设置不同的计费倍率。例如 vip 用户使用 standard 令牌时倍率从 1.0 降为 0.8。": "Set different billing ratios based on user group. E.g., vip users using standard tokens get ratio 0.8 instead of 1.0.", "可信": "Đáng tin cậy", + "可勾选需要执行的变更:新增会加入渠道模型列表,删除会从渠道模型列表移除。": "", "可在设置页面设置关于内容,支持 HTML & Markdown": "Nội dung Giới thiệu có thể được đặt trên trang cài đặt, hỗ trợ HTML & Markdown", "可手动填写,多个 scope 用空格分隔": "Có thể điền thủ công, nhiều scope phân cách bằng dấu cách", "可用": "Khả dụng", @@ -867,14 +938,15 @@ "可视化": "Trực quan hóa", "可视化倍率设置": "Cài đặt tỷ lệ mô hình trực quan", "可视化编辑": "Chỉnh sửa trực quan", - "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。": "Tùy chọn. Khớp User-Agent của yêu cầu đầu vào; bất kỳ dòng nào khớp dưới dạng chuỗi con (không phân biệt hoa thường) được tính là trúng.", - "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。": "Tùy chọn. Xác thực key ưu ái đã trích xuất bằng regex; để trống để bỏ qua xác thực.", - "可选。对请求路径进行匹配;不填表示匹配所有路径。": "Tùy chọn. Khớp đường dẫn yêu cầu; để trống để khớp tất cả đường dẫn.", - "可选值": "Giá trị tùy chọn", + "可空": "", "可选,公告的补充说明": "Tùy chọn, thông tin bổ sung cho thông báo", "可选,用于复现结果": "Tùy chọn, để tái tạo kết quả", "可选:基于用户信息 JSON 做组合条件准入,条件不满足时返回自定义提示": "Tùy chọn: Tiếp nhận dựa trên điều kiện kết hợp từ JSON thông tin người dùng; trả về thông báo tùy chỉnh khi điều kiện không được đáp ứng", "可选:用于自动生成端点或 Discovery URL": "Tùy chọn: Dùng để tự động tạo endpoint hoặc Discovery URL", + "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。": "Tùy chọn. Khớp User-Agent của yêu cầu đầu vào; bất kỳ dòng nào khớp dưới dạng chuỗi con (không phân biệt hoa thường) được tính là trúng.", + "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。": "Tùy chọn. Xác thực key ưu ái đã trích xuất bằng regex; để trống để bỏ qua xác thực.", + "可选。对请求路径进行匹配;不填表示匹配所有路径。": "Tùy chọn. Khớp đường dẫn yêu cầu; để trống để khớp tất cả đường dẫn.", + "可选值": "Giá trị tùy chọn", "合计:{{total}}": "Tổng cộng: {{total}}", "合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "Tổng cộng: phần văn bản {{textTotal}} + phần âm thanh {{audioTotal}} = {{total}}", "同时重置消息": "Đặt lại tin nhắn đồng thời", @@ -882,6 +954,7 @@ "同步到渠道": "Sync to Channel", "同步向导": "Trình hướng dẫn đồng bộ", "同步失败": "Đồng bộ hóa thất bại", + "同步字段": "", "同步成功": "Đồng bộ hóa thành công", "同步接口": "Giao diện đồng bộ hóa", "同步渠道失败": "Failed to sync channel", @@ -891,10 +964,11 @@ "名称+密钥": "Tên + Khóa", "名称不能为空": "Tên không được để trống", "名称匹配类型": "Loại khớp tên", + "后:": "Sau:", + "后端固定": "", "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "Tỷ lệ cố định từ backend: {{ratio}}. Trường này chỉ hiển thị giá sau khi quy đổi.", "后端请求失败": "Yêu cầu phụ trợ thất bại", "后缀": "Hậu tố", - "后:": "Sau:", "向右展开": "Mở rộng sang phải", "向左展开": "Mở rộng sang trái", "否": "Không", @@ -907,17 +981,17 @@ "启动部署失败": "Failed to start deployment", "启动配置": "Startup Configuration", "启用": "Bật", - "启用 Prompt 检查": "Bật kiểm tra Prompt", "启用 io.net 部署": "Enable io.net Deployment", "启用 io.net 部署开关": "Enable io.net Deployment Switch", "启用 io.net 部署时必须填写 API Key": "API Key is required when enabling io.net deployment", + "启用 Prompt 检查": "Bật kiểm tra Prompt", + "启用 Waffo": "", "启用2FA失败": "Bật xác thực hai yếu tố thất bại", "启用Claude思考适配(-thinking后缀)": "Bật thích ứng tư duy Claude (hậu tố -thinking)", "启用FunctionCall思维签名填充": "Bật điền chữ ký tư duy FunctionCall", "启用Gemini思考后缀适配": "Bật thích ứng hậu tố tư duy Gemini", "启用Ping间隔": "Bật khoảng thời gian Ping", "启用SMTP SSL": "Bật SMTP SSL", - "强制使用 AUTH LOGIN": "Buộc AUTH LOGIN", "启用SSRF防护(推荐开启以保护服务器安全)": "Bật bảo vệ SSRF (Khuyên dùng để bảo mật máy chủ)", "启用供应商": "Bật nhà cung cấp", "启用全部": "Bật tất cả", @@ -926,6 +1000,7 @@ "启用后套餐将在用户端展示。是否继续?": "Sau khi bật, gói sẽ hiển thị cho người dùng. Tiếp tục?", "启用后将优先复用上一次成功的渠道(粘滞选路)。": "Khi bật, kênh thành công lần cuối sẽ được ưu tiên tái sử dụng (định tuyến dính).", "启用后将使用 Creem Test Mode": "Sau khi bật, Chế độ kiểm tra Creem sẽ được sử dụng", + "启用后将使用 Waffo 沙盒环境": "", "启用密钥失败": "Bật khóa thất bại", "启用屏蔽词过滤功能": "Bật chức năng lọc từ bị chặn", "启用性能监控": "Bật giám sát hiệu suất", @@ -955,10 +1030,14 @@ "响应缺少凭据": "Phản hồi thiếu thông tin xác thực", "响应缺少授权链接": "Phản hồi thiếu liên kết xác thực", "商品价格 ID": "ID giá sản phẩm", + "商户 ID": "", "回答内容": "Nội dung trả lời", "回调 URL 填": "Điền URL gọi lại", "回调 URL 格式": "Định dạng URL callback", "回调地址": "Địa chỉ gọi lại", + "回调支付方式": "Phương thức thanh toán callback", + "回调调用者IP": "IP người gọi callback", + "回调通知地址": "", "固定价格": "Giá cố định", "固定价格(每次)": "Giá cố định (mỗi lần)", "固定价格值": "Giá trị giá cố định", @@ -966,23 +1045,25 @@ "图标": "Biểu tượng", "图标使用 react-icons(Simple Icons)或 URL/emoji,例如:github、gitlab、si:google": "Icon sử dụng react-icons (Simple Icons) hoặc URL/emoji, VD: github, gitlab, si:google", "图标使用@lobehub/icons库,如:OpenAI、Claude.Color,支持链式参数:OpenAI.Avatar.type={'platform'}、OpenRouter.Avatar.shape={'square'},查询所有可用图标请 ": "Biểu tượng sử dụng thư viện @lobehub/icons, như: OpenAI, Claude.Color, hỗ trợ tham số chuỗi: OpenAI.Avatar.type={'platform'}, OpenRouter.Avatar.shape={'square'}, truy vấn tất cả biểu tượng khả dụng vui lòng ", + "图标文件不能超过 100KB,请压缩后重新上传": "", "图混合": "Pha trộn", "图片倍率 {{imageRatio}}": "Hệ số hình ảnh {{imageRatio}}", "图片功能在自定义请求体模式下不可用": "Chức năng hình ảnh không khả dụng trong chế độ yêu cầu tùy chỉnh", "图片地址": "URL hình ảnh", "图片已添加": "Hình ảnh đã được thêm", - "图片生成调用:{{symbol}}{{price}} / 1次": "Gọi tạo hình ảnh: {{symbol}}{{price}} / 1 lần", "图片生成:1 次 * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Tạo ảnh: 1 lần gọi * đơn giá {{price}} * {{ratioType}} {{ratio}} = {{amount}}", + "图片生成调用:{{symbol}}{{price}} / 1次": "Gọi tạo hình ảnh: {{symbol}}{{price}} / 1 lần", "图片输入 {{price}} / 1M tokens": "Đầu vào hình ảnh {{price}} / 1M tokens", "图片输入: {{imageRatio}}": "Đầu vào hình ảnh: {{imageRatio}}", + "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đầu vào hình ảnh: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số hình ảnh {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "图片输入价格": "Giá đầu vào hình ảnh", "图片输入价格 {{symbol}}{{price}} / 1M tokens": "Giá đầu vào hình ảnh {{symbol}}{{price}} / 1M tokens", "图片输入价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (图片倍率: {{imageRatio}})": "Giá đầu vào hình ảnh: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (Tỷ lệ hình ảnh: {{imageRatio}})", "图片输入价格:{{symbol}}{{price}} / 1M tokens": "Giá đầu vào hình ảnh: {{symbol}}{{price}} / 1M tokens", "图片输入价格:{{symbol}}{{total}} / 1M tokens": "Giá đầu vào hình ảnh: {{symbol}}{{total}} / 1M tokens", + "图片输入倍率": "", "图片输入倍率(仅部分模型支持该计费)": "Tỷ lệ đầu vào hình ảnh (chỉ được hỗ trợ bởi một số mô hình để tính phí)", "图片输入相关的倍率设置,键为模型名称,值为倍率,仅部分模型支持该计费": "Cài đặt tỷ lệ liên quan đến đầu vào hình ảnh, khóa là tên mô hình, giá trị là tỷ lệ, chỉ được hỗ trợ bởi một số mô hình để tính phí", - "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đầu vào hình ảnh: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số hình ảnh {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "图生文": "Mô tả", "图生视频": "Hình ảnh sang Video", "在Gotify服务器创建应用后获得的令牌,用于发送通知": "Mã thông báo nhận được sau khi tạo ứng dụng trên máy chủ Gotify, được sử dụng để gửi thông báo", @@ -1013,12 +1094,12 @@ "填充新模板": "Điền mẫu mới", "填充旧模板": "Điền mẫu cũ", "填充模板": "Điền mẫu", - "填充模板(全渠道)": "Điền mẫu (tất cả kênh)", - "填充模板(指定渠道)": "Điền mẫu (kênh được chọn)", "填充模板:等级+激活": "Điền mẫu: Cấp + Kích hoạt", "填充模板:等级提示": "Điền mẫu: Prompt cấp", "填充模板:组织或角色": "Điền mẫu: Tổ chức hoặc vai trò", "填充模板:组织提示": "Điền mẫu: Prompt tổ chức", + "填充模板(全渠道)": "Điền mẫu (tất cả kênh)", + "填充模板(指定渠道)": "Điền mẫu (kênh được chọn)", "填入": "Điền", "填入 CC Switch": "Điền CC Switch", "填入所有模型": "Điền tất cả mô hình", @@ -1036,7 +1117,9 @@ "填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议": "Sau khi điền nội dung thỏa thuận người dùng, người dùng sẽ được yêu cầu tích vào đã đọc thỏa thuận người dùng khi đăng ký", "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "Sau khi điền nội dung chính sách bảo mật, người dùng sẽ được yêu cầu tích vào đã đọc chính sách bảo mật khi đăng ký", "填写音频补全价格前,需要先填写音频输入价格。": "Trước khi nhập giá hoàn thành âm thanh, hãy nhập giá đầu vào âm thanh trước.", + "处理上游模型更新": "", "处理中": "Processing", + "处理全部渠道上游更新": "", "备份支持": "Hỗ trợ sao lưu", "备份状态": "Trạng thái sao lưu", "备注": "Ghi chú", @@ -1046,6 +1129,7 @@ "复制": "Sao chép", "复制代码": "Sao chép mã", "复制令牌": "Sao chép mã thông báo", + "复制令牌失败": "", "复制全部": "Sao chép tất cả", "复制名称": "Sao chép tên", "复制失败": "Sao chép thất bại", @@ -1064,6 +1148,7 @@ "复制渠道的所有信息": "Sao chép tất cả thông tin của kênh", "复制版本号": "Copy Version", "复制生成的密钥并粘贴到此处": "Copy the generated key and paste it here", + "复制请求头": "", "复制连接信息": "Sao chép thông tin kết nối", "复制链接": "Copy link", "外接设备": "Thiết bị ngoại vi", @@ -1087,13 +1172,13 @@ "套餐标题": "Tiêu đề gói", "套餐标题不能为空": "Tên gói không được để trống", "套餐的基本信息和定价": "Thông tin cơ bản và giá của gói", + "如:大带宽批量分析图片推荐": "ví dụ: Phân tích hàng loạt băng thông lớn đề xuất hình ảnh", + "如:香港线路": "ví dụ: Tuyến Hồng Kông", "如果亲和到的渠道失败,重试到其他渠道成功后,将亲和更新到成功的渠道。": "Nếu kênh ưu ái thất bại, sau khi thử lại thành công trên kênh khác, ưu ái sẽ được cập nhật sang kênh thành công.", "如果你对接的是上游One API或者New API等转发项目,请使用OpenAI类型,不要使用此类型,除非你知道你在做什么。": "Nếu bạn đang kết nối với các dự án chuyển tiếp One API hoặc New API thượng nguồn, vui lòng sử dụng loại OpenAI. Đừng sử dụng loại này trừ khi bạn biết mình đang làm gì.", "如果用户请求中包含系统提示词,则使用此设置拼接到用户的系统提示词前面": "Nếu yêu cầu của người dùng chứa từ nhắc hệ thống, cài đặt này sẽ được nối vào trước từ nhắc hệ thống của người dùng", "如果镜像为私有,请填写密码或Token": "If the image is private, please fill in the password or token", "如果镜像为私有,请填写用户名": "If the image is private, please fill in the username", - "如:大带宽批量分析图片推荐": "ví dụ: Phân tích hàng loạt băng thông lớn đề xuất hình ảnh", - "如:香港线路": "ví dụ: Tuyến Hồng Kông", "始终使用浅色主题": "Luôn sử dụng chủ đề sáng", "始终使用深色主题": "Luôn sử dụng chủ đề tối", "字段映射": "Ánh xạ trường", @@ -1161,6 +1246,7 @@ "密钥": "Khóa", "密钥 JSON 必须包含 access_token": "JSON khóa phải bao gồm access_token", "密钥 JSON 必须包含 account_id": "JSON khóa phải bao gồm account_id", + "密钥(编辑模式下,保存的密钥不会显示)": "Khóa (trong chế độ chỉnh sửa, khóa đã lưu sẽ không hiển thị)", "密钥去重": "Loại bỏ khóa trùng lặp", "密钥将以Bearer方式添加到请求头中,用于验证webhook请求的合法性": "Khóa sẽ được thêm vào tiêu đề yêu cầu dưới dạng Bearer để xác minh tính hợp pháp của yêu cầu webhook", "密钥已删除": "Khóa đã bị xóa", @@ -1178,7 +1264,6 @@ "密钥获取成功": "Lấy khóa thành công", "密钥输入方式": "Phương thức nhập khóa", "密钥预览": "Xem trước khóa", - "密钥(编辑模式下,保存的密钥不会显示)": "Khóa (trong chế độ chỉnh sửa, khóa đã lưu sẽ không hiển thị)", "对于官方渠道,new-api已经内置地址,除非是第三方代理站点或者Azure的特殊接入地址,否则不需要填写": "Đối với các kênh chính thức, new-api đã tích hợp sẵn địa chỉ. Trừ khi đó là trang web proxy của bên thứ ba hoặc địa chỉ truy cập đặc biệt của Azure, không cần điền vào", "对免费模型启用预消耗": "Enable pre-consumption for free models", "对域名启用 IP 过滤(推荐开启)": "Bật lọc IP cho tên miền (khuyến nghị)", @@ -1203,6 +1288,7 @@ "将只保留最近 {{value}} 个日志文件,其余将被删除。": "Chỉ giữ lại {{value}} tệp nhật ký gần nhất; phần còn lại sẽ bị xóa.", "将大请求体临时存储到磁盘": "Lưu tạm body yêu cầu lớn vào đĩa", "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "Cấu hình giá của mô hình đang chỉnh sửa {{name}} sẽ được áp dụng hàng loạt cho {{count}} mô hình đã chọn.", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。_other": "", "将清除所有保存的配置并恢复默认设置,此操作不可撤销。是否继续?": "Thao tác này sẽ xóa tất cả các cấu hình đã lưu và khôi phục cài đặt mặc định, thao tác này không thể hoàn tác. Tiếp tục?", "将清除选定时间之前的所有日志": "Thao tác này sẽ xóa tất cả nhật ký trước thời gian đã chọn", "将追加 2 条规则到现有规则列表。": "2 quy tắc sẽ được thêm vào danh sách quy tắc hiện có.", @@ -1217,9 +1303,9 @@ "展示价格": "Giá hiển thị", "嵌套映射:用户分组 → 使用分组 → 倍率": "Nested mapping: user group → using group → ratio", "左侧边栏个人设置": "Cài đặt cá nhân ở thanh bên trái", - "已为 ${count} 个渠道设置标签!": "Đã đặt thẻ cho ${count} kênh!", "已为 {{count}} 个模型设置{{type}}_one": "Đã đặt {{type}} cho {{count}} mô hình", "已为 {{count}} 个模型设置{{type}}_other": "Đã đặt {{type}} cho {{count}} mô hình", + "已为 ${count} 个渠道设置标签!": "Đã đặt thẻ cho ${count} kênh!", "已从 Discovery 自动填充配置": "Cấu hình đã được tự động điền từ Discovery", "已从 Discovery 获取配置,可继续手动修改所有字段。": "Đã nhận cấu hình từ Discovery. Bạn có thể tiếp tục chỉnh sửa thủ công tất cả các trường.", "已作废": "Đã vô hiệu", @@ -1234,15 +1320,16 @@ "已切换至最优倍率视图,每个模型使用其最低倍率分组": "Đã chuyển sang chế độ xem tỷ lệ tối ưu, mỗi mô hình sử dụng nhóm tỷ lệ thấp nhất của nó", "已初始化": "Đã khởi tạo", "已删除": "Đã xóa", - "已删除 ${data} 个通道!": "Đã xóa ${data} kênh!", "已删除 {{count}} 个令牌!": "Đã xóa {{count}} mã thông báo!", "已删除 {{count}} 个令牌!_other": "Deleted {{count}} tokens!", "已删除 {{count}} 条失效兑换码_one": "Đã xóa {{count}} mã đổi thưởng hết hạn", "已删除 {{count}} 条失效兑换码_other": "Đã xóa {{count}} mã đổi thưởng hết hạn", + "已删除 ${data} 个通道!": "Đã xóa ${data} kênh!", "已删除所有禁用渠道,共计 ${data} 个": "Đã xóa tất cả các kênh bị vô hiệu hóa, tổng cộng ${data}", "已删除消息及其回复": "Đã xóa tin nhắn và các câu trả lời của nó", "已勾选": "Đã chọn", "已勾选 {{count}} 个模型": "Đã chọn {{count}} mô hình", + "已勾选 {{count}} 个模型_other": "", "已发起支付": "Đã khởi tạo thanh toán", "已发送到 Fluent": "Đã gửi đến Fluent", "已取消 Passkey 注册": "Đã hủy đăng ký Passkey", @@ -1254,10 +1341,13 @@ "已填充提示模板": "Mẫu prompt đã được điền", "已填充模版": "Mẫu đã được điền", "已填充策略模板": "Mẫu chính sách đã được điền", + "已处理上游模型更新:加入 {{added}} 个,删除 {{removed}} 个,本次忽略 {{ignored}} 个,当前已忽略模型 {{totalIgnored}} 个": "", "已备份": "Đã sao lưu", "已复制": "Đã sao chép", "已复制 ${count} 个模型": "Đã sao chép ${count} mô hình", "已复制 ID 到剪贴板": "ID copied to clipboard", + "已复制:": "Đã sao chép:", + "已复制:{{name}}": "Đã sao chép: {{name}}", "已复制全部数据": "Tất cả dữ liệu đã được sao chép", "已复制到剪切板": "Đã sao chép vào khay nhớ tạm", "已复制到剪贴板": "Đã sao chép vào khay nhớ tạm", @@ -1266,14 +1356,15 @@ "已复制模型名称": "Đã sao chép tên mô hình", "已复制版本号": "Version copied", "已复制自动生成的 API Key": "Auto-generated API Key copied", - "已复制:": "Đã sao chép:", - "已复制:{{name}}": "Đã sao chép: {{name}}", "已完成": "Completed", "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "Đã áp dụng hàng loạt cấu hình giá của mô hình {{name}} cho {{count}} mô hình", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型_other": "", "已开启全局请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "Đã bật truyền qua yêu cầu toàn cục. Các tính năng tích hợp của NewAPI như ghi đè tham số, chuyển hướng mô hình và thích ứng kênh sẽ bị vô hiệu hóa. Đây không phải là thực hành tốt nhất. Nếu phát sinh vấn đề, vui lòng không gửi issue.", + "已忽略模型": "", "已成功开始测试所有已启用通道,请刷新页面查看结果。": "Đã bắt đầu kiểm tra tất cả các kênh đã bật thành công. Vui lòng làm mới trang để xem kết quả.", "已打开授权页面": "Đã mở trang xác thực", "已打开支付页面": "Đã mở trang thanh toán", + "已批量处理上游模型更新:渠道 {{channels}} 个,加入 {{added}} 个,删除 {{removed}} 个,失败 {{fails}} 个": "", "已提交": "Đã gửi", "已支付金额": "Amount Paid", "已新增 {{count}} 个模型:{{list}}_one": "Đã thêm {{count}} mô hình: {{list}}", @@ -1289,6 +1380,7 @@ "已添加 {{count}} 个模板_other": "Đã thêm {{count}} mẫu", "已添加到白名单": "Đã thêm vào danh sách trắng", "已清理 {{count}} 个日志文件,释放 {{size}}": "Đã dọn dẹp {{count}} tệp nhật ký, giải phóng {{size}}", + "已清理 {{count}} 个日志文件,释放 {{size}}_other": "", "已清空": "Đã xóa sạch", "已清空测试结果": "Đã xóa kết quả kiểm tra", "已生成授权凭据": "Đã tạo thông tin xác thực", @@ -1307,13 +1399,14 @@ "已达到购买上限": "Đã đạt giới hạn mua", "已过期": "Đã hết hạn", "已运行时间": "Uptime", - "已选择 ${count} 个渠道": "Đã chọn ${count} kênh", "已选择 {{count}} 个模型_one": "Đã chọn {{count}} mô hình", "已选择 {{count}} 个模型_other": "Đã chọn {{count}} mô hình", "已选择 {{selected}} / {{total}}": "Đã chọn {{selected}} / {{total}}", + "已选择 ${count} 个渠道": "Đã chọn ${count} kênh", "已重置为默认配置": "Đã đặt lại về cấu hình mặc định", "已销毁": "Destroyed", "币种": "Tiền tệ", + "帐号信息": "", "常用上下文 Key(用于 context_*)": "Key ngữ cảnh phổ biến (cho context_*)", "常见问答": "Câu hỏi thường gặp", "常见问答管理,为用户提供常见问题的答案(最多50个,前端显示最新20条)": "Quản lý câu hỏi thường gặp, cung cấp câu trả lời cho các câu hỏi thường gặp của người dùng (tối đa 50, hiển thị 20 mới nhất ở giao diện người dùng)", @@ -1332,19 +1425,16 @@ "延长容器时长将会产生额外费用,请确认您有足够的账户余额。": "Extending container duration will incur additional charges, please ensure you have sufficient account balance.", "延长操作一旦确认无法撤销,费用将立即扣除。": "Once confirmed, the extension operation cannot be undone, and charges will be deducted immediately.", "延长时长": "Extension Duration", + "延长时长(小时)": "Extension Duration (hours)", "延长时长不能超过720小时(30天)": "Extension duration cannot exceed 720 hours (30 days)", "延长时长失败": "Failed to extend duration", "延长时长至少为1小时": "Extension duration must be at least 1 hour", - "延长时长(小时)": "Extension Duration (hours)", "建立连接时发生错误": "Đã xảy ra lỗi khi thiết lập kết nối", "建议在生产环境中使用 MySQL 或 PostgreSQL 数据库,或确保 SQLite 数据库文件已映射到宿主机的持久化存储。": "Khuyên dùng cơ sở dữ liệu MySQL hoặc PostgreSQL trong môi trường sản xuất, hoặc đảm bảo tệp cơ sở dữ liệu SQLite được ánh xạ tới bộ nhớ bền vững của máy chủ.", "开": "mở", "开启「默认使用 auto 分组」后,新建令牌和初始令牌都会自动设为 auto。": "After enabling \"Default to auto group\", new tokens and initial tokens will be set to auto.", "开启之后会清除用户提示词中的": "Sau khi bật, từ nhắc của người dùng sẽ bị xóa", "开启之后将上游地址替换为服务器地址": "Sau khi bật, địa chỉ thượng nguồn sẽ được thay thế bằng địa chỉ máy chủ", - "开启后不限制:必须设置模型倍率": "Sau khi bật, không giới hạn: phải đặt tỷ lệ mô hình", - "开启后创建令牌默认选择auto分组,初始令牌也将设为auto": "When enabled, new tokens default to auto group, initial tokens are also set to auto", - "开启后未登录用户无法访问模型广场": "Khi bật, người dùng chưa xác thực không thể truy cập thị trường mô hình", "开启后,using_group 会参与 cache key(不同分组隔离)。": "Khi bật, using_group sẽ tham gia vào cache key (cách ly theo nhóm).", "开启后,仅\"消费\"和\"错误\"日志将记录您的客户端IP地址": "Sau khi bật, chỉ nhật ký \"tiêu thụ\" và \"lỗi\" sẽ ghi lại địa chỉ IP máy khách của bạn", "开启后,对免费模型(倍率为0,或者价格为0)的模型也会预消耗额度": "After enabling, free models (ratio 0 or price 0) will also pre-consume quota", @@ -1356,14 +1446,21 @@ "开启后,规则名称会参与 cache key(不同规则隔离)。": "Khi bật, tên quy tắc sẽ tham gia vào cache key (cách ly theo quy tắc).", "开启后,该渠道请求 Claude 时将强制追加 ?beta=true(无需客户端手动传参)": "Khi bật, yêu cầu đến Claude qua kênh này sẽ tự động thêm ?beta=true (client không cần truyền thủ công)", "开启后,违规请求将额外扣费。": "Khi bật, các yêu cầu vi phạm sẽ bị tính phí bổ sung.", + "开启后不限制:必须设置模型倍率": "Sau khi bật, không giới hạn: phải đặt tỷ lệ mô hình", + "开启后创建令牌默认选择auto分组,初始令牌也将设为auto": "When enabled, new tokens default to auto group, initial tokens are also set to auto", + "开启后未登录用户无法访问模型广场": "Khi bật, người dùng chưa xác thực không thể truy cập thị trường mô hình", + "开启后检测到新增模型会自动加入当前渠道模型列表": "", + "开启后由后端定时任务检测该渠道上游模型变化": "", "开启批量操作": "Bật chọn hàng loạt", "开始": "Bắt đầu", "开始同步": "Bắt đầu đồng bộ", "开始批量测试 ${count} 个模型,已清空上次结果...": "Bắt đầu kiểm tra hàng loạt ${count} mô hình, đã xóa kết quả trước đó...", "开始时间": "thời gian bắt đầu", + "异常": "", "异步任务退款": "Hoàn tiền tác vụ bất đồng bộ", "张图片": "hình ảnh", "弱变换": "Biến thể cao", + "强制使用 AUTH LOGIN": "Buộc AUTH LOGIN", "强制将响应格式化为 OpenAI 标准格式(只适用于OpenAI渠道类型)": "Buộc định dạng phản hồi theo định dạng chuẩn OpenAI (Chỉ dành cho các loại kênh OpenAI)", "强制格式化": "Buộc định dạng", "强制要求": "Yêu cầu bắt buộc", @@ -1401,6 +1498,7 @@ "当前设置类型: ": "Loại cài đặt hiện tại: ", "当前跟随系统": "Hiện đang theo hệ thống", "当前配置无法连接到 io.net。": "Unable to connect to io.net with current configuration.", + "当前额度": "Hạn ngạch hiện tại", "当某个分组的用户使用另一个分组的令牌时,可设置特殊倍率覆盖基础倍率。例如:vip 分组的用户使用 default 分组时倍率为 0.5": "When a user in one group uses a token from another group, a special ratio can override the base ratio. E.g., vip users using default group get ratio 0.5", "当模型没有设置价格时仍接受调用,仅当您信任该网站时使用,可能会产生高额费用": "Chấp nhận cuộc gọi ngay cả khi mô hình không có cài đặt giá, chỉ sử dụng khi bạn tin tưởng trang web, điều này có thể phát sinh chi phí cao", "当运行通道全部测试时,超过此时间将自动禁用通道": "Khi chạy tất cả các kiểm tra kênh, kênh sẽ tự động bị vô hiệu hóa khi vượt quá thời gian này", @@ -1480,10 +1578,13 @@ "批量删除失败": "Xóa hàng loạt thất bại", "批量删除成功": "Batch deletion successful", "批量删除模型": "Xóa mô hình hàng loạt", + "批量处理失败": "", "批量应用当前模型价格": "Áp dụng hàng loạt giá của mô hình hiện tại", "批量操作": "Thao tác hàng loạt", "批量操作失败": "Batch operation failed", "批量操作完成: {{success}}个成功, {{failed}}个失败": "Batch operation completed: {{success}} succeeded, {{failed}} failed", + "批量检测失败": "", + "批量检测完成:渠道 {{channels}} 个,新增 {{add}} 个,删除 {{remove}} 个,失败 {{fails}} 个": "", "批量测试${count}个模型": "Kiểm tra hàng loạt ${count} mô hình", "批量测试完成!成功: ${success}, 失败: ${fail}, 总计: ${total}": "Kiểm tra hàng loạt hoàn tất! Thành công: ${success}, Thất bại: ${fail}, Tổng cộng: ${total}", "批量测试已停止": "Đã dừng kiểm tra hàng loạt", @@ -1505,28 +1606,29 @@ "按倍率设置": "Đặt theo tỷ lệ", "按次": "Theo lượt gọi", "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Theo lượt gọi {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "按次计费": "Tính phí theo lượt gọi", "按次:{{symbol}}{{price}}": "Theo lượt gọi: {{symbol}}{{price}}", "按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Theo lượt gọi: {{symbol}}{{price}} * {{ratioType}}: {{ratio}} = {{symbol}}{{total}}", + "按次计费": "Tính phí theo lượt gọi", "按照如下格式输入:AccessKey|SecretAccessKey|Region": "Enter in the format: AccessKey|SecretAccessKey|Region", "按量计费": "Trả tiền theo mức sử dụng", "按量计费下需要先填写输入价格,才能保存其它价格项。": "Ở chế độ tính phí theo lượng, cần điền giá đầu vào trước thì mới lưu được các mục giá khác.", "按顺序替换content中的变量占位符": "Thay thế các trình giữ chỗ biến trong nội dung theo thứ tự", "换脸": "Hoán đổi khuôn mặt", - "授权失败": "Ủy quyền thất bại", "授权,需在遵守": " và phải được sử dụng tuân thủ ", + "授权失败": "Ủy quyền thất bại", "排序": "Thứ tự", "排队中": "Đang xếp hàng", "接受未设置价格模型": "Chấp nhận các mô hình không có cài đặt giá", "接口凭证": "Thông tin xác thực giao diện", "接口密钥已过期": "API key has expired", + "接收上游模型更新通知": "", "控制台": "Bảng điều khiển", "控制台区域": "Khu vực bảng điều khiển", "控制输出的随机性和创造性": "Kiểm soát tính ngẫu nhiên và sáng tạo của đầu ra", "控制顶栏模块显示状态,全局生效": "Kiểm soát trạng thái hiển thị mô-đun tiêu đề, hiệu ứng toàn cầu", "推荐": "Đề xuất", - "推荐使用(用户可选)": "Khuyên dùng (người dùng tùy chọn)", "推荐:用户可以选择是否使用指纹等验证": "Khuyên dùng: Người dùng có thể chọn sử dụng xác minh vân tay hay không", + "推荐使用(用户可选)": "Khuyên dùng (người dùng tùy chọn)", "描述": "Mô tả", "提交": "Gửi", "提交时间": "Thời gian gửi", @@ -1536,14 +1638,14 @@ "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "Prompt {{input}} tokens / 1M tokens * {{symbol}}{{price}}", "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Gợi ý {{input}} tokens / 1M tokens * {{symbol}}{{price}} + Hoàn thành {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Gợi ý {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + Bộ nhớ đệm {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + Tạo bộ nhớ đệm {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + Hoàn thành {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "提示价格:{{symbol}}{{price}} / 1M tokens": "Giá gợi ý: {{symbol}}{{price}} / 1M tokens", - "提示缓存倍率": "Tỷ lệ bộ nhớ đệm gợi ý", "提示:如需备份数据,只需复制上述目录即可": "Mẹo: Để sao lưu dữ liệu, chỉ cần sao chép thư mục trên", "提示:此处配置仅用于控制「模型广场」对用户的展示效果,不会影响模型的实际调用与路由。若需配置真实调用行为,请前往「渠道管理」进行设置。": "Lưu ý: Cấu hình tại đây chỉ ảnh hưởng đến cách hiển thị trong \"Chợ mô hình\" và không ảnh hưởng đến việc gọi hoặc định tuyến thực tế. Nếu cần cấu hình hành vi gọi thực tế, vui lòng thiết lập trong \"Quản lý kênh\".", "提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。": "Lưu ý: Ánh xạ endpoint chỉ dùng để hiển thị trong \"Chợ mô hình\" và không ảnh hưởng đến việc gọi thực tế. Để cấu hình gọi thực tế, vui lòng vào \"Quản lý kênh\".", "提示:该功能为测试版,未来配置结构与功能行为可能发生变更,请勿在生产环境使用。": "Lưu ý: Đây là tính năng beta. Cấu trúc cấu hình và hành vi có thể thay đổi trong tương lai. Không dùng trong môi trường production.", "提示:语言偏好会同步到您登录的所有设备,并影响API返回的错误消息语言。": "Gợi ý: Tùy chọn ngôn ngữ sẽ được đồng bộ trên tất cả thiết bị đã đăng nhập và ảnh hưởng đến ngôn ngữ thông báo lỗi API trả về.", "提示:链接中的{key}将被替换为API密钥,{address}将被替换为服务器地址": "Mẹo: {key} trong liên kết sẽ được thay thế bằng khóa API, {address} sẽ được thay thế bằng địa chỉ máy chủ", + "提示价格:{{symbol}}{{price}} / 1M tokens": "Giá gợi ý: {{symbol}}{{price}} / 1M tokens", + "提示缓存倍率": "Tỷ lệ bộ nhớ đệm gợi ý", "搜索供应商": "Tìm kiếm nhà cung cấp", "搜索关键字": "Từ khóa tìm kiếm", "搜索失败": "Search failed", @@ -1557,6 +1659,7 @@ "搜索模型失败": "Tìm kiếm mô hình thất bại", "搜索渠道名称或地址": "Tìm kiếm tên hoặc địa chỉ kênh", "搜索聊天应用名称": "Tìm kiếm tên ứng dụng trò chuyện", + "搜索规则(描述 / 类型 / 路径 / 来源 / 目标)": "", "搜索规则(类型 / 路径 / 来源 / 目标)": "Tìm kiếm quy tắc (loại / đường dẫn / nguồn / đích)", "搜索部署名称": "Search deployment name", "操作": "Hành động", @@ -1570,13 +1673,16 @@ "支付": "Thanh toán", "支付地址": "Địa chỉ thanh toán", "支付失败": "Thanh toán thất bại", + "支付完成后用户跳转的页面,留空则自动使用 服务器地址 + /console/topup": "", "支付宝": "Alipay", "支付方式": "Phương thức thanh toán", "支付方式名称": "Tên phương thức thanh toán", + "支付方式名称不能为空": "", "支付方式类型": "Loại phương thức thanh toán", "支付渠道": "Kênh thanh toán", "支付设置": "Thanh toán", "支付请求失败": "Yêu cầu thanh toán thất bại", + "支付返回地址": "", "支付金额": "Số tiền thanh toán", "支持 Ctrl+V 粘贴图片": "Hỗ trợ Ctrl+V để dán hình ảnh", "支持6位TOTP验证码或8位备用码,可到`个人设置-安全设置-两步验证设置`配置或查看。": "Hỗ trợ mã xác minh TOTP 6 chữ số hoặc mã dự phòng 8 chữ số, có thể được cấu hình hoặc xem trong `Cài đặt cá nhân - Cài đặt bảo mật - Cài đặt xác thực hai yếu tố`.", @@ -1593,6 +1699,7 @@ "支持拉取 Ollama 官方模型库中的所有模型,拉取过程可能需要几分钟时间": "Supports pulling all models from the Ollama official model library, the pulling process may take a few minutes", "支持搜索用户的 ID、用户名、显示名称和邮箱地址": "Hỗ trợ tìm kiếm ID người dùng, tên người dùng, tên hiển thị và địa chỉ email", "支持的图像模型": "Mô hình hình ảnh được hỗ trợ", + "支持精确匹配;使用 regex: 开头可按正则匹配。": "", "支持通配符格式,如:example.com, *.api.example.com": "Hỗ trợ định dạng ký tự đại diện, ví dụ: example.com, *.api.example.com", "支持逻辑 and/or 与嵌套 groups;操作符支持 eq/ne/gt/gte/lt/lte/in/not_in/contains/exists": "Hỗ trợ logic and/or với groups lồng nhau; toán tử: eq/ne/gt/gte/lt/lte/in/not_in/contains/exists", "收益": "Thu nhập", @@ -1600,6 +1707,7 @@ "收起": "Thu gọn", "收起侧边栏": "Thu gọn thanh bên", "收起内容": "Thu gọn nội dung", + "收起原生额度输入": "Ẩn nhập hạn mức gốc", "放大": "Upscalers", "放大编辑": "Mở rộng trình chỉnh sửa", "敏感信息不会发送到前端显示": "Thông tin nhạy cảm sẽ không được hiển thị ở giao diện người dùng", @@ -1616,9 +1724,9 @@ "数据看板默认时间粒度": "Độ chi tiết thời gian mặc định của bảng dữ liệu", "数据管理和日志查看": "Quản lý dữ liệu và xem nhật ký", "文件上传": "Tải lên tệp", + "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Tìm kiếm tệp: {{count}} / 1K * đơn giá {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "文件搜索价格:{{symbol}}{{price}} / 1K 次": "Giá tìm kiếm tệp: {{symbol}}{{price}} / 1K lần", "文件搜索调用 {{fileSearchCallCount}} 次": "Đã gọi tìm kiếm tệp {{fileSearchCallCount}} lần", - "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Tìm kiếm tệp: {{count}} / 1K * đơn giá {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "Gợi ý văn bản {{input}} tokens / 1M tokens * {{symbol}}{{price}} + Hoàn thành văn bản {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Prompt văn bản {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + Hoàn thành văn bản {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + Prompt âm thanh {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + Hoàn thành âm thanh {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "文字提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "Gợi ý văn bản {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + Bộ nhớ đệm {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + Hoàn thành văn bản {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", @@ -1633,8 +1741,11 @@ "新增 Key 来源": "Thêm nguồn Key", "新增供应商": "Thêm nhà cung cấp", "新增失败": "Thêm thất bại", + "新增已选 {{selected}} / {{total}}": "", "新增成功": "Thêm thành công", + "新增支付方式": "", "新增条件": "Thêm điều kiện", + "新增模型": "", "新增规则": "Thêm quy tắc", "新增订阅": "Thêm đăng ký", "新密码": "Mật khẩu mới", @@ -1645,9 +1756,9 @@ "新建容器部署": "Create Container Deployment", "新建数量": "Số lượng mới", "新建组": "Nhóm mới", - "新格式模板": "Mẫu định dạng mới", "新格式(支持条件判断与json自定义):": "Định dạng mới (hỗ trợ phán đoán điều kiện và tùy chỉnh JSON):", "新格式(规则 + 条件)": "Định dạng mới (Quy tắc + Điều kiện)", + "新格式模板": "Mẫu định dạng mới", "新版本": "Phiên bản mới", "新用户使用邀请码奖励额度": "Hạn ngạch thưởng mã mời người dùng mới", "新用户初始额度": "Hạn ngạch ban đầu cho người dùng mới", @@ -1684,10 +1795,10 @@ "日志类型": "Loại nhật ký", "日志设置": "Cài đặt nhật ký", "日志详情": "Chi tiết nhật ký", - "旧格式必须是 JSON 对象": "Định dạng cũ phải là đối tượng JSON", - "旧格式模板": "Mẫu định dạng cũ", "旧格式(JSON 对象)": "Định dạng cũ (Đối tượng JSON)", "旧格式(直接覆盖):": "Định dạng cũ (ghi đè trực tiếp):", + "旧格式必须是 JSON 对象": "Định dạng cũ phải là đối tượng JSON", + "旧格式模板": "Mẫu định dạng cũ", "旧的备用码已失效,请保存新的备用码": "Mã dự phòng cũ đã bị vô hiệu hóa, vui lòng lưu mã dự phòng mới", "早上好": "Chào buổi sáng", "时间": "Thời gian", @@ -1700,16 +1811,18 @@ "是否为企业账户": "Đây có phải là tài khoản doanh nghiệp không?", "是否同时重置对话消息?选择\"是\"将清空所有对话记录并恢复默认示例;选择\"否\"将保留当前对话记录。": "Đặt lại tin nhắn trò chuyện cùng lúc? Chọn \"Có\" sẽ xóa tất cả hồ sơ trò chuyện và khôi phục các ví dụ mặc định; chọn \"Không\" sẽ giữ lại hồ sơ trò chuyện hiện tại.", "是否将该订单标记为成功并为用户入账?": "Đánh dấu đơn hàng này là thành công và ghi có cho người dùng?", + "是否检测上游模型更新": "", "是否确认充值?": "Confirm the recharge?", + "是否自动同步上游模型更新": "", "是否自动禁用": "Có tự động vô hiệu hóa không", "是否要求指纹/面容等生物识别": "Có yêu cầu nhận dạng vân tay/khuôn mặt không", "显示倍率": "Hiển thị tỷ lệ", + "显示最新20条": "Hiển thị 20 mới nhất", "显示名称": "Tên hiển thị", "显示名称字段(可选)": "Trường tên hiển thị (tùy chọn)", "显示完整内容": "Hiển thị nội dung đầy đủ", "显示操作项": "Hiển thị hành động", "显示更多": "Hiển thị thêm", - "显示最新20条": "Hiển thị 20 mới nhất", "显示第": "Đang hiển thị", "显示设置": "Cài đặt hiển thị", "显示调试": "Hiển thị gỡ lỗi", @@ -1730,6 +1843,7 @@ "暂无公告": "Không có thông báo", "暂无分组,点击下方按钮添加": "No groups yet. Click the button below to add one.", "暂无匹配模型": "Không có mô hình phù hợp", + "暂无参数覆盖记录": "", "暂无可复制 JSON": "Không có JSON để sao chép", "暂无可复制的版本信息": "No version information to copy", "暂无可展示数据": "Không có dữ liệu để hiển thị", @@ -1743,6 +1857,7 @@ "暂无已绑定项": "Không có mục đã liên kết", "暂无常见问答": "Không có câu hỏi thường gặp", "暂无成功模型": "Không có mô hình thành công", + "暂无支付方式,点击上方按钮新增": "", "暂无数据": "Không có dữ liệu", "暂无数据,点击下方按钮添加键值对": "Không có dữ liệu, nhấp vào nút bên dưới để thêm cặp khóa-giá trị", "暂无日志": "No logs", @@ -1771,6 +1886,7 @@ "更新": "Cập nhật", "更新 Creem 设置": "Cập nhật cài đặt Creem", "更新 Stripe 设置": "Cập nhật cài đặt Stripe", + "更新 Waffo 设置": "", "更新SSRF防护设置": "Cập nhật cài đặt bảo vệ SSRF", "更新Worker设置": "Cập nhật cài đặt Worker", "更新令牌信息": "Cập nhật thông tin mã thông báo", @@ -1793,18 +1909,7 @@ "更新配置后,容器可能需要重启以应用新的设置。请确保您了解这些更改的影响。": "After updating the configuration, the container may need to restart to apply the new settings. Please ensure you understand the impact of these changes.", "更新配置失败": "Failed to update configuration", "更新预填组": "Cập nhật nhóm điền sẵn", - "最低": "thấp nhất", - "最低充值美元数量": "Số tiền nạp đô la tối thiểu", - "最后使用时间": "Thời gian sử dụng cuối cùng", - "最后更新": "Last Updated", - "最后请求": "Yêu cầu cuối cùng", - "最大GPU数量": "Max Number of GPUs", - "最大可用": "Max Available", - "最大条目数": "Số mục tối đa", - "最终抵扣": "Khấu trừ cuối cùng", - "最近一次": "Lần gần nhất", - "最近事件": "Recent Events", - "最高优先级": "highest priority", + "替换": "", "月": "tháng", "有 Reasoning": "Có lập luận", "有序字符串数组": "Ordered string array", @@ -1814,6 +1919,7 @@ "有效期设置": "Cài đặt thời hạn", "服务可用性": "Trạng thái dịch vụ", "服务商": "Service Provider", + "服务器IP": "IP máy chủ", "服务器地址": "Địa chỉ máy chủ", "服务器日志功能未启用(未配置日志目录)": "Ghi nhật ký máy chủ chưa được bật (chưa cấu hình thư mục nhật ký)", "服务器日志管理": "Quản lý nhật ký máy chủ", @@ -1860,8 +1966,8 @@ "本地数据存储": "Lưu trữ dữ liệu cục bộ", "本地计费": "Local billing", "本月获得": "Tháng này", - "本设备内置": "Thiết bị tích hợp", "本设备:手机指纹/面容,外接:USB安全密钥": "Tích hợp: vân tay/khuôn mặt điện thoại, Bên ngoài: khóa bảo mật USB", + "本设备内置": "Thiết bị tích hợp", "本项目根据": "Dự án này được cấp phép theo ", "机密环境变量": "Secret Environment Variables", "机密环境变量将被加密存储,适用于存储密码、API密钥等敏感信息。": "Secret environment variables will be stored encrypted, suitable for storing passwords, API keys and other sensitive information.", @@ -1870,19 +1976,21 @@ "权限设置": "Cài đặt quyền", "条": "mục", "条 - 第": "đến", + "条,共": "của", "条件取反": "Đảo ngược điều kiện", "条件数": "Số điều kiện", "条件规则": "Quy tắc điều kiện", "条件项设置": "Cài đặt mục điều kiện", "条日志已清理!": "nhật ký đã được xóa!", "条规则": "rules", - "条,共": "của", "来源": "Nguồn", "来源于 IO.NET 部署": "From IO.NET Deployment", "来源端点": "Endpoint nguồn", "来自模型重定向,尚未加入模型列表": "From model redirect, not yet added to the model list", "某些配置更改可能需要几分钟才能生效。": "Some configuration changes may take a few minutes to take effect.", "查看": "Kiểm tra", + "查看 Codex 帐号信息与用量": "", + "查看 JSON 示例": "", "查看关联部署": "View Associated Deployment", "查看图片": "Xem hình ảnh", "查看密钥": "Xem khóa", @@ -1914,10 +2022,13 @@ "格式示例:": "Ví dụ định dạng:", "格式错误": "Định dạng không hợp lệ", "检查更新": "Kiểm tra cập nhật", + "检测全部渠道上游更新": "", "检测到 FluentRead(流畅阅读)": "Đã phát hiện FluentRead (đọc trôi chảy)", "检测到剪贴板中的连接信息": "Phát hiện thông tin kết nối trong bộ nhớ tạm", "检测到多个密钥,您可以单独复制每个密钥,或点击复制全部获取完整内容。": "Đã phát hiện nhiều khóa, bạn có thể sao chép từng khóa riêng lẻ hoặc nhấp vào Sao chép tất cả để lấy nội dung đầy đủ.", "检测到该消息后有AI回复,是否删除后续回复并重新生成?": "Phát hiện trả lời AI sau tin nhắn này, xóa các trả lời tiếp theo và tạo lại?", + "检测失败": "", + "检测完成:新增 {{add}} 个,删除 {{remove}} 个": "", "检测必须等待绘图成功才能进行放大等操作": "Việc phát hiện phải đợi vẽ thành công trước khi thực hiện phóng to và các thao tác khác", "概览": "Overview", "模型": "Mô hình", @@ -1933,6 +2044,7 @@ "模型价格:{{symbol}}{{price}}": "Giá mô hình: {{symbol}}{{price}}", "模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "Giá mô hình: {{symbol}}{{price}} * {{ratioType}}: {{ratio}} = {{symbol}}{{total}}", "模型价格:{{symbol}}{{price}} / 次": "Giá mô hình: {{symbol}}{{price}} / lượt gọi", + "模型价格未配置": "Giá mô hình chưa được cấu hình", "模型倍率": "Tỷ lệ mô hình", "模型倍率 {{modelRatio}}": "Model ratio {{modelRatio}}", "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "Tỷ lệ mô hình {{modelRatio}}, tỷ lệ bộ nhớ đệm {{cacheRatio}}, tỷ lệ hoàn thành {{completionRatio}}, {{ratioType}} {{ratio}}", @@ -1947,10 +2059,10 @@ "模型倍率设置": "Cài đặt tỷ lệ mô hình", "模型关键字": "Từ khóa mô hình", "模型列表": "Danh sách mô hình", + "模型列表,使用逗号分隔,例如:gpt-3.5-turbo,gpt-4": "Danh sách mô hình, phân tách bằng dấu phẩy, ví dụ: gpt-3.5-turbo,gpt-4", "模型列表已复制到剪贴板": "Danh sách mô hình đã được sao chép vào khay nhớ tạm", "模型列表已更新": "Danh sách mô hình đã được cập nhật", "模型列表已追加更新": "Model list has been updated", - "模型列表,使用逗号分隔,例如:gpt-3.5-turbo,gpt-4": "Danh sách mô hình, phân tách bằng dấu phẩy, ví dụ: gpt-3.5-turbo,gpt-4", "模型创建成功!": "Tạo mô hình thành công!", "模型删除失败": "Failed to delete model", "模型删除失败: {{error}}": "Failed to delete model: {{error}}", @@ -1964,8 +2076,8 @@ "模型名称正则": "Regex tên mô hình", "模型固定价格": "Giá cố định mô hình", "模型图标": "Biểu tượng mô hình", - "模型定价设置": "Model Pricing", "模型定价,需要登录访问": "Định giá mô hình, yêu cầu đăng nhập để truy cập", + "模型定价设置": "Model Pricing", "模型广场": "Thị trường mô hình", "模型库": "Thư viện mô hình", "模型拉取失败: {{error}}": "Failed to pull model: {{error}}", @@ -1980,8 +2092,8 @@ "模型未加入列表,可能无法调用": "Model not in the list; requests may fail", "模型权限": "Quyền mô hình", "模型正则": "Regex model", - "模型正则不能为空": "Regex model không được để trống", "模型正则(每行一个)": "Regex model (mỗi dòng một mục)", + "模型正则不能为空": "Regex model không được để trống", "模型消耗分布": "Phân phối tiêu thụ mô hình", "模型消耗趋势": "Xu hướng tiêu thụ mô hình", "模型版本": "Phiên bản mô hình", @@ -2007,8 +2119,8 @@ "模型部署设置": "Triển khai mô hình", "模型配置": "Cấu hình mô hình", "模型重定向": "Chuyển hướng mô hình", - "模型重定向里的下列模型尚未添加到“模型”列表,调用时会因为缺少可用模型而失败:": "The following models from the redirect have not been added to the “Models” list and requests will fail due to no available model:", "模型重定向,JSON格式,例如:{\"gpt-3.5-turbo\": \"gpt-3.5-turbo-0613\"}": "Chuyển hướng mô hình, định dạng JSON, ví dụ: {\"gpt-3.5-turbo\": \"gpt-3.5-turbo-0613\"}", + "模型重定向里的下列模型尚未添加到“模型”列表,调用时会因为缺少可用模型而失败:": "The following models from the redirect have not been added to the “Models” list and requests will fail due to no available model:", "模型限制": "Giới hạn mô hình", "模型限制列表": "Danh sách giới hạn mô hình", "模式": "Chế độ", @@ -2022,19 +2134,24 @@ "欢迎回来": "Chào mừng trở lại", "欢迎回来!": "Chào mừng trở lại!", "欧元": "EUR", + "正则替换": "", "正在使用": "Đang sử dụng", "正在加载...": "Đang tải...", "正在加载可用部署位置...": "Loading available deployment locations...", "正在加载签到状态...": "Đang tải trạng thái đăng nhập...", "正在处理": "Đang xử lý", + "正在处理,请稍候": "", "正在处理大内容...": "Đang xử lý nội dung lớn...", "正在导出...": "Đang xuất...", + "正在批量处理,请稍候": "", + "正在批量检测,请稍候": "", "正在提交": "Đang gửi", "正在提交...": "Đang gửi...", "正在更新...": "Đang cập nhật...", "正在构造请求体预览...": "Đang tạo xem trước thân yêu cầu...", "正在检查 io.net 连接...": "Checking io.net connection...", "正在检查数据库一致性,请稍候...": "Đang kiểm tra tính nhất quán của cơ sở dữ liệu, vui lòng đợi...", + "正在检测,请稍候": "", "正在测试...": "Đang kiểm tra...", "正在测试第 ${current} - ${end} 个模型 (共 ${total} 个)": "Đang kiểm tra mô hình thứ ${current} - ${end} (tổng cộng ${total})", "正在登录...": "Đang đăng nhập...", @@ -2044,6 +2161,7 @@ "正在验证...": "Đang xác minh...", "正常": "Bình thường", "正常情况下,令牌的计费倍率由令牌所选的分组决定。特殊倍率可以根据「用户所在分组」进一步覆盖这个倍率。": "Normally, a token's billing ratio is determined by its selected group. Special ratios can override this based on the user's group.", + "正数为增加,负数为减少": "Số dương để tăng, số âm để giảm", "此代理仅用于图片请求转发,Webhook通知发送等,AI API请求仍然由服务器直接发出,可在渠道设置中单独配置代理": "Proxy này chỉ được sử dụng để chuyển tiếp yêu cầu hình ảnh, gửi thông báo webhook, v.v. Các yêu cầu AI API vẫn được gửi trực tiếp bởi máy chủ và proxy có thể được cấu hình riêng trong cài đặt kênh", "此修改将不可逆": "Sửa đổi này sẽ không thể đảo ngược", "此操作不可恢复,请仔细确认时间后再操作!": "Thao tác này không thể khôi phục, vui lòng xác nhận thời gian cẩn thận trước khi thực hiện!", @@ -2070,6 +2188,7 @@ "此项可选,用于覆盖请求参数。不支持覆盖 stream 参数": "Tùy chọn, được sử dụng để ghi đè tham số yêu cầu. Không hỗ trợ ghi đè tham số stream.", "此项可选,用于覆盖请求头参数": "Tùy chọn, được sử dụng để ghi đè tham số tiêu đề yêu cầu.", "此项可选,用于通过自定义API地址来进行 API 调用,末尾不要带/v1和/": "Tùy chọn cho các cuộc gọi API thông qua địa chỉ API tùy chỉnh, không thêm /v1 và / ở cuối", + "每个充值单位对应的 USD 金额,默认 1.0": "", "每个分组代表一个价格档位。管理员创建分组后,可以选择哪些档位对用户开放自选。": "Each group represents a pricing tier. After creating groups, admins can choose which tiers are open for user self-selection.", "每个用户最多可创建的令牌数量,默认 1000,设置过大可能会影响性能": "Số lượng token tối đa mỗi người dùng có thể tạo, mặc định 1000. Đặt quá lớn có thể ảnh hưởng hiệu suất", "每周": "Hàng tuần", @@ -2089,7 +2208,9 @@ "永久删除所有备用码(包括未使用的)": "Xóa vĩnh viễn tất cả các mã dự phòng (bao gồm cả mã chưa sử dụng)", "永久有效": "Có hiệu lực vĩnh viễn", "汇率": "Tỷ giá hối đoái", + "沙盒模式": "", "沙盒环境 RSA 私钥 Base64 (PKCS#8 DER)": "Khóa riêng RSA Base64 (PKCS#8 DER) môi trường sandbox", + "沙盒环境 Waffo API 密钥": "", "沙盒环境 Waffo 公钥 Base64 (X.509 DER)": "Khóa công khai Waffo Base64 (X.509 DER) môi trường sandbox", "没有匹配的字段": "Không có trường khớp", "没有匹配的日志条目": "No matching log entries", @@ -2116,12 +2237,12 @@ "注册用户": "Người dùng đã đăng ký", "注册设置": "Cài đặt đăng ký", "注意": "Lưu ý", - "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "Lưu ý: Đối với API không phải Chat, vui lòng đảm bảo nhập đúng địa chỉ API, nếu không có thể dẫn đến không sử dụng được", "注意:": "Lưu ý: ", "注意:JSON中重复的键只会保留最后一个同名键的值": "Lưu ý: Trong JSON, các khóa trùng lặp sẽ chỉ giữ lại giá trị của khóa cuối cùng có cùng tên", "注意:修改密码后,所有已登录的设备将被强制登出。": "Lưu ý: Sau khi thay đổi mật khẩu, tất cả các thiết bị đã đăng nhập sẽ bị buộc đăng xuất.", "注意:所有配置更改在保存后立即生效。": "Lưu ý: Tất cả các thay đổi cấu hình có hiệu lực ngay sau khi lưu.", "注意:请确保您的邮箱地址正确,否则您将无法找回密码。": "Lưu ý: Vui lòng đảm bảo địa chỉ email của bạn là chính xác, nếu không bạn sẽ không thể khôi phục mật khẩu của mình.", + "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "Lưu ý: Đối với API không phải Chat, vui lòng đảm bảo nhập đúng địa chỉ API, nếu không có thể dẫn đến không sử dụng được", "注销": "Đăng xuất", "注销成功!": "Đăng xuất thành công!", "活跃文件": "Tệp đang hoạt động", @@ -2130,7 +2251,9 @@ "流式": "Streaming", "流式响应完成": "Luồng hoàn tất", "流式输出": "Đầu ra luồng", + "流状态": "", "流量端口": "Traffic Port", + "流错误详情": "", "浅色": "Sáng", "浅色模式": "Chế độ sáng", "测活": "Health Check", @@ -2226,14 +2349,6 @@ "添加键值对": "Thêm cặp khóa-giá trị", "添加问答": "Thêm hỏi đáp", "添加额度": "Thêm hạn ngạch", - "减少": "Giảm", - "覆盖": "Ghi đè", - "调整额度": "Điều chỉnh hạn ngạch", - "调整额度成功": "Điều chỉnh hạn ngạch thành công", - "当前额度": "Hạn ngạch hiện tại", - "变更": "Thay đổi", - "预计结果": "Kết quả dự kiến", - "正数为增加,负数为减少": "Số dương để tăng, số âm để giảm", "清理": "Dọn dẹp", "清理不活跃缓存": "Xóa cache không hoạt động", "清理历史日志": "Dọn dẹp nhật ký lịch sử", @@ -2254,6 +2369,7 @@ "清空测试结果": "Xóa kết quả kiểm tra", "清空该规则缓存": "Xóa bộ nhớ đệm của quy tắc này", "清空重定向": "Xóa chuyển hướng", + "清除": "", "清除历史日志": "Xóa nhật ký lịch sử", "清除失效兑换码": "Xóa mã đổi thưởng không hợp lệ", "清除所有模型": "Xóa tất cả các mô hình", @@ -2262,8 +2378,8 @@ "渠道ID": "ID kênh", "渠道ID,名称,密钥,API地址": "ID kênh, tên, khóa, Base URL", "渠道亲和性": "Độ ưu tiên kênh", - "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "Ưu ái kênh tái sử dụng kênh thành công lần cuối dựa trên key được trích xuất từ context yêu cầu hoặc JSON body.", "渠道亲和性:上游缓存命中": "Ưu ái kênh: Trúng bộ nhớ đệm upstream", + "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "Ưu ái kênh tái sử dụng kênh thành công lần cuối dựa trên key được trích xuất từ context yêu cầu hoặc JSON body.", "渠道优先级": "Ưu tiên kênh", "渠道信息": "Thông tin kênh", "渠道列表": "Danh sách kênh", @@ -2313,6 +2429,8 @@ "点击上传文件或拖拽文件到这里": "Nhấp để tải lên tệp hoặc kéo và thả tệp vào đây", "点击下方按钮通过 Telegram 完成绑定": "Nhấp vào nút bên dưới để hoàn tất liên kết qua Telegram", "点击修改": "Nhấp để sửa đổi", + "点击处理删除模型": "", + "点击处理新增模型": "", "点击复制": "Nhấp để sao chép", "点击复制ID": "Click to copy ID", "点击复制模型名称": "Nhấp để sao chép tên mô hình", @@ -2346,6 +2464,7 @@ "状态页面Slug": "Slug trang trạng thái", "环境变量": "Environment Variables", "生产环境 RSA 私钥 Base64 (PKCS#8 DER)": "Khóa riêng RSA Base64 (PKCS#8 DER) môi trường sản xuất", + "生产环境 Waffo API 密钥": "", "生产环境 Waffo 公钥 Base64 (X.509 DER)": "Khóa công khai Waffo Base64 (X.509 DER) môi trường sản xuất", "生成": "Tạo", "生成中...": "Đang tạo...", @@ -2396,6 +2515,7 @@ "用户信息": "Thông tin người dùng", "用户信息更新成功!": "Cập nhật thông tin người dùng thành công!", "用户信息缺失": "Thiếu thông tin người dùng", + "用户最大令牌数量": "Số token tối đa mỗi người dùng", "用户分组": "Nhóm người dùng", "用户分组和额度管理": "Quản lý nhóm người dùng và hạn ngạch", "用户分组的联动作用": "How user group affects other features", @@ -2415,6 +2535,7 @@ "用户名字段(可选)": "Trường tên người dùng (tùy chọn)", "用户名或邮箱": "Tên người dùng hoặc email", "用户名称": "Tên người dùng", + "用户在充值页面看到的支付方式名称,例如:Credit Card": "", "用户在线": "Người dùng trực tuyến", "用户头像": "Ảnh đại diện người dùng", "用户密码": "Mật khẩu người dùng", @@ -2424,7 +2545,6 @@ "用户指南": "Hướng dẫn người dùng", "用户控制面板,管理账户": "Bảng điều khiển người dùng để quản lý tài khoản", "用户新建令牌时可选的分组,格式为 JSON 字符串,例如:{\"vip\": \"VIP 用户\", \"test\": \"测试\"},表示用户可以选择 vip 分组和 test 分组": "Nhóm người dùng có thể chọn khi tạo mã thông báo, ở định dạng chuỗi JSON, ví dụ: {\"vip\": \"Người dùng VIP\", \"test\": \"Kiểm tra\"}, cho biết người dùng có thể chọn nhóm vip và nhóm test", - "用户最大令牌数量": "Số token tối đa mỗi người dùng", "用户权限": "Quyền người dùng", "用户每周期最多请求完成次数": "Số lần yêu cầu thành công tối đa của người dùng mỗi chu kỳ", "用户每周期最多请求次数": "Số lần yêu cầu tối đa của người dùng mỗi chu kỳ", @@ -2465,6 +2585,7 @@ "留空则使用默认设置": "Để trống để sử dụng cài đặt mặc định", "留空则保持原有密钥": "Để trống để giữ khóa hiện tại", "留空则禁用": "Để trống để vô hiệu hóa", + "留空则自动使用 服务器地址 + /api/waffo/webhook": "", "留空则自动生成": "Để trống để tự động tạo", "留空则默认使用服务器地址,注意不能携带http://或者https://": "Nếu để trống, địa chỉ máy chủ sẽ được sử dụng theo mặc định. Lưu ý rằng không được bao gồm http:// hoặc https://", "登 录": "Đăng nhập", @@ -2515,6 +2636,7 @@ "硬件类型": "Hardware Type", "硬件配置": "Hardware Configuration", "确定": "Xác nhận", + "确定?": "Chắc chắn?", "确定删除": "Xác nhận xóa", "确定删除此令牌?": "Bạn có chắc chắn muốn xóa mã thông báo này không?", "确定删除此兑换码?": "Bạn có chắc chắn muốn xóa mã đổi thưởng này không?", @@ -2537,6 +2659,7 @@ "确定是否要复制此渠道?": "Bạn có chắc chắn muốn sao chép kênh này không?", "确定是否要注销此用户?": "Bạn có chắc chắn muốn hủy kích hoạt người dùng này không?", "确定清除所有失效兑换码?": "Bạn có chắc chắn muốn xóa tất cả các mã đổi thưởng không hợp lệ không?", + "确定要仅检测全部渠道上游模型更新吗?(不执行新增/删除)": "", "确定要修改所有子渠道优先级为 ": "Xác nhận sửa đổi tất cả các ưu tiên kênh con thành ", "确定要修改所有子渠道权重为 ": "Xác nhận sửa đổi tất cả các trọng số kênh con thành ", "确定要充值 $": "Confirm to recharge $", @@ -2557,6 +2680,7 @@ "确定要删除选中的": "Are you sure you want to delete the selected", "确定要启用所有密钥吗?": "Bạn có chắc chắn muốn bật tất cả các khóa không?", "确定要启用此用户吗?": "Bạn có chắc chắn muốn bật người dùng này không?", + "确定要对全部渠道执行上游模型更新吗?": "", "确定要提升此用户吗?": "Bạn có chắc chắn muốn thăng cấp người dùng này không?", "确定要更新所有已启用通道余额吗?": "Bạn có chắc chắn muốn cập nhật số dư của tất cả các kênh đã bật không?", "确定要测试所有未手动禁用渠道吗?": "Bạn có chắc chắn muốn kiểm tra tất cả các kênh ngoại trừ các kênh bị vô hiệu hóa thủ công không?", @@ -2567,7 +2691,6 @@ "确定要降级此用户吗?": "Bạn có chắc chắn muốn hạ cấp người dùng này không?", "确定重置": "Xác nhận đặt lại", "确定重置模型倍率吗?": "Xác nhận đặt lại tỷ lệ mô hình?", - "确定?": "Chắc chắn?", "确认": "Xác nhận", "确认作废": "Xác nhận vô hiệu", "确认修改": "Xác nhận sửa đổi", @@ -2575,8 +2698,8 @@ "确认冲突项修改": "Xác nhận sửa đổi mục xung đột", "确认删除": "Xác nhận xóa", "确认删除模型": "Confirm Delete Model", - "确认删除该分组的所有规则?": "Delete all rules for this group?", "确认删除该分组?": "Confirm delete this group?", + "确认删除该分组的所有规则?": "Delete all rules for this group?", "确认删除该规则?": "Confirm delete this rule?", "确认取消密码登录": "Xác nhận hủy đăng nhập mật khẩu", "确认启用": "Xác nhận bật", @@ -2609,15 +2732,18 @@ "磁盘使用率超过此值时拒绝请求": "Từ chối yêu cầu khi sử dụng đĩa vượt quá giá trị này", "磁盘可用空间小于缓存最大总量设置": "Dung lượng đĩa trống nhỏ hơn cài đặt tổng dung lượng đệm tối đa", "磁盘命中": "Lượt trúng đĩa", + "磁盘缓存最大总量 (MB)": "Tổng dung lượng tối đa bộ nhớ đệm đĩa (MB)", "磁盘缓存占用的最大空间": "Dung lượng tối đa chiếm bởi bộ nhớ đệm đĩa", "磁盘缓存已清理": "Bộ nhớ đệm đĩa đã được dọn", - "磁盘缓存最大总量 (MB)": "Tổng dung lượng tối đa bộ nhớ đệm đĩa (MB)", "磁盘缓存设置(磁盘换内存)": "Cài đặt bộ nhớ đệm đĩa (đổi đĩa/bộ nhớ)", "磁盘缓存阈值 (MB)": "Ngưỡng bộ nhớ đệm đĩa (MB)", "示例": "Ví dụ", "示例:": "Ví dụ: ", "示例:{\"default\": [200, 100], \"vip\": [0, 1000]}。": "Ví dụ: {\"default\": [200, 100], \"vip\": [0, 1000]}.", "社群": "Cộng đồng", + "视频": "Video", + "视频Remix": "Remix video", + "视频无法在当前浏览器中播放,这可能是由于:": "The video cannot be played in this browser, possibly because:", "禁用": "Vô hiệu hóa", "禁用 Passkey": "Vô hiệu hóa Passkey", "禁用 store 透传": "Vô hiệu hóa truyền qua store", @@ -2647,10 +2773,12 @@ "积分": "Điểm", "积分兑换": "Đổi điểm", "积分记录": "Hồ sơ điểm", + "移动": "", + "移动请求头": "", "移除": "Remove", "移除 (-:)": "Remove (-:)", - "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "Việc xóa dấu bản quyền One API trước tiên phải được ủy quyền. Việc bảo trì dự án đòi hỏi rất nhiều nỗ lực. Nếu dự án này có ý nghĩa với bạn, vui lòng chủ động ủng hộ dự án này.", "移除 functionResponse.id 字段": "Xóa trường functionResponse.id", + "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "Việc xóa dấu bản quyền One API trước tiên phải được ủy quyền. Việc bảo trì dự án đòi hỏi rất nhiều nỗ lực. Nếu dự án này có ý nghĩa với bạn, vui lòng chủ động ủng hộ dự án này.", "空": "Trống", "窗口处理": "xử lý cửa sổ", "窗口等待": "chờ cửa sổ", @@ -2693,21 +2821,21 @@ "等待获取邮箱信息...": "Đang chờ lấy thông tin email...", "策略": "Chiến lược", "筛选": "Lọc", + "签到最大额度": "Hạn mức đăng nhập tối đa", + "签到最小额度": "Hạn mức đăng nhập tối thiểu", "签到功能允许用户每日签到获取随机额度奖励": "Tính năng đăng nhập cho phép người dùng đăng nhập hàng ngày để nhận phần thưởng hạn mức ngẫu nhiên", "签到失败": "Đăng nhập thất bại", "签到奖励将直接添加到您的账户余额": "Phần thưởng đăng nhập sẽ được thêm trực tiếp vào số dư tài khoản của bạn", "签到奖励的最大额度": "Hạn mức tối đa cho phần thưởng đăng nhập", "签到奖励的最小额度": "Hạn mức tối thiểu cho phần thưởng đăng nhập", "签到成功!获得": "Đăng nhập thành công! Đã nhận", - "签到最大额度": "Hạn mức đăng nhập tối đa", - "签到最小额度": "Hạn mức đăng nhập tối thiểu", "签到设置": "Cài đặt đăng nhập", "简介": "Giới thiệu", "简单来说:同一个令牌分组,不同等级的用户可以享受不同的价格。": "In short: same token group, different user tiers can enjoy different prices.", "简单模式": "Chế độ đơn giản", "简洁": "Đơn giản", - "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。": "Chế độ đơn giản chỉ trả về message; mã trạng thái và loại lỗi sẽ sử dụng giá trị mặc định hệ thống.", "简洁模式:按 type 全量清理对象,例如 redacted_thinking。": "Chế độ đơn giản: Dọn tất cả đối tượng theo type, VD: redacted_thinking.", + "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。": "Chế độ đơn giản chỉ trả về message; mã trạng thái và loại lỗi sẽ sử dụng giá trị mặc định hệ thống.", "管理": "Quản lý", "管理 Ollama 模型的拉取和删除": "Manage Ollama model pulling and deletion", "管理你的 LinuxDO OAuth App": "Quản lý LinuxDO OAuth App của bạn", @@ -2720,8 +2848,8 @@ "管理员暂时未设置任何关于内容": "Quản trị viên chưa đặt bất kỳ nội dung Giới thiệu tùy chỉnh nào", "管理员未开启 Creem 充值!": "The administrator has not enabled Creem recharge!", "管理员未开启Stripe充值!": "Quản trị viên chưa bật nạp tiền Stripe!", - "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "Quản trị viên chưa bật chức năng nạp tiền trực tuyến, vui lòng liên hệ quản trị viên để bật hoặc nạp tiền bằng mã đổi thưởng.", "管理员未开启在线充值!": "Quản trị viên chưa bật nạp tiền trực tuyến!", + "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "Quản trị viên chưa bật chức năng nạp tiền trực tuyến, vui lòng liên hệ quản trị viên để bật hoặc nạp tiền bằng mã đổi thưởng.", "管理员未开启在线支付功能,请联系管理员配置。": "Quản trị viên chưa bật thanh toán trực tuyến, vui lòng liên hệ quản trị viên.", "管理员未设置用户可选分组": "Quản trị viên chưa đặt nhóm người dùng có thể chọn", "管理员给用户分配的分组(如 vip)不仅决定用户身份,还会影响后续两个功能:": "The group assigned to a user (e.g., vip) not only determines their identity, but also affects the following two features:", @@ -2773,6 +2901,7 @@ "系统文档和帮助信息": "Tài liệu hệ thống và thông tin trợ giúp", "系统日志": "Nhật ký hệ thống", "系统消息": "Tin nhắn hệ thống", + "系统版本": "Phiên bản hệ thống", "系统状态": "Trạng thái hệ thống", "系统监控": "Giám sát hệ thống", "系统管理": "Quản lý hệ thống", @@ -2783,14 +2912,15 @@ "系统通知": "Thông báo hệ thống", "系统错误": "Lỗi hệ thống", "系统错误,请联系管理员": "Lỗi hệ thống, vui lòng liên hệ quản trị viên", + "约": "Khoảng", + "级别": "Cấp độ", "索引": "Chỉ mục", "紧凑列表": "Danh sách thu gọn", + "纯字符串会直接覆盖整条请求头,或者点击“查看 JSON 示例”按 token 规则处理。": "", + "纯文本": "Văn bản thuần túy", "累计消耗": "Tiêu thụ tích lũy", "累计签到": "Tổng số lần đăng nhập", "累计获得": "Tổng đã nhận", - "约": "Khoảng", - "级别": "Cấp độ", - "纯文本": "Văn bản thuần túy", "线路": "Tuyến", "线路描述": "Mô tả tuyến", "组": "Nhóm", @@ -2839,44 +2969,45 @@ "统计次数": "Thống kê số lần", "统计额度": "Thống kê hạn ngạch", "继续": "Tiếp tục", - "缓存 Tokens": "Tokens bộ nhớ đệm", "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Bộ nhớ đệm {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Cache {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio: {{ratio}})", + "缓存 Tokens": "Tokens bộ nhớ đệm", "缓存: {{cacheRatio}}": "Bộ nhớ đệm: {{cacheRatio}}", "缓存价格:{{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "Giá bộ nhớ đệm: {{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M tokens (Tỷ lệ bộ nhớ đệm: {{cacheRatio}})", "缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "Giá bộ nhớ đệm: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (Tỷ lệ bộ nhớ đệm: {{cacheRatio}})", "缓存倍率": "Tỷ lệ bộ nhớ đệm", "缓存倍率 {{cacheRatio}}": "Cache ratio {{cacheRatio}}", "缓存写": "Ghi bộ nhớ đệm", - "缓存创建 Tokens": "Tokens tạo bộ nhớ đệm", "缓存创建 {{price}} / 1M tokens": "Tạo bộ nhớ đệm {{price}} / 1M tokens", "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Tạo bộ nhớ đệm {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "Cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (ratio: {{ratio}})", + "缓存创建 Tokens": "Tokens tạo bộ nhớ đệm", + "缓存创建: {{cacheCreationRatio}}": "Tạo bộ nhớ đệm: {{cacheCreationRatio}}", "缓存创建: 1h {{cacheCreationRatio1h}}": "Tạo bộ nhớ đệm: 1h {{cacheCreationRatio1h}}", "缓存创建: 5m {{cacheCreationRatio5m}}": "Tạo bộ nhớ đệm: 5m {{cacheCreationRatio5m}}", "缓存创建: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Cache creation: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存创建: {{cacheCreationRatio}}": "Tạo bộ nhớ đệm: {{cacheCreationRatio}}", + "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Tạo bộ nhớ đệm: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số tạo bộ nhớ đệm {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存创建价格": "Giá tạo bộ nhớ đệm đầu vào", "缓存创建价格 {{symbol}}{{price}} / 1M tokens": "Giá tạo bộ nhớ đệm {{symbol}}{{price}} / 1M tokens", - "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "Cache creation price total: 5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "Giá tạo bộ nhớ đệm: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (Tỷ lệ tạo bộ nhớ đệm: {{cacheCreationRatio}})", "缓存创建价格:{{symbol}}{{price}} / 1M tokens": "Giá tạo bộ nhớ đệm: {{symbol}}{{price}} / 1M tokens", + "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "Cache creation price total: 5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens", "缓存创建倍率": "Tỷ lệ tạo bộ nhớ đệm", + "缓存创建倍率 {{cacheCreationRatio}}": "Cache creation ratio {{cacheCreationRatio}}", "缓存创建倍率 1h {{cacheCreationRatio1h}}": "Tỷ lệ tạo bộ nhớ đệm 1h {{cacheCreationRatio1h}}", "缓存创建倍率 5m {{cacheCreationRatio5m}}": "Tỷ lệ tạo bộ nhớ đệm 5m {{cacheCreationRatio5m}}", "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "Hệ số tạo bộ nhớ đệm 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存创建倍率 {{cacheCreationRatio}}": "Cache creation ratio {{cacheCreationRatio}}", - "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Tạo bộ nhớ đệm: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số tạo bộ nhớ đệm {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存条目数": "Số mục bộ nhớ đệm", "缓存目录": "Thư mục bộ nhớ đệm", "缓存目录磁盘空间": "Dung lượng đĩa thư mục bộ nhớ đệm", "缓存读": "Đọc bộ nhớ đệm", "缓存读 {{price}} / 1M tokens": "Đọc bộ nhớ đệm {{price}} / 1M tokens", + "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đọc bộ nhớ đệm: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số bộ nhớ đệm {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存读取价格": "Giá đọc bộ nhớ đệm đầu vào", "缓存读取价格 {{symbol}}{{price}} / 1M tokens": "Giá đọc bộ nhớ đệm {{symbol}}{{price}} / 1M tokens", "缓存读取价格:{{symbol}}{{price}} / 1M tokens": "Giá đọc bộ nhớ đệm: {{symbol}}{{price}} / 1M tokens", "缓存读取价格:{{symbol}}{{total}} / 1M tokens": "Giá đọc bộ nhớ đệm: {{symbol}}{{total}} / 1M tokens", - "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đọc bộ nhớ đệm: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số bộ nhớ đệm {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "缓存读取倍率": "", "缓存输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đầu vào bộ nhớ đệm: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số bộ nhớ đệm {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "编辑": "Chỉnh sửa", "编辑 OAuth 提供商": "Chỉnh sửa nhà cung cấp OAuth", @@ -2890,6 +3021,7 @@ "编辑失败": "Chỉnh sửa thất bại", "编辑密钥": "Chỉnh sửa khóa", "编辑成功": "Chỉnh sửa thành công", + "编辑支付方式": "", "编辑方式": "Chế độ chỉnh sửa", "编辑标签": "Chỉnh sửa thẻ", "编辑模型": "Chỉnh sửa mô hình", @@ -2991,13 +3123,14 @@ "获取": "Lấy", "获取 Discovery 配置": "Lấy cấu hình Discovery", "获取 Discovery 配置失败:": "Lấy cấu hình Discovery thất bại: ", + "获取 io.net API Key": "Get io.net API Key", "获取 OIDC 配置失败,请检查网络状况和 Well-Known URL 是否正确": "Lấy cấu hình OIDC thất bại, vui lòng kiểm tra trạng thái mạng và Well-Known URL có chính xác không", "获取 OIDC 配置成功!": "Lấy cấu hình OIDC thành công!", "获取 Ollama 版本失败": "Failed to get Ollama version", - "获取 io.net API Key": "Get io.net API Key", "获取2FA状态失败": "Lấy trạng thái 2FA thất bại", "获取中...": "Đang lấy...", "获取代码": "Lấy mã", + "获取令牌密钥失败": "", "获取充值配置失败": "Không thể lấy cấu hình nạp tiền", "获取充值配置异常": "Lỗi cấu hình nạp tiền", "获取初始化状态失败": "Lấy trạng thái khởi tạo thất bại", @@ -3035,9 +3168,9 @@ "补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "Hoàn thành {{completion}} tokens * Tỷ lệ đầu ra {{completionRatio}}", "补全 {{completion}} tokens / 1M tokens * {{symbol}}{{price}}": "Completion {{completion}} tokens / 1M tokens * {{symbol}}{{price}}", "补全价格": "Giá hoàn thành", - "补全价格已锁定": "Giá hoàn thành đã bị khóa", "补全价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "Giá hoàn thành: {{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (Tỷ lệ hoàn thành: {{completionRatio}})", "补全价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens": "Giá hoàn thành: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens", + "补全价格已锁定": "Giá hoàn thành đã bị khóa", "补全倍率": "Tỷ lệ hoàn thành", "补全倍率 {{completionRatio}}": "Tỷ lệ hoàn thành {{completionRatio}}", "补全倍率值": "Giá trị tỷ lệ hoàn thành", @@ -3061,10 +3194,8 @@ "规则为 JSON 数组;可视化与 JSON 模式共用同一份数据。": "Quy tắc là mảng JSON; chế độ trực quan và JSON dùng chung dữ liệu.", "规则名称(可读性更好,也会出现在管理侧日志中)。": "Tên quy tắc (dễ đọc hơn, cũng hiển thị trong log quản trị).", "规则导航": "Điều hướng quy tắc", + "规则描述(可选)": "", "规则未找到,请刷新后重试": "Không tìm thấy quy tắc, vui lòng làm mới và thử lại", - "视频": "Video", - "视频Remix": "Remix video", - "视频无法在当前浏览器中播放,这可能是由于:": "The video cannot be played in this browser, possibly because:", "角色": "Vai trò", "解析响应数据时发生错误": "Đã xảy ra lỗi khi phân tích dữ liệu phản hồi", "解析密钥文件失败: {{msg}}": "Phân tích tệp khóa thất bại: {{msg}}", @@ -3076,9 +3207,6 @@ "触发": "Kích hoạt", "触发关键词": "Từ khóa kích hoạt", "触发词": "Từ kích hoạt", - "警告": "Cảnh báo", - "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔": "Cảnh báo: Sau khi bật giữ kết nối, nếu kênh bị lỗi sau khi dữ liệu giữ kết nối đã được ghi, hệ thống không thể thử lại. Nếu bắt buộc phải bật, nên đặt khoảng thời gian Ping càng lớn càng tốt", - "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "Cảnh báo: Vô hiệu hóa xác thực hai yếu tố sẽ xóa vĩnh viễn cài đặt xác minh và tất cả mã dự phòng của bạn, thao tác này không thể hoàn tác!", "计价币种": "Pricing Currency", "计算中": "Calculating", "计算成本": "Calculate Cost", @@ -3086,6 +3214,7 @@ "计费": "Thanh toán", "计费乘数,倍率越低费用越低。例如倍率 0.5 表示半价。": "Billing multiplier — the lower the ratio, the lower the cost. E.g., ratio 0.5 means half price.", "计费开始": "Billing Start", + "计费摘要": "", "计费方式": "Chế độ tính phí", "计费显示模式": "Chế độ hiển thị tính phí", "计费模式": "Billing mode", @@ -3093,6 +3222,7 @@ "计费规则": "Quy tắc thanh toán", "计费过程": "Quá trình thanh toán", "订单号": "Số đơn hàng", + "订单支付方式": "Phương thức thanh toán đơn hàng", "订阅": "Đăng ký", "订阅剩余": "Đăng ký còn lại", "订阅套餐": "Gói đăng ký", @@ -3111,6 +3241,7 @@ "记录请求与错误日志IP": "Ghi lại IP nhật ký yêu cầu và lỗi", "设备": "Device", "设备类型偏好": "Tùy chọn loại thiết bị", + "设置": "", "设置 Logo": "Cài đặt Logo", "设置2FA失败": "Cài đặt 2FA thất bại", "设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}": "Đặt giảm giá tương ứng với các số tiền nạp khác nhau, khóa là số tiền nạp, giá trị là tỷ lệ giảm giá, ví dụ: {\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}", @@ -3140,6 +3271,7 @@ "设置默认地区和特定模型的专用地区": "Đặt khu vực mặc định và khu vực dành riêng cho các mô hình cụ thể", "设计与开发由": "Thiết kế và phát triển bởi", "设计版本": "b80c3466cb6feafeb3990c7820e10e50", + "设请求头": "", "访问 io.net 控制台的 API Keys 页面": "Visit the API Keys page of the io.net console", "访问容器": "Access Container", "访问模型部署功能需要先启用 io.net 部署服务": "Accessing model deployment features requires enabling the io.net deployment service first", @@ -3158,6 +3290,8 @@ "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "Tỷ lệ hoàn thành của mô hình này được backend cố định ở {{ratio}}. Không thể chỉnh giá hoàn thành tại đây.", "该渠道已开启请求透传,参数覆写、模型重定向等 NewAPI 内置功能将失效,非最佳实践。": "Kênh này đã bật truyền qua yêu cầu; các tính năng tích hợp của NewAPI như ghi đè tham số và chuyển hướng mô hình sẽ bị vô hiệu hóa. Đây không phải là thực hành tốt nhất.", "该渠道已开启请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "Kênh này đã bật truyền qua yêu cầu. Các tính năng tích hợp của NewAPI như ghi đè tham số, chuyển hướng mô hình và thích ứng kênh sẽ bị vô hiệu hóa. Đây không phải là thực hành tốt nhất. Nếu phát sinh vấn đề, vui lòng không gửi issue.", + "该渠道暂无可处理的上游模型更新": "", + "该渠道未开启上游模型更新检测": "", "该规则未启用“作用域:包含规则名称”,无法按规则清空缓存。": "Quy tắc này chưa bật \"Phạm vi: Bao gồm tên quy tắc\", không thể xóa bộ nhớ đệm theo quy tắc.", "该规则未设置参数覆盖模板": "Quy tắc này chưa thiết lập mẫu ghi đè tham số", "该规则的缓存保留时长;0 表示使用默认 TTL:": "Thời gian lưu bộ nhớ đệm cho quy tắc này; 0 nghĩa là sử dụng TTL mặc định: ", @@ -3173,10 +3307,10 @@ "语音输入": "Đầu vào giọng nói", "语音输出": "Đầu ra giọng nói", "说明": "Mô tả", - "说明信息": "Thông tin mô tả", "说明:": "Mô tả: ", "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。": "Lưu ý: Bài kiểm tra trên trang này sử dụng yêu cầu không streaming. Nếu kênh chỉ hỗ trợ phản hồi streaming, bài kiểm tra có thể thất bại. Vui lòng dựa vào sử dụng thực tế.", "说明:生成结果是可直接粘贴到渠道密钥里的 JSON(包含 access_token / refresh_token / account_id)。": "Lưu ý: Kết quả tạo ra là JSON có thể dán trực tiếp vào khóa kênh (bao gồm access_token / refresh_token / account_id).", + "说明信息": "Thông tin mô tả", "请上传": "Vui lòng tải lên", "请上传图片": "Vui lòng tải lên hình ảnh", "请上传密钥文件": "Vui lòng tải lên tệp khóa", @@ -3192,6 +3326,7 @@ "请先填写 Ollama API 地址": "Please fill in Ollama API address first", "请先填写服务器地址": "Vui lòng điền địa chỉ máy chủ trước", "请先开启并填写音频输入价格。": "Hãy bật và điền giá đầu vào âm thanh trước.", + "请先新增模型或从左侧列表选择一个模型": "", "请先登录": "Vui lòng đăng nhập trước", "请先登录!": "Vui lòng đăng nhập trước!", "请先粘贴回调 URL": "Vui lòng dán URL callback trước", @@ -3213,6 +3348,7 @@ "请务必保存好您的密码,否则将无法找回": "Vui lòng đảm bảo lưu mật khẩu của bạn, nếu không bạn sẽ không thể khôi phục nó", "请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "Đừng quá tin tưởng tính năng này, IP có thể bị giả mạo, vui lòng sử dụng cùng với nginx và các cổng khác như cdn", "请勿重复提交": "Vui lòng không gửi lại", + "请在 Waffo 后台获取 API 密钥、商户 ID 以及 RSA 密钥对,并配置回调地址。": "", "请在系统设置页面编辑分组倍率以添加新的分组:": "Vui lòng chỉnh sửa tỷ lệ nhóm trên trang cài đặt hệ thống để thêm nhóm mới:", "请填写": "Vui lòng điền", "请填写完整的产品信息": "Vui lòng điền đầy đủ thông tin sản phẩm", @@ -3246,7 +3382,6 @@ "请求参数无效": "Invalid request parameters", "请求发生错误": "Đã xảy ra lỗi yêu cầu", "请求发生错误: ": "Đã xảy ra lỗi yêu cầu: ", - "模型价格未配置": "Giá mô hình chưa được cấu hình", "请求后端接口失败:": "Yêu cầu giao diện phụ trợ thất bại: ", "请求失败": "Yêu cầu thất bại", "请求失败,请重试": "Yêu cầu thất bại, vui lòng thử lại", @@ -3294,11 +3429,13 @@ "请输入": "Vui lòng nhập", "请输入 API Key,一行一个,格式:APIKey|Region": "Enter API Key, one per line, format: APIKey|Region", "请输入 API Key,格式:APIKey|Region": "Enter API Key, format: APIKey|Region", - "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com": "Vui lòng nhập AZURE_OPENAI_ENDPOINT, ví dụ: https://docs-test-001.openai.azure.com", "请输入 Authorization Endpoint": "Vui lòng nhập Authorization Endpoint", + "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com": "Vui lòng nhập AZURE_OPENAI_ENDPOINT, ví dụ: https://docs-test-001.openai.azure.com", "请输入 Client ID": "Vui lòng nhập Client ID", "请输入 Client Secret": "Vui lòng nhập Client Secret", "请输入 ID": "Vui lòng nhập ID", + "请输入 io.net API Key": "Please enter io.net API Key", + "请输入 io.net API Key(敏感信息不显示)": "Please enter io.net API Key (sensitive information not displayed)", "请输入 JSON 格式的 OAuth 凭据,例如:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}": "Vui lòng nhập thông tin OAuth dạng JSON, VD:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}", "请输入 JSON 格式的密钥内容,例如:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}": "Vui lòng nhập nội dung khóa ở định dạng JSON, ví dụ:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}", "请输入 OIDC 的 Well-Known URL": "Vui lòng nhập Well-Known URL của OIDC", @@ -3306,8 +3443,6 @@ "请输入 Token Endpoint": "Vui lòng nhập Token Endpoint", "请输入 URL": "Vui lòng nhập URL", "请输入 User Info Endpoint": "Vui lòng nhập User Info Endpoint", - "请输入 io.net API Key": "Please enter io.net API Key", - "请输入 io.net API Key(敏感信息不显示)": "Please enter io.net API Key (sensitive information not displayed)", "请输入6位验证码或8位备用码": "Vui lòng nhập mã xác minh 6 chữ số hoặc mã dự phòng 8 chữ số", "请输入API地址": "Vui lòng nhập địa chỉ API", "请输入API地址!": "Vui lòng nhập địa chỉ API!", @@ -3317,9 +3452,9 @@ "请输入Gotify服务器地址": "Vui lòng nhập địa chỉ máy chủ Gotify", "请输入Gotify服务器地址,例如: https://gotify.example.com": "Vui lòng nhập địa chỉ máy chủ Gotify, ví dụ: https://gotify.example.com", "请输入JSON数组,如 [\"model-a\",\"model-b\"]": "Vui lòng nhập mảng JSON, ví dụ [\"model-a\",\"model-b\"]", - "请输入URL链接": "Vui lòng nhập liên kết URL", "请输入Uptime Kuma地址": "Vui lòng nhập địa chỉ Uptime Kuma", "请输入Uptime Kuma服务地址,如:https://status.example.com": "Vui lòng nhập địa chỉ dịch vụ Uptime Kuma, như: https://status.example.com", + "请输入URL链接": "Vui lòng nhập liên kết URL", "请输入Webhook地址": "Vui lòng nhập địa chỉ Webhook", "请输入Webhook地址,例如: https://example.com/webhook": "Vui lòng nhập địa chỉ Webhook, ví dụ: https://example.com/webhook", "请输入个人简介": "Vui lòng nhập tiểu sử", @@ -3352,14 +3487,14 @@ "请输入备注(仅管理员可见)": "Vui lòng nhập ghi chú (chỉ quản trị viên mới thấy)", "请输入套餐标题": "Vui lòng nhập tiêu đề gói", "请输入完整的 JSON 格式密钥内容": "Vui lòng nhập nội dung khóa định dạng JSON đầy đủ", - "请输入完整的URL链接": "Vui lòng nhập liên kết URL đầy đủ", "请输入完整的URL,例如:https://api.openai.com/v1/chat/completions": "Vui lòng nhập URL đầy đủ, ví dụ: https://api.openai.com/v1/chat/completions", + "请输入完整的URL链接": "Vui lòng nhập liên kết URL đầy đủ", "请输入容器名称": "Please enter container name", "请输入密码": "Vui lòng nhập mật khẩu", "请输入密钥": "Vui lòng nhập khóa", - "请输入密钥!": "Vui lòng nhập khóa!", "请输入密钥,一行一个": "Vui lòng nhập khóa, mỗi dòng một cái", "请输入密钥,一行一个,格式:AccessKey|SecretAccessKey|Region": "Enter keys one per line, format: AccessKey|SecretAccessKey|Region", + "请输入密钥!": "Vui lòng nhập khóa!", "请输入延长时长": "Please enter extension duration", "请输入总额度": "Vui lòng nhập tổng hạn mức", "请输入您的密码": "Vui lòng nhập mật khẩu của bạn", @@ -3428,9 +3563,9 @@ "请输入转账金额": "Vui lòng nhập số tiền chuyển", "请输入运行时长": "Please enter runtime duration", "请输入邮箱": "Vui lòng nhập email", + "请输入邮箱!": "Vui lòng nhập email của bạn!", "请输入邮箱地址": "Vui lòng nhập địa chỉ email", "请输入邮箱验证码!": "Vui lòng nhập mã xác minh email!", - "请输入邮箱!": "Vui lòng nhập email của bạn!", "请输入部署名称": "Please enter deployment name", "请输入部署名称以完成二次确认": "Enter deployment name to complete secondary confirmation", "请输入部署地区,例如:us-central1\n支持使用模型映射格式\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}": "Vui lòng nhập khu vực triển khai, ví dụ: us-central1\nHỗ trợ sử dụng định dạng ánh xạ mô hình\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}", @@ -3491,12 +3626,17 @@ "请阅读并同意": "Vui lòng đọc và đồng ý", "读取": "Đọc", "读取失败": "Đọc thất bại", + "调整额度": "Điều chỉnh hạn ngạch", + "调整额度成功": "Điều chỉnh hạn ngạch thành công", "调用次数": "Số lần gọi", "调用次数分布": "Phân phối số lần gọi", "调用次数排行": "Xếp hạng số lần gọi", "调用趋势": "Xu hướng cuộc gọi", "调试信息": "Thông tin gỡ lỗi", "谨慎": "Thận trọng", + "警告": "Cảnh báo", + "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔": "Cảnh báo: Sau khi bật giữ kết nối, nếu kênh bị lỗi sau khi dữ liệu giữ kết nối đã được ghi, hệ thống không thể thử lại. Nếu bắt buộc phải bật, nên đặt khoảng thời gian Ping càng lớn càng tốt", + "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "Cảnh báo: Vô hiệu hóa xác thực hai yếu tố sẽ xóa vĩnh viễn cài đặt xác minh và tất cả mã dự phòng của bạn, thao tác này không thể hoàn tác!", "豆包": "Doubao", "财务": "Tài chính", "账单": "Hóa đơn", @@ -3549,6 +3689,8 @@ "路径正则": "Regex đường dẫn", "路径正则(每行一个)": "Regex đường dẫn (mỗi dòng một mục)", "跳转": "Nhảy", + "转大写": "", + "转小写": "", "转账": "Chuyển tiền", "转账成功": "Chuyển tiền thành công", "转账给用户": "Chuyển tiền cho người dùng", @@ -3557,12 +3699,14 @@ "轮询模式": "Chế độ thăm dò", "轮询模式必须搭配Redis和内存缓存功能使用,否则性能将大幅降低,并且无法实现轮询功能": "Chế độ thăm dò phải được sử dụng với Redis và chức năng bộ nhớ đệm, nếu không hiệu suất sẽ giảm đáng kể và chức năng thăm dò sẽ không thể thực hiện được", "软件版本": "Phiên bản phần mềm", + "软错误": "", "输入": "Đầu vào", + "输入 {{price}} / 1M tokens": "Đầu vào {{price}} / 1M tokens", + "输入 $/1M tokens": "", "输入 OIDC 的 Authorization Endpoint": "Nhập Authorization Endpoint của OIDC", "输入 OIDC 的 Client ID": "Nhập Client ID của OIDC", "输入 OIDC 的 Token Endpoint": "Nhập Token Endpoint của OIDC", "输入 OIDC 的 Userinfo Endpoint": "Nhập Userinfo Endpoint của OIDC", - "输入 {{price}} / 1M tokens": "Đầu vào {{price}} / 1M tokens", "输入IP地址后回车,如:8.8.8.8": "Nhập địa chỉ IP và nhấn Enter, ví dụ: 8.8.8.8", "输入JSON对象": "Nhập đối tượng JSON", "输入价格": "Giá đầu vào", @@ -3582,6 +3726,7 @@ "输入模型倍率": "Nhập tỷ lệ mô hình", "输入模型名称,例如 gpt-4.1": "Nhập tên mô hình, ví dụ gpt-4.1", "输入每次价格": "Nhập giá mỗi lần", + "输入每次调用价格": "", "输入端口后回车,如:80 或 8000-8999": "Nhập cổng và nhấn Enter, ví dụ: 80 hoặc 8000-8999", "输入系统提示词,用户的系统提示词将优先于此设置": "Nhập từ nhắc hệ thống, từ nhắc hệ thống của người dùng sẽ được ưu tiên hơn cài đặt này", "输入自定义模型名称": "Nhập tên mô hình tùy chỉnh", @@ -3597,14 +3742,14 @@ "输入验证码完成设置": "Nhập mã xác minh để hoàn tất cài đặt", "输出": "Đầu ra", "输出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}": "Đầu ra {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đầu ra: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số hoàn thành {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đầu ra: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số đầu ra {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "输出价格": "Giá đầu ra", "输出价格 {{symbol}}{{price}} / 1M tokens": "Giá đầu ra {{symbol}}{{price}} / 1M tokens", "输出价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "Giá đầu ra: {{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (Tỷ lệ hoàn thành: {{completionRatio}})", "输出价格:{{symbol}}{{price}} / 1M tokens": "Giá đầu ra: {{symbol}}{{price}} / 1M tokens", "输出价格:{{symbol}}{{total}} / 1M tokens": "Giá đầu ra: {{symbol}}{{total}} / 1M tokens", "输出倍率 {{completionRatio}}": "Output ratio {{completionRatio}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đầu ra: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số hoàn thành {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đầu ra: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số đầu ra {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "边栏设置": "Cài đặt thanh bên", "过期于": "Hết hạn vào", "过期时间": "Thời gian hết hạn", @@ -3623,6 +3768,7 @@ "返回修改": "Go back and edit", "返回列表": "Quay lại danh sách", "返回登录": "Quay lại đăng nhập", + "返回错误": "", "返回首页": "Quay lại trang chủ", "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "Giao diện này mặc định nhập theo giá, khi lưu sẽ tự động quy đổi lại thành JSON tỷ lệ mà backend yêu cầu.", "这些价格都是可选项,不填也可以。": "Tất cả các mức giá này đều là tùy chọn và có thể để trống.", @@ -3700,6 +3846,7 @@ "选择预设模板(可选)": "Chọn mẫu đặt trước (tùy chọn)", "选项": "Tùy chọn", "透传请求体": "Truyền qua thân yêu cầu", + "透传请求头": "", "递归": "Đệ quy", "递归策略": "Chiến lược đệ quy", "通义千问": "Qwen", @@ -3796,7 +3943,9 @@ "配置 SMTP": "Cấu hình SMTP", "配置 Telegram 登录": "Cấu hình đăng nhập Telegram", "配置 Turnstile": "Cấu hình Turnstile", + "配置 Waffo 充值时可用的支付方式,保存后在充值页面展示给用户。": "", "配置 WeChat Server": "Cấu hình WeChat Server", + "配置:": "Cấu hình:", "配置信息": "Thông tin cấu hình", "配置列表": "Danh sách cấu hình", "配置名称": "Tên cấu hình", @@ -3824,7 +3973,6 @@ "配置详情": "Chi tiết cấu hình", "配置说明": "Hướng dẫn cấu hình", "配置邮箱域名白名单": "Cấu hình danh sách trắng tên miền email", - "配置:": "Cấu hình:", "重发": "Gửi lại", "重发验证码": "Gửi lại mã xác minh", "重启部署失败": "Failed to restart deployment", @@ -3837,6 +3985,7 @@ "重定向 URL": "URL chuyển hướng", "重定向 URL 填": "Điền URL chuyển hướng", "重定向地址": "Địa chỉ chuyển hướng", + "重新上传": "", "重新发送": "Gửi lại", "重新生成": "Tạo lại", "重新生成备用码": "Tạo lại mã dự phòng", @@ -3915,15 +4064,15 @@ "限制周期统一使用上方配置的“限制周期”值。": "Chu kỳ giới hạn sử dụng thống nhất giá trị \"Chu kỳ giới hạn\" được cấu hình ở trên.", "限流": "Giới hạn tốc độ", "限购": "Giới hạn mua", - "随机": "Ngẫu nhiên", - "随机模式": "Chế độ ngẫu nhiên", - "随机种子 (留空为随机)": "Hạt giống ngẫu nhiên (để trống cho ngẫu nhiên)", "隐私政策": "Chính sách bảo mật", "隐私政策已更新": "Chính sách bảo mật đã được cập nhật", "隐私政策更新失败": "Cập nhật chính sách bảo mật thất bại", "隐私设置": "Cài đặt quyền riêng tư", "隐藏操作项": "Ẩn hành động", "隐藏调试": "Ẩn gỡ lỗi", + "随机": "Ngẫu nhiên", + "随机模式": "Chế độ ngẫu nhiên", + "随机种子 (留空为随机)": "Hạt giống ngẫu nhiên (để trống cho ngẫu nhiên)", "零一万物": "01.AI", "需要安全验证": "Yêu cầu xác minh bảo mật", "需要添加的额度(支持负数)": "Hạn ngạch cần thêm (hỗ trợ số âm)", @@ -3941,13 +4090,15 @@ "音频补全价格": "Giá hoàn thành âm thanh", "音频补全价格:{{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (音频补全倍率: {{audioCompRatio}})": "Giá hoàn thành âm thanh: {{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (Tỷ lệ hoàn thành âm thanh: {{audioCompRatio}})", "音频补全价格:{{symbol}}{{price}} / 1M tokens": "Giá hoàn thành âm thanh: {{symbol}}{{price}} / 1M tokens", + "音频补全倍率": "", "音频补全倍率(仅部分模型支持该计费)": "Tỷ lệ hoàn thành âm thanh (chỉ được hỗ trợ bởi một số mô hình để tính phí)", + "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đầu vào âm thanh: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số âm thanh {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "音频输入价格": "Giá đầu vào âm thanh", "音频输入价格:{{symbol}}{{price}} / 1M tokens": "Giá đầu vào âm thanh: {{symbol}}{{price}} / 1M tokens", + "音频输入倍率": "", "音频输入相关的倍率设置,键为模型名称,值为倍率": "Cài đặt tỷ lệ liên quan đến đầu vào âm thanh, khóa là tên mô hình, giá trị là tỷ lệ", - "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đầu vào âm thanh: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số âm thanh {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "Cài đặt tỷ lệ liên quan đến hoàn thành đầu ra âm thanh, khóa là tên mô hình, giá trị là tỷ lệ", "音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Đầu ra âm thanh: {{tokens}} / 1M * hệ số mô hình {{modelRatio}} * hệ số âm thanh {{audioRatio}} * hệ số hoàn thành âm thanh {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "Cài đặt tỷ lệ liên quan đến hoàn thành đầu ra âm thanh, khóa là tên mô hình, giá trị là tỷ lệ", "页脚": "Chân trang", "页面未找到,请检查您的浏览器地址是否正确": "Không tìm thấy trang, vui lòng kiểm tra xem địa chỉ trình duyệt của bạn có chính xác không", "页面渲染出错,请刷新页面重试": "Đã xảy ra lỗi khi hiển thị trang. Vui lòng tải lại trang và thử lại.", @@ -3965,9 +4116,10 @@ "预览更新": "Xem trước cập nhật", "预览模板": "Xem trước mẫu", "预览请求体": "Xem trước thân yêu cầu", - "预警阈值必须为正数": "Ngưỡng cảnh báo phải là số dương", "预计结束": "Estimated End", + "预计结果": "Kết quả dự kiến", "预设模板": "Mẫu đặt trước", + "预警阈值必须为正数": "Ngưỡng cảnh báo phải là số dương", "频率惩罚,减少重复词汇的出现": "Phạt tần suất, giảm sự lặp lại của từ", "频率限制的周期(分钟)": "Chu kỳ giới hạn tần suất (phút)", "颜色": "Màu sắc", @@ -4016,10 +4168,6 @@ "默认折叠侧边栏": "Mặc định thu gọn thanh bên", "默认测试模型": "Mô hình kiểm tra mặc định", "默认用户消息": "Xin chào", - "默认补全倍率": "Tỷ lệ hoàn thành mặc định", - "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(Hiện tại chỉ hỗ trợ giao diện Epay, địa chỉ máy chủ phía trên được sử dụng làm địa chỉ gọi lại theo mặc định!)", - ",当前无生效订阅,将自动使用钱包": ", hiện không có gói đăng ký hiệu lực, sẽ tự động dùng ví.", - ",时间:": ", thời gian:", - ",点击更新": ", nhấn để cập nhật" + "默认补全倍率": "Tỷ lệ hoàn thành mặc định" } } diff --git a/web/src/i18n/locales/zh-CN.json b/web/src/i18n/locales/zh-CN.json index a64e50d0..155e877f 100644 --- a/web/src/i18n/locales/zh-CN.json +++ b/web/src/i18n/locales/zh-CN.json @@ -6,49 +6,73 @@ " 个模型设置相同的值": " 个模型设置相同的值", " 吗?": " 吗?", " 秒": " 秒", + " 秒。": " 秒。", + ",当前无生效订阅,将自动使用钱包": ",当前无生效订阅,将自动使用钱包", ",时间:": ",时间:", ",点击更新": ",点击更新", + "(共 {{total}} 个,省略 {{omit}} 个)": "(共 {{total}} 个,省略 {{omit}} 个)", + "(共 {{total}} 个)": "(共 {{total}} 个)", "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)", "(筛选后显示 {{count}} 条)_other": "(筛选后显示 {{count}} 条)", "(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}", "(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}", "(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}", + "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}", "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。", "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。", "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", + "{{count}} 项操作_other": "{{count}} 项操作", "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", + "{{name}} ID": "{{name}} ID", "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", + "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", "• 视频服务商的跨域限制": "• 视频服务商的跨域限制", "• 防盗链保护机制": "• 防盗链保护机制", "• 需要特定的请求头或认证": "• 需要特定的请求头或认证", "© {{currentYear}}": "© {{currentYear}}", "| 基于": "| 基于", "$/1M tokens": "$/1M tokens", + "$/次": "$/次", "0 - 最低": "0 - 最低", + "0 表示不限": "0 表示不限", "0.002-1之间的小数": "0.002-1之间的小数", "0.1以上的小数": "0.1以上的小数", + "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。": "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。", "10 - 最高": "10 - 最高", + "1h缓存创建 {{price}} / 1M tokens": "1h缓存创建 {{price}} / 1M tokens", + "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})", + "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", + "1h缓存创建价格 {{symbol}}{{price}} / 1M tokens": "1h缓存创建价格 {{symbol}}{{price}} / 1M tokens", "1h缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h缓存创建倍率: {{cacheCreationRatio1h}})": "1h缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h缓存创建倍率: {{cacheCreationRatio1h}})", + "1h缓存创建价格:{{symbol}}{{price}} / 1M tokens": "1h缓存创建价格:{{symbol}}{{price}} / 1M tokens", "2 - 低": "2 - 低", "2025年5月10日后添加的渠道,不需要再在部署的时候移除模型名称中的\".\"": "2025年5月10日后添加的渠道,不需要再在部署的时候移除模型名称中的\".\"", "360智脑": "360智脑", "5 - 正常(默认)": "5 - 正常(默认)", + "5m缓存创建 {{price}} / 1M tokens": "5m缓存创建 {{price}} / 1M tokens", + "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})", + "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", + "5m缓存创建价格 {{symbol}}{{price}} / 1M tokens": "5m缓存创建价格 {{symbol}}{{price}} / 1M tokens", "5m缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m缓存创建倍率: {{cacheCreationRatio5m}})": "5m缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m缓存创建倍率: {{cacheCreationRatio5m}})", + "5m缓存创建价格:{{symbol}}{{price}} / 1M tokens": "5m缓存创建价格:{{symbol}}{{price}} / 1M tokens", "8 - 高": "8 - 高", "AGPL v3.0协议": "AGPL v3.0协议", "AI 对话": "AI 对话", "AI模型测试环境": "AI模型测试环境", "AI模型配置": "AI模型配置", "AK/SK 模式:使用 AccessKey 和 SecretAccessKey;API Key 模式:使用 API Key": "AK/SK 模式:使用 AccessKey 和 SecretAccessKey;API Key 模式:使用 API Key", + "anthropic-beta JSON 示例": "anthropic-beta JSON 示例", "API Key": "API Key", "API Key 模式下不支持批量创建": "API Key 模式下不支持批量创建", "API Key 验证失败": "API Key 验证失败", "API Key 验证成功!连接到 io.net 服务正常": "API Key 验证成功!连接到 io.net 服务正常", "API 地址和相关配置": "API 地址和相关配置", "API 密钥": "API 密钥", + "API 密钥 (沙盒)": "API 密钥 (沙盒)", + "API 密钥 (生产)": "API 密钥 (生产)", "API 文档": "API 文档", "API 配置": "API 配置", "API令牌管理": "API令牌管理", @@ -64,20 +88,34 @@ "Bark推送URL": "Bark推送URL", "Bark推送URL必须以http://或https://开头": "Bark推送URL必须以http://或https://开头", "Bark通知": "Bark通知", + "Basic Auth 头": "Basic Auth 头", + "Cached tokens": "Cached tokens", + "Cached tokens 占比口径由后端返回:Claude 语义按 cached/(prompt+cached),其余按 cached/prompt。": "Cached tokens 占比口径由后端返回:Claude 语义按 cached/(prompt+cached),其余按 cached/prompt。", "Changing batch type to:": "Changing batch type to:", + "ChatCompletions→Responses 兼容配置": "ChatCompletions→Responses 兼容配置", + "ChatCompletions→Responses 兼容配置(Beta)": "ChatCompletions→Responses 兼容配置(Beta)", + "Claude 强制 beta=true": "Claude 强制 beta=true", + "Claude会在原有请求头基础上追加这些值,不会覆盖已有同名请求头;重复值会自动忽略。": "Claude会在原有请求头基础上追加这些值,不会覆盖已有同名请求头;重复值会自动忽略。", "Claude思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Claude思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比", "Claude设置": "Claude设置", "Claude请求头覆盖": "Claude请求头覆盖", "Claude请求头追加": "Claude请求头追加", - "Claude会在原有请求头基础上追加这些值,不会覆盖已有同名请求头;重复值会自动忽略。": "Claude会在原有请求头基础上追加这些值,不会覆盖已有同名请求头;重复值会自动忽略。", "Client ID": "Client ID", "Client Secret": "Client Secret", + "Codex 授权": "Codex 授权", + "Codex 渠道不支持批量创建": "Codex 渠道不支持批量创建", "common.changeLanguage": "common.changeLanguage", + "Completion tokens": "Completion tokens", + "Configuration": "Configuration", + "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。": "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。", + "CPU 使用率超过此值时拒绝请求": "CPU 使用率超过此值时拒绝请求", + "CPU 阈值 (%)": "CPU 阈值 (%)", "Creem API 密钥,敏感信息不显示": "Creem API 密钥,敏感信息不显示", "Creem Setting Tips": "Creem 只支持预设的固定金额产品,这产品以及价格需要提前在Creem网站内创建配置,所以不支持自定义动态金额充值。在Creem端配置产品的名字以及价格,获取Product Id 后填到下面的产品,在new-api为该产品设置充值额度,以及展示价格。", "Creem 介绍": "Creem 是一个简单的支付处理平台,支持固定金额产品销售,以及订阅销售。", "Creem 充值": "Creem 充值", "Creem 设置": "Creem 设置", + "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。": "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。", "default为默认设置,可单独设置每个分类的安全等级": "default为默认设置,可单独设置每个分类的安全等级", "default为默认设置,可单独设置每个模型的版本": "default为默认设置,可单独设置每个模型的版本", "Dify渠道只适配chatflow和agent,并且agent不支持图片!": "Dify渠道只适配chatflow和agent,并且agent不支持图片!", @@ -85,8 +123,14 @@ "Discord Client ID": "Discord Client ID", "Discord Client Secret": "Discord Client Secret", "Discord ID": "Discord ID", + "Discovery claims": "Discovery claims", + "Discovery scopes": "Discovery scopes", + "Discovery 建议 scopes:": "Discovery 建议 scopes:", "EUR (欧元)": "EUR (欧元)", "false": "false", + "GC 已执行": "GC 已执行", + "GC 执行失败": "GC 执行失败", + "GC 次数": "GC 次数", "Gemini安全设置": "Gemini安全设置", "Gemini思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Gemini思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比", "Gemini思考适配设置": "Gemini思考适配设置", @@ -96,26 +140,40 @@ "GitHub Client ID": "GitHub Client ID", "GitHub Client Secret": "GitHub Client Secret", "GitHub ID": "GitHub ID", + "Goroutine 数": "Goroutine 数", "Gotify应用令牌": "Gotify应用令牌", "Gotify服务器地址": "Gotify服务器地址", "Gotify服务器地址必须以http://或https://开头": "Gotify服务器地址必须以http://或https://开头", "Gotify通知": "Gotify通知", - "Grok设置": "Grok设置", "GPU/容器": "GPU/容器", "GPU数量": "GPU数量", + "Grok设置": "Grok设置", "Homepage URL 填": "Homepage URL 填", "ID": "ID", + "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护": "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护", + "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息", "IP": "IP", "IP白名单": "IP白名单", "IP白名单(支持CIDR表达式)": "IP白名单(支持CIDR表达式)", "IP限制": "IP限制", "IP黑名单": "IP黑名单", "JSON": "JSON", + "JSON 已格式化": "JSON 已格式化", + "JSON 文本": "JSON 文本", + "JSON 无效": "JSON 无效", + "JSON 格式参考": "JSON 格式参考", + "JSON 模式": "JSON 模式", "JSON 模式支持手动输入或上传服务账号 JSON": "JSON 模式支持手动输入或上传服务账号 JSON", "JSON格式密钥,请确保格式正确": "JSON格式密钥,请确保格式正确", "JSON格式错误": "JSON格式错误", "JSON编辑": "JSON编辑", "JSON解析错误:": "JSON解析错误:", + "Key": "Key", + "Key 或 Path": "Key 或 Path", + "Key 指纹": "Key 指纹", + "Key 摘要": "Key 摘要", + "Key 来源": "Key 来源", + "Key 来源类型": "Key 来源类型", "Linux DO Client ID": "Linux DO Client ID", "Linux DO Client Secret": "Linux DO Client Secret", "LinuxDO": "LinuxDO", @@ -124,6 +182,12 @@ "Midjourney 任务记录": "Midjourney 任务记录", "MIT许可证": "MIT许可证", "New API项目仓库地址:": "New API项目仓库地址:", + "NewAPI 默认不会将入口请求的 User-Agent 透传到上游渠道;该条件仅用于识别访问本站点的客户端。": "NewAPI 默认不会将入口请求的 User-Agent 透传到上游渠道;该条件仅用于识别访问本站点的客户端。", + "OAuth Client ID": "OAuth Client ID", + "OAuth Client Secret": "OAuth Client Secret", + "OAuth 登录失败:": "OAuth 登录失败:", + "OAuth 端点": "OAuth 端点", + "OAuth 配置错误:授权端点必须是完整的 URL(以 http:// 或 https:// 开头)": "OAuth 配置错误:授权端点必须是完整的 URL(以 http:// 或 https:// 开头)", "OIDC": "OIDC", "OIDC ID": "OIDC ID", "Ollama 模型管理": "Ollama 模型管理", @@ -135,41 +199,70 @@ "Passkey 注册失败,请重试": "Passkey 注册失败,请重试", "Passkey 注册成功": "Passkey 注册成功", "Passkey 登录": "Passkey 登录", + "Pay Method Name": "Pay Method Name", + "Pay Method Type": "Pay Method Type", "Ping间隔(秒)": "Ping间隔(秒)", + "POST 参数": "POST 参数", "price_xxx 的商品价格 ID,新建产品后可获得": "price_xxx 的商品价格 ID,新建产品后可获得", + "Prompt cache hit tokens": "Prompt cache hit tokens", + "Prompt tokens": "Prompt tokens", "Reasoning Effort": "Reasoning Effort", + "Request ID": "Request ID", + "RSA 私钥 (沙盒)": "RSA 私钥 (沙盒)", + "RSA 私钥 (生产)": "RSA 私钥 (生产)", "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私", + "Scopes(可选)": "Scopes(可选)", "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用", - "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式", "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示", "SMTP 发送者邮箱": "SMTP 发送者邮箱", "SMTP 服务器地址": "SMTP 服务器地址", "SMTP 端口": "SMTP 端口", "SMTP 访问凭证": "SMTP 访问凭证", "SMTP 账户": "SMTP 账户", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式", "SSE 事件": "SSE 事件", "SSE数据流": "SSE数据流", "SSRF防护开关详细说明": "总开关控制是否启用SSRF防护功能。关闭后将跳过所有SSRF检查,允许访问任意URL。⚠️ 仅在完全信任环境中关闭此功能。", "SSRF防护设置": "SSRF防护设置", "SSRF防护详细说明": "SSRF防护可防止恶意用户利用您的服务器访问内网资源。您可以配置受信任域名/IP的白名单,并限制允许的端口。适用于文件下载、Webhook回调和通知功能。", + "standard 已被移除,vip 用户看不到": "standard 已被移除,vip 用户看不到", "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用", - "免责声明:仅限个人使用,请勿分发或共享任何凭证。该渠道存在前置条件与使用门槛,请在充分了解流程与风险后使用,并遵守 OpenAI 的相关条款与政策。相关凭证与配置仅限接入 Codex CLI 使用,不适用于其他客户端、平台或渠道。": "免责声明:仅限个人使用,请勿分发或共享任何凭证。该渠道存在前置条件与使用门槛,请在充分了解流程与风险后使用,并遵守 OpenAI 的相关条款与政策。相关凭证与配置仅限接入 Codex CLI 使用,不适用于其他客户端、平台或渠道。", "Stripe 设置": "Stripe 设置", + "Stripe/Creem 商品ID(可选)": "Stripe/Creem 商品ID(可选)", + "Stripe/Creem 需在第三方平台创建商品并填入 ID": "Stripe/Creem 需在第三方平台创建商品并填入 ID", "Telegram": "Telegram", "Telegram Bot Token": "Telegram Bot Token", "Telegram Bot 名称": "Telegram Bot 名称", "Telegram ID": "Telegram ID", "Token Endpoint": "Token Endpoint", + "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。": "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。", + "Total tokens": "Total tokens", "true": "true", + "TTL(秒,0 表示默认)": "TTL(秒,0 表示默认)", + "TTL(秒)": "TTL(秒)", "Turnstile Secret Key": "Turnstile Secret Key", "Turnstile Site Key": "Turnstile Site Key", "Unix时间戳": "Unix时间戳", "Uptime Kuma地址": "Uptime Kuma地址", "Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)": "Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)", + "URL 标识,只能包含小写字母、数字和连字符": "URL 标识,只能包含小写字母、数字和连字符", "URL链接": "URL链接", "USD (美元)": "USD (美元)", "User Info Endpoint": "User Info Endpoint", + "User-Agent include(每行一个,可不写)": "User-Agent include(每行一个,可不写)", + "Value 正则": "Value 正则", "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段", + "Waffo API 参数,可空,例如:CREDITCARD,DEBITCARD(最多64位)": "Waffo API 参数,可空,例如:CREDITCARD,DEBITCARD(最多64位)", + "Waffo API 参数,可空(最多64位)": "Waffo API 参数,可空(最多64位)", + "Waffo 充值": "Waffo 充值", + "Waffo 充值的最低数量,默认 1": "Waffo 充值的最低数量,默认 1", + "Waffo 公钥 (沙盒)": "Waffo 公钥 (沙盒)", + "Waffo 公钥 (生产)": "Waffo 公钥 (生产)", + "Waffo 商户 ID": "Waffo 商户 ID", + "Waffo 是一个支付聚合平台,支持多种支付方式。": "Waffo 是一个支付聚合平台,支持多种支付方式。", + "Waffo 设置": "Waffo 设置", + "Web 搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Web 搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", + "Web 搜索调用 {{webSearchCallCount}} 次": "Web 搜索调用 {{webSearchCallCount}} 次", "Webhook 密钥": "Webhook 密钥", "Webhook 签名密钥": "Webhook 签名密钥", "Webhook地址": "Webhook地址", @@ -191,25 +284,44 @@ "一行一个,不区分大小写": "一行一个,不区分大小写", "一行一个屏蔽词,不需要符号分割": "一行一个屏蔽词,不需要符号分割", "一键填充到 FluentRead": "一键填充到 FluentRead", + "三种操作的区别:": "三种操作的区别:", "上一个表单块": "上一个表单块", "上一步": "上一步", + "上传 PNG/JPG/SVG 图片,建议尺寸 ≤ 128×128px": "上传 PNG/JPG/SVG 图片,建议尺寸 ≤ 128×128px", + "上传图片": "上传图片", "上次保存: ": "上次保存: ", + "上次检测到可加入模型": "上次检测到可加入模型", + "上次检测时间": "上次检测时间", "上游倍率同步": "上游倍率同步", + "上游模型管理": "上游模型管理", "上游返回": "上游返回", "下一个表单块": "下一个表单块", + "下一次重置": "下一次重置", "下一步": "下一步", "下午好": "下午好", "下载日志": "下载日志", + "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。", + "下面是带注释的示例,仅用于参考;实际保存时请删除注释。": "下面是带注释的示例,仅用于参考;实际保存时请删除注释。", + "不会出现": "不会出现", "不再提醒": "不再提醒", + "不升级": "不升级", + "不变": "不变", "不同用户分组的价格信息": "不同用户分组的价格信息", "不填则为模型列表第一个": "不填则为模型列表第一个", "不建议使用": "不建议使用", "不支持": "不支持", "不是合法的 JSON 字符串": "不是合法的 JSON 字符串", "不更改": "不更改", + "不配置特殊倍率时:": "不配置特殊倍率时:", + "不配置规则时,所有用户看到的下拉框一样:": "不配置规则时,所有用户看到的下拉框一样:", + "不重置": "不重置", + "不重试": "不重试", + "不限": "不限", "不限制": "不限制", "与本地相同": "与本地相同", "专属倍率": "专属倍率", + "专属分组": "专属分组", + "两个分组都勾选了「用户可选」,所以用户创建令牌时可以看到这两个选项:": "两个分组都勾选了「用户可选」,所以用户创建令牌时可以看到这两个选项:", "两次输入的密码不一致": "两次输入的密码不一致", "两次输入的密码不一致!": "两次输入的密码不一致!", "两步验证": "两步验证", @@ -223,13 +335,18 @@ "个人中心区域": "个人中心区域", "个人信息设置": "个人信息设置", "个人设置": "个人设置", + "个字段": "个字段", "个实例": "个实例", + "个已过期": "个已过期", "个性化设置": "个性化设置", "个性化设置左侧边栏的显示内容": "个性化设置左侧边栏的显示内容", + "个月": "个月", "个未配置模型": "个未配置模型", "个模型": "个模型", + "个生效中": "个生效中", "个部署吗?此操作不可逆。": "个部署吗?此操作不可逆。", "中午好": "中午好", + "为 vip 用户配置规则:": "为 vip 用户配置规则:", "为一个 JSON 对象,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}": "为一个 JSON 对象,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}", "为一个 JSON 数组,例如:[10, 20, 50, 100, 200, 500]": "为一个 JSON 数组,例如:[10, 20, 50, 100, 200, 500]", "为一个 JSON 文本": "为一个 JSON 文本", @@ -244,6 +361,8 @@ "为一个 JSON 文本,键为组名称,值为倍率": "为一个 JSON 文本,键为组名称,值为倍率", "为了保护账户安全,请验证您的两步验证码。": "为了保护账户安全,请验证您的两步验证码。", "为了保护账户安全,请验证您的身份。": "为了保护账户安全,请验证您的身份。", + "为保证匹配准确,请确保客户端直连本站点(避免反向代理/网关改写 User-Agent)。": "为保证匹配准确,请确保客户端直连本站点(避免反向代理/网关改写 User-Agent)。", + "为特定用户分组配置可用分组的增减规则。「添加」为该分组新增可用分组,「移除」移除默认可用分组,「追加」直接追加分组": "为特定用户分组配置可用分组的增减规则。「添加」为该分组新增可用分组,「移除」移除默认可用分组,「追加」直接追加分组", "为空则默认使用服务器地址,多个 Origin 用逗号分隔,例如 https://newapi.pro,https://newapi.com ,注意不能携带[],需使用https": "为空则默认使用服务器地址,多个 Origin 用逗号分隔,例如 https://newapi.pro,https://newapi.com ,注意不能携带[],需使用https", "主页链接填": "主页链接填", "之前的所有日志": "之前的所有日志", @@ -253,6 +372,8 @@ "产品名称": "产品名称", "产品配置": "产品配置", "产品配置错误,请联系管理员": "产品配置错误,请联系管理员", + "享受 8 折": "享受 8 折", + "什么是分组?": "什么是分组?", "仅为使用OpenAI格式的Gemini/Vertex渠道填充thoughtSignature": "仅为使用OpenAI格式的Gemini/Vertex渠道填充thoughtSignature", "仅会覆盖你勾选的字段,未勾选的字段保持本地不变。": "仅会覆盖你勾选的字段,未勾选的字段保持本地不变。", "仅供参考,以实际扣费为准": "仅供参考,以实际扣费为准", @@ -260,29 +381,47 @@ "仅修改展示粒度,统计精确到小时": "仅修改展示粒度,统计精确到小时", "仅密钥": "仅密钥", "仅对自定义模型有效": "仅对自定义模型有效", + "仅当前层": "仅当前层", "仅当自动禁用开启时有效,关闭后不会自动禁用该渠道": "仅当自动禁用开启时有效,关闭后不会自动禁用该渠道", + "仅提交已勾选": "仅提交已勾选", "仅支持": "仅支持", + "仅支持 JSON 对象,必须包含 access_token 与 account_id": "仅支持 JSON 对象,必须包含 access_token 与 account_id", "仅支持 JSON 文件": "仅支持 JSON 文件", "仅支持 JSON 文件,支持多文件": "仅支持 JSON 文件,支持多文件", "仅支持 OpenAI 接口格式": "仅支持 OpenAI 接口格式", + "仅显示已绑定": "仅显示已绑定", "仅显示矛盾倍率": "仅显示矛盾倍率", + "仅检测上游模型更新": "仅检测上游模型更新", "仅用于开发环境,生产环境应使用 HTTPS": "仅用于开发环境,生产环境应使用 HTTPS", + "仅用订阅": "仅用订阅", + "仅用钱包": "仅用钱包", + "仅管理员可用。开启后,当系统定时检测全部渠道发现上游模型变更或检测异常时,将按你选择的通知方式发送汇总通知;渠道或模型过多时会自动省略部分明细。": "仅管理员可用。开启后,当系统定时检测全部渠道发现上游模型变更或检测异常时,将按你选择的通知方式发送汇总通知;渠道或模型过多时会自动省略部分明细。", "仅重置配置": "仅重置配置", "今日关闭": "今日关闭", + "今日已签到": "今日已签到", + "今日已签到,累计签到": "今日已签到,累计签到", + "仍有未处理项": "仍有未处理项", + "从 0.5 降到 0.3": "从 0.5 降到 0.3", + "从剪贴板粘贴配置": "从剪贴板粘贴配置", "从官方模型库同步": "从官方模型库同步", "从认证器应用中获取验证码,或使用备用码": "从认证器应用中获取验证码,或使用备用码", "从配置文件同步": "从配置文件同步", + "从默认列表中去掉一个分组": "从默认列表中去掉一个分组", "代理地址": "代理地址", "代理设置": "代理设置", "代码已复制到剪贴板": "代码已复制到剪贴板", "令牌": "令牌", + "令牌不存在": "令牌不存在", "令牌分组": "令牌分组", "令牌分组,默认为用户的分组": "令牌分组,默认为用户的分组", + "令牌分组设为 auto 时,按以下顺序依次尝试选择可用分组,排在前面的优先级更高": "令牌分组设为 auto 时,按以下顺序依次尝试选择可用分组,排在前面的优先级更高", + "令牌分组设为 auto 时,系统按优先级顺序自动选择一个可用分组。": "令牌分组设为 auto 时,系统按优先级顺序自动选择一个可用分组。", "令牌创建成功,请在列表页面点击复制获取令牌!": "令牌创建成功,请在列表页面点击复制获取令牌!", "令牌名称": "令牌名称", "令牌已重置并已复制到剪贴板": "令牌已重置并已复制到剪贴板", "令牌更新成功!": "令牌更新成功!", "令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制": "令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制", + "令牌端点": "令牌端点", "令牌管理": "令牌管理", "以下上游数据可能不可信:": "以下上游数据可能不可信:", "以下文件解析失败,已忽略:{{list}}": "以下文件解析失败,已忽略:{{list}}", @@ -291,7 +430,10 @@ "价格": "价格", "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}", "价格:${{price}} * {{ratioType}}:{{ratio}}": "价格:${{price}} * {{ratioType}}:{{ratio}}", + "价格摘要": "价格摘要", "价格暂时不可用,请稍后重试": "价格暂时不可用,请稍后重试", + "价格模式": "价格模式", + "价格模式(默认)": "价格模式(默认)", "价格计算中...": "价格计算中...", "价格计算失败": "价格计算失败", "价格计算失败: ": "价格计算失败: ", @@ -299,18 +441,31 @@ "价格设置方式": "价格设置方式", "价格重新计算中...": "价格重新计算中...", "价格预估": "价格预估", + "任一满足(OR)": "任一满足(OR)", "任务 ID": "任务 ID", + "任务ID": "任务ID", "任务日志": "任务日志", "任务状态": "任务状态", "任务记录": "任务记录", "企业账户为特殊返回格式,需要特殊处理,如果非企业账户,请勿勾选": "企业账户为特殊返回格式,需要特殊处理,如果非企业账户,请勿勾选", "优先级": "优先级", + "优先订阅": "优先订阅", + "优先钱包": "优先钱包", "优惠": "优惠", "低于此额度时将发送邮件提醒用户": "低于此额度时将发送邮件提醒用户", "余额": "余额", "余额充值管理": "余额充值管理", + "作废": "作废", + "作废于": "作废于", + "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "作废后该订阅将立即失效,历史记录不受影响。是否继续?", + "作用域": "作用域", + "作用域:包含分组": "作用域:包含分组", + "作用域:包含模型名称": "作用域:包含模型名称", + "作用域:包含规则名称": "作用域:包含规则名称", "你似乎并没有修改什么": "你似乎并没有修改什么", "你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。", + "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?_other": "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?", + "使用 {{name}} 继续": "使用 {{name}} 继续", "使用 Discord 继续": "使用 Discord 继续", "使用 GitHub 继续": "使用 GitHub 继续", "使用 JSON 对象格式,格式为:{\"组名\": [最多请求次数, 最多请求完成次数]}": "使用 JSON 对象格式,格式为:{\"组名\": [最多请求次数, 最多请求完成次数]}", @@ -323,13 +478,20 @@ "使用 用户名 注册": "使用 用户名 注册", "使用 邮箱或用户名 登录": "使用 邮箱或用户名 登录", "使用ID排序": "使用ID排序", + "使用分组": "使用分组", + "使用原生额度输入": "使用原生额度输入", "使用日志": "使用日志", "使用模式": "使用模式", "使用统计": "使用统计", "使用认证器应用(如 Google Authenticator、Microsoft Authenticator)扫描下方二维码:": "使用认证器应用(如 Google Authenticator、Microsoft Authenticator)扫描下方二维码:", "使用认证器应用扫描二维码": "使用认证器应用扫描二维码", + "使用说明": "使用说明", + "例如": "例如", + "例如 /var/cache/new-api": "例如 /var/cache/new-api", "例如 €, £, Rp, ₩, ₹...": "例如 €, £, Rp, ₩, ₹...", "例如 https://docs.newapi.pro": "例如 https://docs.newapi.pro", + "例如 https://example.com/api/waffo/webhook": "例如 https://example.com/api/waffo/webhook", + "例如 https://example.com/console/topup": "例如 https://example.com/console/topup", "例如:": "例如:", "例如: /bin/bash -c \"python app.py\"": "例如: /bin/bash -c \"python app.py\"", "例如: nginx:latest": "例如: nginx:latest", @@ -342,13 +504,31 @@ "例如:2,就是最低充值2$": "例如:2,就是最低充值2$", "例如:2000": "例如:2000", "例如:4.99": "例如:4.99", + "例如:401, 403, 429, 500-599": "例如:401,403,429,500-599", "例如:7,就是7元/美金": "例如:7,就是7元/美金", + "例如:Credit Card": "例如:Credit Card", + "例如:email": "例如:email", "例如:example.com": "例如:example.com", + "例如:github / si:google / https://example.com/logo.png / 🐱": "例如:github / si:google / https://example.com/logo.png / 🐱", + "例如:GitHub Enterprise": "例如:GitHub Enterprise", + "例如:github-enterprise": "例如:github-enterprise", + "例如:gpt-4.1-nano,regex:^claude-.*$,regex:^sora-.*$": "例如:gpt-4.1-nano,regex:^claude-.*$,regex:^sora-.*$", + "例如:https://example.com/.well-known/openid-configuration": "例如:https://example.com/.well-known/openid-configuration", + "例如:https://gitea.example.com": "例如:https://gitea.example.com", "例如:https://yourdomain.com": "例如:https://yourdomain.com", + "例如:name、full_name": "例如:name、full_name", "例如:nginx:latest": "例如:nginx:latest", + "例如:preferred_username、login": "例如:preferred_username、login", "例如:preview": "例如:preview", "例如:prod_6I8rBerHpPxyoiU9WK4kot": "例如:prod_6I8rBerHpPxyoiU9WK4kot", + "例如:sub、id、data.user.id": "例如:sub、id、data.user.id", "例如:基础套餐": "例如:基础套餐", + "例如:清理工具参数,避免上游校验错误": "例如:清理工具参数,避免上游校验错误", + "例如:该请求不满足准入策略": "例如:该请求不满足准入策略", + "例如:适合轻度使用": "例如:适合轻度使用", + "例如:需要等级 {{required}},你当前等级 {{current}}": "例如:需要等级 {{required}},你当前等级 {{current}}", + "例如(全渠道):": "例如(全渠道):", + "例如(指定渠道):": "例如(指定渠道):", "例如发卡网站的购买链接": "例如发卡网站的购买链接", "供应商": "供应商", "供应商介绍": "供应商介绍", @@ -360,6 +540,8 @@ "供应商更新成功!": "供应商更新成功!", "侧边栏管理(全局控制)": "侧边栏管理(全局控制)", "侧边栏设置保存成功": "侧边栏设置保存成功", + "保前缀": "保前缀", + "保后缀": "保后缀", "保存": "保存", "保存 Discord OAuth 设置": "保存 Discord OAuth 设置", "保存 GitHub OAuth 设置": "保存 GitHub OAuth 设置", @@ -371,28 +553,34 @@ "保存 Turnstile 设置": "保存 Turnstile 设置", "保存 WeChat Server 设置": "保存 WeChat Server 设置", "保存分组倍率设置": "保存分组倍率设置", + "保存分组相关设置": "保存分组相关设置", "保存备用码": "保存备用码", "保存备用码以备不时之需": "保存备用码以备不时之需", "保存失败": "保存失败", "保存失败,请重试": "保存失败,请重试", "保存失败:": "保存失败:", "保存屏蔽词过滤设置": "保存屏蔽词过滤设置", + "保存性能设置": "保存性能设置", "保存成功": "保存成功", "保存数据看板设置": "保存数据看板设置", "保存日志设置": "保存日志设置", "保存模型倍率设置": "保存模型倍率设置", "保存模型速率限制": "保存模型速率限制", "保存监控设置": "保存监控设置", + "保存签到设置": "保存签到设置", "保存绘图设置": "保存绘图设置", "保存聊天设置": "保存聊天设置", "保存设置": "保存设置", "保存通用设置": "保存通用设置", "保存邮箱域名白名单设置": "保存邮箱域名白名单设置", + "保存预览": "保存预览", "保存额度设置": "保存额度设置", - "保留天数": "保留天数", - "保留文件数": "保留文件数", + "保留": "保留", "保留最近N个文件": "保留最近N个文件", "保留最近N天": "保留最近N天", + "保留原值(目标已有值时不覆盖)": "保留原值(目标已有值时不覆盖)", + "保留天数": "保留天数", + "保留文件数": "保留文件数", "修复数据库一致性": "修复数据库一致性", "修改为": "修改为", "修改子渠道优先级": "修改子渠道优先级", @@ -404,14 +592,21 @@ "倍率信息": "倍率信息", "倍率是为了方便换算不同价格的模型": "倍率是为了方便换算不同价格的模型", "倍率模式": "倍率模式", + "倍率模式(默认)": "倍率模式(默认)", + "倍率用于计费乘数,勾选「用户可选」后用户可在创建令牌时选择该分组": "倍率用于计费乘数,勾选「用户可选」后用户可在创建令牌时选择该分组", "倍率类型": "倍率类型", + "假设再加两个分组 default 和 vip,但不勾选用户可选:": "假设再加两个分组 default 和 vip,但不勾选用户可选:", + "偏好设置": "偏好设置", "停止测试": "停止测试", + "停止重试": "停止重试", "停用": "停用", "允许 AccountFilter 参数": "允许 AccountFilter 参数", "允许 HTTP 协议图片请求(适用于自部署代理)": "允许 HTTP 协议图片请求(适用于自部署代理)", + "允许 inference_geo 透传": "允许 inference_geo 透传", "允许 safety_identifier 透传": "允许 safety_identifier 透传", "允许 service_tier 透传": "允许 service_tier 透传", "允许 speed 透传": "允许 speed 透传", + "允许 stream_options.include_obfuscation 透传": "允许 stream_options.include_obfuscation 透传", "允许 Turnstile 用户校验": "允许 Turnstile 用户校验", "允许不安全的 Origin(HTTP)": "允许不安全的 Origin(HTTP)", "允许回调(会泄露服务器 IP 地址)": "允许回调(会泄露服务器 IP 地址)", @@ -430,6 +625,7 @@ "允许通过密码进行注册": "允许通过密码进行注册", "允许通过密码进行登录": "允许通过密码进行登录", "允许通过微信登录 & 注册": "允许通过微信登录 & 注册", + "允许重试": "允许重试", "元": "元", "充值": "充值", "充值价格(x元/美金)": "充值价格(x元/美金)", @@ -447,12 +643,12 @@ "充值金额折扣配置不是合法的 JSON 对象": "充值金额折扣配置不是合法的 JSON 对象", "充值链接": "充值链接", "充值额度": "充值额度", + "先填写配置,再自动填充 OAuth 端点,能显著减少手工输入": "先填写配置,再自动填充 OAuth 端点,能显著减少手工输入", + "先搜索,再一键复制字段名或填入当前规则。字段名为系统内部路径,可直接用于路径 / 来源 / 目标。": "先搜索,再一键复制字段名或填入当前规则。字段名为系统内部路径,可直接用于路径 / 来源 / 目标。", + "免责声明:仅限个人使用,请勿分发或共享任何凭证。该渠道存在前置条件与使用门槛,请在充分了解流程与风险后使用,并遵守 OpenAI 的相关条款与政策。相关凭证与配置仅限接入 Codex CLI 使用,不适用于其他客户端、平台或渠道。": "免责声明:仅限个人使用,请勿分发或共享任何凭证。该渠道存在前置条件与使用门槛,请在充分了解流程与风险后使用,并遵守 OpenAI 的相关条款与政策。相关凭证与配置仅限接入 Codex CLI 使用,不适用于其他客户端、平台或渠道。", "兑换人ID": "兑换人ID", "兑换成功!": "兑换成功!", "兑换码充值": "兑换码充值", - "确认清理不活跃的磁盘缓存?": "确认清理不活跃的磁盘缓存?", - "这将删除超过 10 分钟未使用的临时缓存文件": "这将删除超过 10 分钟未使用的临时缓存文件", - "清理不活跃缓存": "清理不活跃缓存", "兑换码创建成功": "兑换码创建成功", "兑换码创建成功,是否下载兑换码?": "兑换码创建成功,是否下载兑换码?", "兑换码创建成功!": "兑换码创建成功!", @@ -464,15 +660,18 @@ "全局控制侧边栏区域和功能显示,管理员隐藏的功能用户无法启用": "全局控制侧边栏区域和功能显示,管理员隐藏的功能用户无法启用", "全局设置": "全局设置", "全选": "全选", + "全选当前列表模型": "全选当前列表模型", "全部": "全部", "全部供应商": "全部供应商", "全部分组": "全部分组", "全部地区总可用资源": "全部地区总可用资源", + "全部填入": "全部填入", "全部容器": "全部容器", "全部展开": "全部展开", "全部收起": "全部收起", "全部标签": "全部标签", "全部模型": "全部模型", + "全部满足(AND)": "全部满足(AND)", "全部状态": "全部状态", "全部硬件总可用资源": "全部硬件总可用资源", "全部端点": "全部端点", @@ -498,37 +697,63 @@ "关闭侧边栏": "关闭侧边栏", "关闭公告": "关闭公告", "关闭后,此模型将不会被“同步官方”自动覆盖或创建": "关闭后,此模型将不会被“同步官方”自动覆盖或创建", + "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?", "关闭弹窗,已停止批量测试": "关闭弹窗,已停止批量测试", + "关闭提示": "关闭提示", "其他": "其他", "其他注册选项": "其他注册选项", "其他登录选项": "其他登录选项", "其他设置": "其他设置", "其他详情": "其他详情", + "内存 阈值 (%)": "内存 阈值 (%)", + "内存使用率超过此值时拒绝请求": "内存使用率超过此值时拒绝请求", + "内存命中": "内存命中", + "内存缓存最大条目数。0 表示使用后端默认容量:100000。": "内存缓存最大条目数。0 表示使用后端默认容量:100000。", "内容": "内容", "内容较大,已启用性能优化模式": "内容较大,已启用性能优化模式", "内容较大,部分功能可能受限": "内容较大,部分功能可能受限", + "内置": "内置", "内置 Ollama 镜像": "内置 Ollama 镜像", "再次输入部署名称": "再次输入部署名称", "最低": "最低", + "最低充值数量": "最低充值数量", "最低充值美元数量": "最低充值美元数量", "最后使用时间": "最后使用时间", "最后更新": "最后更新", "最后请求": "最后请求", "最大GPU数量": "最大GPU数量", "最大可用": "最大可用", + "最大条目数": "最大条目数", + "最终抵扣": "最终抵扣", + "最近一次": "最近一次", "最近事件": "最近事件", + "最高优先级": "最高优先级", + "写": "写", + "准入策略": "准入策略", + "准入策略 JSON(可选)": "准入策略 JSON(可选)", "准备中...": "准备中...", "准备完成初始化": "准备完成初始化", + "减少": "减少", + "凭证已刷新": "凭证已刷新", "分类名称": "分类名称", "分组": "分组", + "分组JSON设置": "分组JSON设置", "分组与模型定价设置": "分组与模型定价设置", "分组价格": "分组价格", "分组倍率": "分组倍率", "分组倍率设置": "分组倍率设置", "分组倍率设置,可以在此处新增分组或修改现有分组的倍率,格式为 JSON 字符串,例如:{\"vip\": 0.5, \"test\": 1},表示 vip 分组的倍率为 0.5,test 分组的倍率为 1": "分组倍率设置,可以在此处新增分组或修改现有分组的倍率,格式为 JSON 字符串,例如:{\"vip\": 0.5, \"test\": 1},表示 vip 分组的倍率为 0.5,test 分组的倍率为 1", + "分组名": "分组名", + "分组名称": "分组名称", + "分组名称到倍率的映射": "分组名称到倍率的映射", + "分组描述": "分组描述", + "分组是用于控制计费倍率和模型访问权限的核心概念。每个用户属于一个分组,每个令牌也可以指定使用某个分组。": "分组是用于控制计费倍率和模型访问权限的核心概念。每个用户属于一个分组,每个令牌也可以指定使用某个分组。", "分组特殊倍率": "分组特殊倍率", "分组特殊可用分组": "分组特殊可用分组", + "分组相关设置": "分组相关设置", + "分组管理": "分组管理", "分组设置": "分组设置", + "分组设置使用说明": "分组设置使用说明", "分组速率配置优先级高于全局速率限制。": "分组速率配置优先级高于全局速率限制。", "分组速率限制": "分组速率限制", "分钟": "分钟", @@ -543,41 +768,61 @@ "列出的模型将不会自动添加或移除-thinking/-nothinking 后缀": "列出的模型将不会自动添加或移除-thinking/-nothinking 后缀", "列设置": "列设置", "创建": "创建", + "创建令牌可选": "创建令牌可选", "创建令牌默认选择auto分组,初始令牌也将设为auto(否则留空,为用户默认分组)": "创建令牌默认选择auto分组,初始令牌也将设为auto(否则留空,为用户默认分组)", + "创建和管理分组": "创建和管理分组", "创建失败": "创建失败", + "创建成功": "创建成功", "创建或选择密钥时,将 Project 设置为 io.cloud": "创建或选择密钥时,将 Project 设置为 io.cloud", "创建新用户账户": "创建新用户账户", "创建新的令牌": "创建新的令牌", "创建新的兑换码": "创建新的兑换码", "创建新的模型": "创建新的模型", "创建新的渠道": "创建新的渠道", + "创建新的订阅套餐": "创建新的订阅套餐", "创建新的预填组": "创建新的预填组", "创建时间": "创建时间", + "创建渠道所需的基本信息": "创建渠道所需的基本信息", "创建用户": "创建用户", "初始化失败,请重试": "初始化失败,请重试", "初始化系统": "初始化系统", + "删请求头": "删请求头", "删除": "删除", + "删除 Key 来源": "删除 Key 来源", + "删除会彻底移除该订阅记录(含权益明细)。是否继续?": "删除会彻底移除该订阅记录(含权益明细)。是否继续?", "删除后无法恢复,确定要删除模型 \"{{name}}\" 吗?": "删除后无法恢复,确定要删除模型 \"{{name}}\" 吗?", "删除失败": "删除失败", "删除密钥失败": "删除密钥失败", + "删除已选 {{selected}} / {{total}}": "删除已选 {{selected}} / {{total}}", "删除成功": "删除成功", "删除所选": "删除所选", "删除所选令牌": "删除所选令牌", "删除所选通道": "删除所选通道", + "删除条件": "删除条件", + "删除模型": "删除模型", "删除禁用密钥失败": "删除禁用密钥失败", "删除禁用通道": "删除禁用通道", "删除自动禁用密钥": "删除自动禁用密钥", + "删除规则": "删除规则", "删除账户": "删除账户", "删除账户确认": "删除账户确认", "删除部署失败": "删除部署失败", "刷新": "刷新", + "刷新凭证": "刷新凭证", "刷新失败": "刷新失败", "刷新容器信息": "刷新容器信息", "刷新日志": "刷新日志", + "刷新统计": "刷新统计", + "刷新缓存统计": "刷新缓存统计", + "刷新缓存统计失败": "刷新缓存统计失败", + "刷新页面": "刷新页面", + "前:": "前:", "前往 io.net API Keys": "前往 io.net API Keys", "前往设置": "前往设置", "前往设置页面": "前往设置页面", "前缀": "前缀", + "前置": "前置", + "剪贴板中未检测到连接信息": "剪贴板中未检测到连接信息", "副本数量": "副本数量", "剩余": "剩余", "剩余备用码:": "剩余备用码:", @@ -605,69 +850,122 @@ "加载详情中...": "加载详情中...", "加载账单失败": "加载账单失败", "加载隐私政策内容失败...": "加载隐私政策内容失败...", + "勾选后,该分组会出现在用户创建令牌时的下拉菜单中。未勾选的分组只能由管理员分配,用户自己无法选择。": "勾选后,该分组会出现在用户创建令牌时的下拉菜单中。未勾选的分组只能由管理员分配,用户自己无法选择。", "包含": "包含", "包含来自未知或未标明供应商的AI模型,这些模型可能来自小型供应商或开源项目。": "包含来自未知或未标明供应商的AI模型,这些模型可能来自小型供应商或开源项目。", "包括失败请求的次数,0代表不限制": "包括失败请求的次数,0代表不限制", + "匹配值": "匹配值", + "匹配值(可选)": "匹配值(可选)", + "匹配方式": "匹配方式", "匹配类型": "匹配类型", "区域": "区域", + "升级分组": "升级分组", "单GPU小时费率": "单GPU小时费率", + "单价 (USD)": "单价 (USD)", "历史消耗": "历史消耗", "原价": "原价", + "原价,和普通用户一样": "原价,和普通用户一样", "原因:": "原因:", "原密码": "原密码", + "原生格式": "原生格式", + "原生额度": "原生额度", + "去前缀": "去前缀", + "去后缀": "去后缀", + "去处理{{type}}": "去处理{{type}}", + "去空格": "去空格", "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥", "参与官方同步": "参与官方同步", "参数": "参数", + "参数传递": "参数传递", "参数值": "参数值", "参数覆盖": "参数覆盖", + "参数覆盖 JSON 已复制": "参数覆盖 JSON 已复制", + "参数覆盖已复制": "参数覆盖已复制", + "参数覆盖必须是合法的 JSON 对象": "参数覆盖必须是合法的 JSON 对象", + "参数覆盖必须是合法的 JSON 格式!": "参数覆盖必须是合法的 JSON 格式!", + "参数覆盖模板": "参数覆盖模板", + "参数覆盖模板 JSON 格式不正确": "参数覆盖模板 JSON 格式不正确", + "参数覆盖模板预览": "参数覆盖模板预览", + "参数覆盖详情": "参数覆盖详情", + "参数配置": "参数配置", + "参数配置有误": "参数配置有误", + "参数错误": "参数错误", "参照生视频": "参照生视频", "友情链接": "友情链接", "发布日期": "发布日期", "发布时间": "发布时间", + "发现文档地址(Discovery URL,可选)": "发现文档地址(Discovery URL,可选)", + "发行者 URL(Issuer URL)": "发行者 URL(Issuer URL)", "取消": "取消", "取消全选": "取消全选", "取消选择": "取消选择", "变换": "变换", + "变更": "变更", "变焦": "变焦", "变量值": "变量值", "变量名": "变量名", "只包括请求成功的次数": "只包括请求成功的次数", "只支持HTTPS,系统将以POST方式发送通知,请确保地址可以接收POST请求": "只支持HTTPS,系统将以POST方式发送通知,请确保地址可以接收POST请求", "只有当用户设置开启IP记录时,才会进行请求和错误类型日志的IP记录": "只有当用户设置开启IP记录时,才会进行请求和错误类型日志的IP记录", + "只有配置了规则的组合才会覆盖,未配置的组合仍使用令牌分组的基础倍率。": "只有配置了规则的组合才会覆盖,未配置的组合仍使用令牌分组的基础倍率。", + "可以根据用户分组增减令牌可选的分组范围。例如 vip 用户额外开放 premium 分组,或移除某个分组的选择权。": "可以根据用户分组增减令牌可选的分组范围。例如 vip 用户额外开放 premium 分组,或移除某个分组的选择权。", + "可以根据用户分组设置不同的计费倍率。例如 vip 用户使用 standard 令牌时倍率从 1.0 降为 0.8。": "可以根据用户分组设置不同的计费倍率。例如 vip 用户使用 standard 令牌时倍率从 1.0 降为 0.8。", "可信": "可信", + "可勾选需要执行的变更:新增会加入渠道模型列表,删除会从渠道模型列表移除。": "可勾选需要执行的变更:新增会加入渠道模型列表,删除会从渠道模型列表移除。", "可在设置页面设置关于内容,支持 HTML & Markdown": "可在设置页面设置关于内容,支持 HTML & Markdown", + "可手动填写,多个 scope 用空格分隔": "可手动填写,多个 scope 用空格分隔", + "可用": "可用", "可用令牌分组": "可用令牌分组", "可用分组": "可用分组", + "可用变量:{{provider}} {{field}} {{op}} {{required}} {{current}} 以及 {{current.path}}": "可用变量:{{provider}} {{field}} {{op}} {{required}} {{current}} 以及 {{current.path}}", "可用数量": "可用数量", "可用模型": "可用模型", + "可用空间: {{free}} / 总空间: {{total}}": "可用空间: {{free}} / 总空间: {{total}}", "可用端点类型": "可用端点类型", "可用邀请额度": "可用邀请额度", + "可留空;留空时会尝试使用 Issuer URL + /.well-known/openid-configuration": "可留空;留空时会尝试使用 Issuer URL + /.well-known/openid-configuration", "可视化": "可视化", "可视化倍率设置": "可视化倍率设置", "可视化编辑": "可视化编辑", + "可空": "可空", "可选,公告的补充说明": "可选,公告的补充说明", "可选,用于复现结果": "可选,用于复现结果", + "可选:基于用户信息 JSON 做组合条件准入,条件不满足时返回自定义提示": "可选:基于用户信息 JSON 做组合条件准入,条件不满足时返回自定义提示", + "可选:用于自动生成端点或 Discovery URL": "可选:用于自动生成端点或 Discovery URL", + "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。": "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。", + "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。": "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。", + "可选。对请求路径进行匹配;不填表示匹配所有路径。": "可选。对请求路径进行匹配;不填表示匹配所有路径。", "可选值": "可选值", + "合计:{{total}}": "合计:{{total}}", + "合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}", "同时重置消息": "同时重置消息", "同步": "同步", "同步到渠道": "同步到渠道", "同步向导": "同步向导", "同步失败": "同步失败", + "同步字段": "同步字段", "同步成功": "同步成功", "同步接口": "同步接口", "同步渠道失败": "同步渠道失败", "同步渠道失败:缺少部署信息": "同步渠道失败:缺少部署信息", + "同步端点": "同步端点", "名称": "名称", "名称+密钥": "名称+密钥", "名称不能为空": "名称不能为空", "名称匹配类型": "名称匹配类型", + "后:": "后:", + "后端固定": "后端固定", + "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。", "后端请求失败": "后端请求失败", "后缀": "后缀", + "向右展开": "向右展开", + "向左展开": "向左展开", "否": "否", "启动": "启动", "启动参数 (Args)": "启动参数 (Args)", "启动命令": "启动命令", "启动命令 (Entrypoint)": "启动命令 (Entrypoint)", + "启动授权失败": "启动授权失败", "启动时间": "启动时间", "启动部署失败": "启动部署失败", "启动配置": "启动配置", @@ -676,52 +974,83 @@ "启用 io.net 部署开关": "启用 io.net 部署开关", "启用 io.net 部署时必须填写 API Key": "启用 io.net 部署时必须填写 API Key", "启用 Prompt 检查": "启用 Prompt 检查", + "启用 Waffo": "启用 Waffo", "启用2FA失败": "启用2FA失败", "启用Claude思考适配(-thinking后缀)": "启用Claude思考适配(-thinking后缀)", "启用FunctionCall思维签名填充": "启用FunctionCall思维签名填充", "启用Gemini思考后缀适配": "启用Gemini思考后缀适配", "启用Ping间隔": "启用Ping间隔", "启用SMTP SSL": "启用SMTP SSL", - "强制使用 AUTH LOGIN": "强制使用 AUTH LOGIN", "启用SSRF防护(推荐开启以保护服务器安全)": "启用SSRF防护(推荐开启以保护服务器安全)", + "启用供应商": "启用供应商", "启用全部": "启用全部", "启用后可接入 io.net GPU 资源": "启用后可接入 io.net GPU 资源", "启用后可添加图片URL进行多模态对话": "启用后可添加图片URL进行多模态对话", + "启用后套餐将在用户端展示。是否继续?": "启用后套餐将在用户端展示。是否继续?", + "启用后将优先复用上一次成功的渠道(粘滞选路)。": "启用后将优先复用上一次成功的渠道(粘滞选路)。", "启用后将使用 Creem Test Mode": "启用后将使用 Creem Test Mode", + "启用后将使用 Waffo 沙盒环境": "启用后将使用 Waffo 沙盒环境", "启用密钥失败": "启用密钥失败", "启用屏蔽词过滤功能": "启用屏蔽词过滤功能", + "启用性能监控": "启用性能监控", + "启用性能监控后,当系统资源使用率超过设定阈值时,将拒绝新的 Relay 请求 (/v1, /v1beta 等),以保护系统稳定性。": "启用性能监控后,当系统资源使用率超过设定阈值时,将拒绝新的 Relay 请求 (/v1, /v1beta 等),以保护系统稳定性。", "启用所有密钥失败": "启用所有密钥失败", "启用数据看板(实验性)": "启用数据看板(实验性)", "启用此模式后,将使用您自定义的请求体发送API请求,模型配置面板的参数设置将被忽略。": "启用此模式后,将使用您自定义的请求体发送API请求,模型配置面板的参数设置将被忽略。", + "启用状态": "启用状态", "启用用户模型请求速率限制(可能会影响高并发性能)": "启用用户模型请求速率限制(可能会影响高并发性能)", + "启用磁盘缓存": "启用磁盘缓存", + "启用磁盘缓存后,大请求体将临时存储到磁盘而非内存,可显著降低内存占用,适用于处理包含大量图片/文件的请求。建议在 SSD 环境下使用。": "启用磁盘缓存后,大请求体将临时存储到磁盘而非内存,可显著降低内存占用,适用于处理包含大量图片/文件的请求。建议在 SSD 环境下使用。", + "启用签到功能": "启用签到功能", "启用绘图功能": "启用绘图功能", "启用请求体透传功能": "启用请求体透传功能", "启用请求透传": "启用请求透传", + "启用违规扣费": "启用违规扣费", "启用额度消费日志记录": "启用额度消费日志记录", "启用验证": "启用验证", - "启用违规扣费": "启用违规扣费", "周": "周", + "命中判定:usage 中存在 cached tokens(例如 cached_tokens/prompt_cache_hit_tokens)即视为命中。": "命中判定:usage 中存在 cached tokens(例如 cached_tokens/prompt_cache_hit_tokens)即视为命中。", + "命中率": "命中率", + "命中该亲和规则后,会把此模板合并到渠道参数覆盖中(同名键由模板覆盖)。": "命中该亲和规则后,会把此模板合并到渠道参数覆盖中(同名键由模板覆盖)。", "和": "和", "和Claude不同,默认情况下Gemini的思考模型会自动决定要不要思考,就算不开启适配模型也可以正常使用,如果您需要计费,推荐设置无后缀模型价格按思考价格设置。支持使用 gemini-2.5-pro-preview-06-05-thinking-128 格式来精确传递思考预算。": "和Claude不同,默认情况下Gemini的思考模型会自动决定要不要思考,就算不开启适配模型也可以正常使用,如果您需要计费,推荐设置无后缀模型价格按思考价格设置。支持使用 gemini-2.5-pro-preview-06-05-thinking-128 格式来精确传递思考预算。", "响应": "响应", "响应时间": "响应时间", + "响应缺少凭据": "响应缺少凭据", + "响应缺少授权链接": "响应缺少授权链接", "商品价格 ID": "商品价格 ID", + "商户 ID": "商户 ID", "回答内容": "回答内容", "回调 URL 填": "回调 URL 填", + "回调 URL 格式": "回调 URL 格式", "回调地址": "回调地址", + "回调支付方式": "回调支付方式", + "回调调用者IP": "回调调用者IP", + "回调通知地址": "回调通知地址", "固定价格": "固定价格", "固定价格(每次)": "固定价格(每次)", "固定价格值": "固定价格值", "图像生成": "图像生成", "图标": "图标", + "图标使用 react-icons(Simple Icons)或 URL/emoji,例如:github、gitlab、si:google": "图标使用 react-icons(Simple Icons)或 URL/emoji,例如:github、gitlab、si:google", "图标使用@lobehub/icons库,如:OpenAI、Claude.Color,支持链式参数:OpenAI.Avatar.type={'platform'}、OpenRouter.Avatar.shape={'square'},查询所有可用图标请 ": "图标使用@lobehub/icons库,如:OpenAI、Claude.Color,支持链式参数:OpenAI.Avatar.type={'platform'}、OpenRouter.Avatar.shape={'square'},查询所有可用图标请 ", + "图标文件不能超过 100KB,请压缩后重新上传": "图标文件不能超过 100KB,请压缩后重新上传", "图混合": "图混合", + "图片倍率 {{imageRatio}}": "图片倍率 {{imageRatio}}", "图片功能在自定义请求体模式下不可用": "图片功能在自定义请求体模式下不可用", "图片地址": "图片地址", "图片已添加": "图片已添加", + "图片生成:1 次 * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "图片生成:1 次 * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "图片生成调用:{{symbol}}{{price}} / 1次": "图片生成调用:{{symbol}}{{price}} / 1次", + "图片输入 {{price}} / 1M tokens": "图片输入 {{price}} / 1M tokens", "图片输入: {{imageRatio}}": "图片输入: {{imageRatio}}", + "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "图片输入价格": "图片输入价格", + "图片输入价格 {{symbol}}{{price}} / 1M tokens": "图片输入价格 {{symbol}}{{price}} / 1M tokens", "图片输入价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (图片倍率: {{imageRatio}})": "图片输入价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (图片倍率: {{imageRatio}})", + "图片输入价格:{{symbol}}{{price}} / 1M tokens": "图片输入价格:{{symbol}}{{price}} / 1M tokens", + "图片输入价格:{{symbol}}{{total}} / 1M tokens": "图片输入价格:{{symbol}}{{total}} / 1M tokens", + "图片输入倍率": "图片输入倍率", "图片输入倍率(仅部分模型支持该计费)": "图片输入倍率(仅部分模型支持该计费)", "图片输入相关的倍率设置,键为模型名称,值为倍率,仅部分模型支持该计费": "图片输入相关的倍率设置,键为模型名称,值为倍率,仅部分模型支持该计费", "图生文": "图生文", @@ -730,6 +1059,7 @@ "在Gotify服务器的应用管理中创建新应用": "在Gotify服务器的应用管理中创建新应用", "在找兑换码?": "在找兑换码?", "在新标签页中打开": "在新标签页中打开", + "在模型广场向用户展示的端点": "在模型广场向用户展示的端点", "在此输入 Logo 图片地址": "在此输入 Logo 图片地址", "在此输入新的公告内容,支持 Markdown & HTML 代码": "在此输入新的公告内容,支持 Markdown & HTML 代码", "在此输入新的关于内容,支持 Markdown & HTML 代码。如果输入的是一个链接,则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为关于页面": "在此输入新的关于内容,支持 Markdown & HTML 代码。如果输入的是一个链接,则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为关于页面", @@ -738,21 +1068,47 @@ "在此输入系统名称": "在此输入系统名称", "在此输入隐私政策内容,支持 Markdown & HTML 代码": "在此输入隐私政策内容,支持 Markdown & HTML 代码", "在此输入首页内容,支持 Markdown & HTML 代码,设置后首页的状态信息将不再显示。如果输入的是一个链接,则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为首页": "在此输入首页内容,支持 Markdown & HTML 代码,设置后首页的状态信息将不再显示。如果输入的是一个链接,则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为首页", + "在默认列表基础上新增一个分组": "在默认列表基础上新增一个分组", + "场景:站点提供两个价格档位,用户可以按需选择": "场景:站点提供两个价格档位,用户可以按需选择", + "场景:站点有 standard 和 premium 两个用户可选分组。希望 vip 用户额外看到 exclusive 分组,同时不再看到 standard 分组": "场景:站点有 standard 和 premium 两个用户可选分组。希望 vip 用户额外看到 exclusive 分组,同时不再看到 standard 分组", + "场景:站点有 standard(倍率 1.0)和 premium(倍率 0.5)两个分组,希望 vip 用户使用 standard 令牌时也能享受折扣": "场景:站点有 standard(倍率 1.0)和 premium(倍率 0.5)两个分组,希望 vip 用户使用 standard 令牌时也能享受折扣", + "场景:设置自动选择优先级": "场景:设置自动选择优先级", "域名IP过滤详细说明": "推荐开启:开启后会对域名进行 DNS 解析并检查解析后的 IP 是否为私有地址,可有效防止 DNS 重绑定攻击绕过 SSRF 防护。注意:域名可能解析到多个 IPv4/IPv6 地址,若配置了 IP 过滤列表,请确保覆盖这些地址,否则可能导致访问失败。", "域名白名单": "域名白名单", "域名黑名单": "域名黑名单", "基本信息": "基本信息", + "基础价格": "基础价格", + "填充 Codex CLI / Claude CLI 模版": "填充 Codex CLI / Claude CLI 模版", + "填充新模板": "填充新模板", + "填充旧模板": "填充旧模板", + "填充模板": "填充模板", + "填充模板:等级+激活": "填充模板:等级+激活", + "填充模板:等级提示": "填充模板:等级提示", + "填充模板:组织或角色": "填充模板:组织或角色", + "填充模板:组织提示": "填充模板:组织提示", + "填充模板(全渠道)": "填充模板(全渠道)", + "填充模板(指定渠道)": "填充模板(指定渠道)", "填入": "填入", + "填入 CC Switch": "填入 CC Switch", "填入所有模型": "填入所有模型", + "填入来源": "填入来源", "填入模板": "填入模板", - "填入透传模版": "填入透传模版", - "填入透传完整模版": "填入透传完整模版", + "填入目标": "填入目标", "填入相关模型": "填入相关模型", + "填入路径": "填入路径", + "填入透传完整模版": "填入透传完整模版", + "填入透传模版": "填入透传模版", + "填写 Issuer URL 后自动生成:": "填写 Issuer URL 后自动生成:", "填写Gotify服务器的完整URL地址": "填写Gotify服务器的完整URL地址", + "填写后会自动拼接预设端点": "填写后会自动拼接预设端点", "填写带https的域名,逗号分隔": "填写带https的域名,逗号分隔", + "填写服务器地址后自动生成:": "填写服务器地址后自动生成:", "填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议": "填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议", "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策", + "填写音频补全价格前,需要先填写音频输入价格。": "填写音频补全价格前,需要先填写音频输入价格。", + "处理上游模型更新": "处理上游模型更新", "处理中": "处理中", + "处理全部渠道上游更新": "处理全部渠道上游更新", "备份支持": "备份支持", "备份状态": "备份状态", "备注": "备注", @@ -762,11 +1118,13 @@ "复制": "复制", "复制代码": "复制代码", "复制令牌": "复制令牌", + "复制令牌失败": "复制令牌失败", "复制全部": "复制全部", "复制名称": "复制名称", "复制失败": "复制失败", "复制失败,请手动复制": "复制失败,请手动复制", "复制失败,请手动选择文本复制": "复制失败,请手动选择文本复制", + "复制密钥": "复制密钥", "复制已选": "复制已选", "复制应用的令牌(Token)并填写到上方的应用令牌字段": "复制应用的令牌(Token)并填写到上方的应用令牌字段", "复制成功": "复制成功", @@ -774,10 +1132,13 @@ "复制所有模型": "复制所有模型", "复制所选令牌": "复制所选令牌", "复制所选兑换码到剪贴板": "复制所选兑换码到剪贴板", + "复制授权链接": "复制授权链接", "复制日志": "复制日志", "复制渠道的所有信息": "复制渠道的所有信息", "复制版本号": "复制版本号", "复制生成的密钥并粘贴到此处": "复制生成的密钥并粘贴到此处", + "复制请求头": "复制请求头", + "复制连接信息": "复制连接信息", "复制链接": "复制链接", "外接设备": "外接设备", "多个命令用空格分隔": "多个命令用空格分隔", @@ -788,19 +1149,34 @@ "天": "天", "天前": "天前", "失败": "失败", + "失败原因": "失败原因", + "失败后不重试": "失败后不重试", + "失败后是否重试": "失败后是否重试", "失败时自动禁用通道": "失败时自动禁用通道", "失败重试次数": "失败重试次数", "奖励说明": "奖励说明", + "套餐": "套餐", + "套餐副标题": "套餐副标题", + "套餐名称": "套餐名称", + "套餐标题": "套餐标题", + "套餐标题不能为空": "套餐标题不能为空", + "套餐的基本信息和定价": "套餐的基本信息和定价", "如:大带宽批量分析图片推荐": "如:大带宽批量分析图片推荐", "如:香港线路": "如:香港线路", + "如果亲和到的渠道失败,重试到其他渠道成功后,将亲和更新到成功的渠道。": "如果亲和到的渠道失败,重试到其他渠道成功后,将亲和更新到成功的渠道。", "如果你对接的是上游One API或者New API等转发项目,请使用OpenAI类型,不要使用此类型,除非你知道你在做什么。": "如果你对接的是上游One API或者New API等转发项目,请使用OpenAI类型,不要使用此类型,除非你知道你在做什么。", "如果用户请求中包含系统提示词,则使用此设置拼接到用户的系统提示词前面": "如果用户请求中包含系统提示词,则使用此设置拼接到用户的系统提示词前面", "如果镜像为私有,请填写密码或Token": "如果镜像为私有,请填写密码或Token", "如果镜像为私有,请填写用户名": "如果镜像为私有,请填写用户名", "始终使用浅色主题": "始终使用浅色主题", "始终使用深色主题": "始终使用深色主题", + "字段映射": "字段映射", + "字段缺失视为命中": "字段缺失视为命中", + "字段路径": "字段路径", "字段透传控制": "字段透传控制", + "字段速查": "字段速查", "存在惩罚,鼓励讨论新话题": "存在惩罚,鼓励讨论新话题", + "存在重复的分组名称:": "存在重复的分组名称:", "存在重复的键名:": "存在重复的键名:", "安全提醒": "安全提醒", "安全设置": "安全设置", @@ -815,8 +1191,8 @@ "完整的 Base URL,支持变量{model}": "完整的 Base URL,支持变量{model}", "官方": "官方", "官方文档": "官方文档", - "官方说明": "官方说明", "官方模型同步": "官方模型同步", + "官方说明": "官方说明", "定价模式": "定价模式", "定时测试所有通道": "定时测试所有通道", "定期更改密码可以提高账户安全性": "定期更改密码可以提高账户安全性", @@ -824,6 +1200,7 @@ "实付金额": "实付金额", "实付金额:": "实付金额:", "实际模型": "实际模型", + "实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)", "实际请求体": "实际请求体", "容器": "容器", "容器ID": "容器ID", @@ -855,6 +1232,8 @@ "密码重置确认": "密码重置确认", "密码长度至少为8个字符": "密码长度至少为8个字符", "密钥": "密钥", + "密钥 JSON 必须包含 access_token": "密钥 JSON 必须包含 access_token", + "密钥 JSON 必须包含 account_id": "密钥 JSON 必须包含 account_id", "密钥(编辑模式下,保存的密钥不会显示)": "密钥(编辑模式下,保存的密钥不会显示)", "密钥去重": "密钥去重", "密钥将以Bearer方式添加到请求头中,用于验证webhook请求的合法性": "密钥将以Bearer方式添加到请求头中,用于验证webhook请求的合法性", @@ -862,6 +1241,8 @@ "密钥已启用": "密钥已启用", "密钥已复制到剪贴板": "密钥已复制到剪贴板", "密钥已禁用": "密钥已禁用", + "密钥必须是 JSON 对象": "密钥必须是 JSON 对象", + "密钥必须是合法的 JSON 格式!": "密钥必须是合法的 JSON 格式!", "密钥文件 (.json)": "密钥文件 (.json)", "密钥更新模式": "密钥更新模式", "密钥格式": "密钥格式", @@ -875,6 +1256,8 @@ "对免费模型启用预消耗": "对免费模型启用预消耗", "对域名启用 IP 过滤(推荐开启)": "对域名启用 IP 过滤(推荐开启)", "对外运营模式": "对外运营模式", + "对比:不勾选「用户可选」的场景": "对比:不勾选「用户可选」的场景", + "对象清理规则": "对象清理规则", "导入": "导入", "导入的配置将覆盖当前设置,是否继续?": "导入的配置将覆盖当前设置,是否继续?", "导入配置": "导入配置", @@ -889,9 +1272,14 @@ "将删除": "将删除", "将删除 {{value}} 天前的日志文件。": "将删除 {{value}} 天前的日志文件。", "将删除已使用、已禁用及过期的兑换码,此操作不可撤销。": "将删除已使用、已禁用及过期的兑换码,此操作不可撤销。", + "将删除所有仍在内存中的渠道亲和性缓存条目。": "将删除所有仍在内存中的渠道亲和性缓存条目。", "将只保留最近 {{value}} 个日志文件,其余将被删除。": "将只保留最近 {{value}} 个日志文件,其余将被删除。", + "将大请求体临时存储到磁盘": "将大请求体临时存储到磁盘", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。_other": "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。", "将清除所有保存的配置并恢复默认设置,此操作不可撤销。是否继续?": "将清除所有保存的配置并恢复默认设置,此操作不可撤销。是否继续?", "将清除选定时间之前的所有日志": "将清除选定时间之前的所有日志", + "将追加 2 条规则到现有规则列表。": "将追加 2 条规则到现有规则列表。", "小时": "小时", "小时费率": "小时费率", "尚未使用": "尚未使用", @@ -901,23 +1289,34 @@ "展开": "展开", "展开更多": "展开更多", "展示价格": "展示价格", + "嵌套映射:用户分组 → 使用分组 → 倍率": "嵌套映射:用户分组 → 使用分组 → 倍率", "左侧边栏个人设置": "左侧边栏个人设置", "已为 {{count}} 个模型设置{{type}}_other": "已为 {{count}} 个模型设置{{type}}", "已为 ${count} 个渠道设置标签!": "已为 ${count} 个渠道设置标签!", + "已从 Discovery 自动填充配置": "已从 Discovery 自动填充配置", + "已从 Discovery 获取配置,可继续手动修改所有字段。": "已从 Discovery 获取配置,可继续手动修改所有字段。", + "已作废": "已作废", + "已保存偏好为": "已保存偏好为", "已修复 ${success} 个通道,失败 ${fails} 个通道。": "已修复 ${success} 个通道,失败 ${fails} 个通道。", "已停止": "已停止", "已停止批量测试": "已停止批量测试", "已关闭后续提醒": "已关闭后续提醒", + "已分配内存": "已分配内存", "已切换为Assistant角色": "已切换为Assistant角色", "已切换为System角色": "已切换为System角色", "已切换至最优倍率视图,每个模型使用其最低倍率分组": "已切换至最优倍率视图,每个模型使用其最低倍率分组", "已初始化": "已初始化", + "已删除": "已删除", "已删除 {{count}} 个令牌!": "已删除 {{count}} 个令牌!", "已删除 {{count}} 个令牌!_other": "已删除 {{count}} 个令牌!", "已删除 {{count}} 条失效兑换码_other": "已删除 {{count}} 条失效兑换码", "已删除 ${data} 个通道!": "已删除 ${data} 个通道!", "已删除所有禁用渠道,共计 ${data} 个": "已删除所有禁用渠道,共计 ${data} 个", "已删除消息及其回复": "已删除消息及其回复", + "已勾选": "已勾选", + "已勾选 {{count}} 个模型": "已勾选 {{count}} 个模型", + "已勾选 {{count}} 个模型_other": "已勾选 {{count}} 个模型", + "已发起支付": "已发起支付", "已发送到 Fluent": "已发送到 Fluent", "已取消 Passkey 注册": "已取消 Passkey 注册", "已同步到渠道": "已同步到渠道", @@ -925,6 +1324,10 @@ "已启用 Passkey,无需密码即可登录": "已启用 Passkey,无需密码即可登录", "已启用所有密钥": "已启用所有密钥", "已在自定义模式中忽略": "已在自定义模式中忽略", + "已填充提示模板": "已填充提示模板", + "已填充模版": "已填充模版", + "已填充策略模板": "已填充策略模板", + "已处理上游模型更新:加入 {{added}} 个,删除 {{removed}} 个,本次忽略 {{ignored}} 个,当前已忽略模型 {{totalIgnored}} 个": "已处理上游模型更新:加入 {{added}} 个,删除 {{removed}} 个,本次忽略 {{ignored}} 个,当前已忽略模型 {{totalIgnored}} 个", "已备份": "已备份", "已复制": "已复制", "已复制 ${count} 个模型": "已复制 ${count} 个模型", @@ -935,12 +1338,19 @@ "已复制到剪切板": "已复制到剪切板", "已复制到剪贴板": "已复制到剪贴板", "已复制到剪贴板!": "已复制到剪贴板!", + "已复制字段:{{name}}": "已复制字段:{{name}}", "已复制模型名称": "已复制模型名称", "已复制版本号": "已复制版本号", "已复制自动生成的 API Key": "已复制自动生成的 API Key", "已完成": "已完成", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型_other": "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型", "已开启全局请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "已开启全局请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。", + "已忽略模型": "已忽略模型", "已成功开始测试所有已启用通道,请刷新页面查看结果。": "已成功开始测试所有已启用通道,请刷新页面查看结果。", + "已打开授权页面": "已打开授权页面", + "已打开支付页面": "已打开支付页面", + "已批量处理上游模型更新:渠道 {{channels}} 个,加入 {{added}} 个,删除 {{removed}} 个,失败 {{fails}} 个": "已批量处理上游模型更新:渠道 {{channels}} 个,加入 {{added}} 个,删除 {{removed}} 个,失败 {{fails}} 个", "已提交": "已提交", "已支付金额": "已支付金额", "已新增 {{count}} 个模型:{{list}}_other": "已新增 {{count}} 个模型:{{list}}", @@ -952,9 +1362,13 @@ "已服务": "已服务", "已注销": "已注销", "已添加": "已添加", + "已添加 {{count}} 个模板_other": "已添加 {{count}} 个模板", "已添加到白名单": "已添加到白名单", "已清理 {{count}} 个日志文件,释放 {{size}}": "已清理 {{count}} 个日志文件,释放 {{size}}", + "已清理 {{count}} 个日志文件,释放 {{size}}_other": "已清理 {{count}} 个日志文件,释放 {{size}}", + "已清空": "已清空", "已清空测试结果": "已清空测试结果", + "已生成授权凭据": "已生成授权凭据", "已用": "已用", "已用/剩余": "已用/剩余", "已用额度": "已用额度", @@ -965,6 +1379,9 @@ "已结束": "已结束", "已耗尽": "已耗尽", "已解锁豆包自定义 API 地址编辑": "已解锁豆包自定义 API 地址编辑", + "已设置": "已设置", + "已达上限": "已达上限", + "已达到购买上限": "已达到购买上限", "已过期": "已过期", "已运行时间": "已运行时间", "已选择 {{count}} 个模型_other": "已选择 {{count}} 个模型", @@ -972,12 +1389,18 @@ "已选择 ${count} 个渠道": "已选择 ${count} 个渠道", "已重置为默认配置": "已重置为默认配置", "已销毁": "已销毁", + "币种": "币种", + "帐号信息": "帐号信息", + "常用上下文 Key(用于 context_*)": "常用上下文 Key(用于 context_*)", "常见问答": "常见问答", "常见问答管理,为用户提供常见问题的答案(最多50个,前端显示最新20条)": "常见问答管理,为用户提供常见问题的答案(最多50个,前端显示最新20条)", "平台": "平台", "平均RPM": "平均RPM", "平均TPM": "平均TPM", "平移": "平移", + "年": "年", + "应付金额": "应付金额", + "应用": "应用", "应用同步": "应用同步", "应用更改": "应用更改", "应用覆盖": "应用覆盖", @@ -993,47 +1416,77 @@ "建立连接时发生错误": "建立连接时发生错误", "建议在生产环境中使用 MySQL 或 PostgreSQL 数据库,或确保 SQLite 数据库文件已映射到宿主机的持久化存储。": "建议在生产环境中使用 MySQL 或 PostgreSQL 数据库,或确保 SQLite 数据库文件已映射到宿主机的持久化存储。", "开": "开", + "开启「默认使用 auto 分组」后,新建令牌和初始令牌都会自动设为 auto。": "开启「默认使用 auto 分组」后,新建令牌和初始令牌都会自动设为 auto。", "开启之后会清除用户提示词中的": "开启之后会清除用户提示词中的", "开启之后将上游地址替换为服务器地址": "开启之后将上游地址替换为服务器地址", + "开启后,using_group 会参与 cache key(不同分组隔离)。": "开启后,using_group 会参与 cache key(不同分组隔离)。", "开启后,仅\"消费\"和\"错误\"日志将记录您的客户端IP地址": "开启后,仅\"消费\"和\"错误\"日志将记录您的客户端IP地址", "开启后,对免费模型(倍率为0,或者价格为0)的模型也会预消耗额度": "开启后,对免费模型(倍率为0,或者价格为0)的模型也会预消耗额度", "开启后,将定期发送ping数据保持连接活跃": "开启后,将定期发送ping数据保持连接活跃", "开启后,当前分组渠道失败时会按顺序尝试下一个分组的渠道": "开启后,当前分组渠道失败时会按顺序尝试下一个分组的渠道", "开启后,所有请求将直接透传给上游,不会进行任何处理(重定向和渠道适配也将失效),请谨慎开启": "开启后,所有请求将直接透传给上游,不会进行任何处理(重定向和渠道适配也将失效),请谨慎开启", + "开启后,模型名称会参与 cache key(不同模型隔离)。": "开启后,模型名称会参与 cache key(不同模型隔离)。", + "开启后,若该规则命中且请求失败,将不会切换渠道重试。": "开启后,若该规则命中且请求失败,将不会切换渠道重试。", + "开启后,规则名称会参与 cache key(不同规则隔离)。": "开启后,规则名称会参与 cache key(不同规则隔离)。", + "开启后,该渠道请求 Claude 时将强制追加 ?beta=true(无需客户端手动传参)": "开启后,该渠道请求 Claude 时将强制追加 ?beta=true(无需客户端手动传参)", "开启后,违规请求将额外扣费。": "开启后,违规请求将额外扣费。", "开启后不限制:必须设置模型倍率": "开启后不限制:必须设置模型倍率", + "开启后创建令牌默认选择auto分组,初始令牌也将设为auto": "开启后创建令牌默认选择auto分组,初始令牌也将设为auto", "开启后未登录用户无法访问模型广场": "开启后未登录用户无法访问模型广场", + "开启后检测到新增模型会自动加入当前渠道模型列表": "开启后检测到新增模型会自动加入当前渠道模型列表", + "开启后由后端定时任务检测该渠道上游模型变化": "开启后由后端定时任务检测该渠道上游模型变化", "开启批量操作": "开启批量操作", + "开始": "开始", "开始同步": "开始同步", "开始批量测试 ${count} 个模型,已清空上次结果...": "开始批量测试 ${count} 个模型,已清空上次结果...", "开始时间": "开始时间", + "异常": "异常", + "异步任务退款": "异步任务退款", "张图片": "张图片", "弱变换": "弱变换", + "强制使用 AUTH LOGIN": "强制使用 AUTH LOGIN", "强制将响应格式化为 OpenAI 标准格式(只适用于OpenAI渠道类型)": "强制将响应格式化为 OpenAI 标准格式(只适用于OpenAI渠道类型)", "强制格式化": "强制格式化", "强制要求": "强制要求", "强变换": "强变换", "当上游通道返回错误中包含这些关键词时(不区分大小写),自动禁用通道": "当上游通道返回错误中包含这些关键词时(不区分大小写),自动禁用通道", + "当令牌分组设为 auto 时,系统按列表顺序依次选择可用分组。排在前面的优先级更高。": "当令牌分组设为 auto 时,系统按列表顺序依次选择可用分组。排在前面的优先级更高。", "当前 API 密钥已过期,请在设置中更新。": "当前 API 密钥已过期,请在设置中更新。", "当前 Ollama 版本为 ${version}": "当前 Ollama 版本为 ${version}", + "当前仅 OpenAI / Claude 语义支持缓存 token 统计,其他通道将隐藏 token 相关字段。": "当前仅 OpenAI / Claude 语义支持缓存 token 统计,其他通道将隐藏 token 相关字段。", "当前余额": "当前余额", "当前值": "当前值", + "当前值不是合法 JSON,无法格式化": "当前值不是合法 JSON,无法格式化", "当前分组为 auto,会自动选择最优分组,当一个组不可用时自动降级到下一个组(熔断机制)": "当前分组为 auto,会自动选择最优分组,当一个组不可用时自动降级到下一个组(熔断机制)", "当前剩余": "当前剩余", + "当前参数覆盖不是合法的 JSON": "当前参数覆盖不是合法的 JSON", + "当前提示": "当前提示", + "当前旧格式 JSON 不合法,无法追加模板": "当前旧格式 JSON 不合法,无法追加模板", + "当前旧格式不是 JSON 对象,无法追加模板": "当前旧格式不是 JSON 对象,无法追加模板", "当前时间": "当前时间", + "当前未启用,需要时再打开即可。": "当前未启用,需要时再打开即可。", "当前未开启Midjourney回调,部分项目可能无法获得绘图结果,可在运营设置中开启。": "当前未开启Midjourney回调,部分项目可能无法获得绘图结果,可在运营设置中开启。", "当前查看的分组为:{{group}},倍率为:{{ratio}}": "当前查看的分组为:{{group}},倍率为:{{ratio}}", "当前模型列表为该标签下所有渠道模型列表最长的一个,并非所有渠道的并集,请注意可能导致某些渠道模型丢失。": "当前模型列表为该标签下所有渠道模型列表最长的一个,并非所有渠道的并集,请注意可能导致某些渠道模型丢失。", + "当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。": "当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。", + "当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。": "当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。", + "当前没有未设置定价的模型": "当前没有未设置定价的模型", "当前版本": "当前版本", "当前状态": "当前状态", + "当前缓存大小": "当前缓存大小", + "当前编辑": "当前编辑", + "当前规则不支持写入到该位置": "当前规则不支持写入到该位置", + "当前规则未设置参数覆盖模板": "当前规则未设置参数覆盖模板", "当前计费": "当前计费", "当前设备不支持 Passkey": "当前设备不支持 Passkey", "当前设置类型: ": "当前设置类型: ", "当前跟随系统": "当前跟随系统", "当前配置无法连接到 io.net。": "当前配置无法连接到 io.net。", - "当钱包或订阅剩余额度低于此数值时,系统将通过选择的方式发送通知": "当钱包或订阅剩余额度低于此数值时,系统将通过选择的方式发送通知", + "当前额度": "当前额度", + "当某个分组的用户使用另一个分组的令牌时,可设置特殊倍率覆盖基础倍率。例如:vip 分组的用户使用 default 分组时倍率为 0.5": "当某个分组的用户使用另一个分组的令牌时,可设置特殊倍率覆盖基础倍率。例如:vip 分组的用户使用 default 分组时倍率为 0.5", "当模型没有设置价格时仍接受调用,仅当您信任该网站时使用,可能会产生高额费用": "当模型没有设置价格时仍接受调用,仅当您信任该网站时使用,可能会产生高额费用", "当运行通道全部测试时,超过此时间将自动禁用通道": "当运行通道全部测试时,超过此时间将自动禁用通道", + "当钱包或订阅剩余额度低于此数值时,系统将通过选择的方式发送通知": "当钱包或订阅剩余额度低于此数值时,系统将通过选择的方式发送通知", "待使用收益": "待使用收益", "待部署": "待部署", "微信": "微信", @@ -1041,18 +1494,25 @@ "微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效)": "微信扫码关注公众号,输入「验证码」获取验证码(三分钟内有效)", "微信扫码登录": "微信扫码登录", "微信账户绑定成功!": "微信账户绑定成功!", + "必填:请输入服务器地址以自动生成完整端点 URL": "必填:请输入服务器地址以自动生成完整端点 URL", + "必填。对请求的 model 名称进行匹配,任意一条匹配即命中该规则。": "必填。对请求的 model 名称进行匹配,任意一条匹配即命中该规则。", + "必须全部满足(AND)": "必须全部满足(AND)", "必须是有效的 JSON 字符串数组,例如:[\"g1\",\"g2\"]": "必须是有效的 JSON 字符串数组,例如:[\"g1\",\"g2\"]", "忘记密码?": "忘记密码?", "快速开始": "快速开始", "快速选择": "快速选择", + "忽略": "忽略", "思考中...": "思考中...", "思考内容转换": "思考内容转换", "思考过程": "思考过程", "思考适配 BudgetTokens 百分比": "思考适配 BudgetTokens 百分比", "思考预算占比": "思考预算占比", "性能指标": "性能指标", + "性能监控": "性能监控", + "性能设置": "性能设置", "总 GPU 小时": "总 GPU 小时", "总价:文字价格 {{textPrice}} + 音频价格 {{audioPrice}} = {{symbol}}{{total}}": "总价:文字价格 {{textPrice}} + 音频价格 {{audioPrice}} = {{symbol}}{{total}}", + "总分配内存": "总分配内存", "总密钥数": "总密钥数", "总收益": "总收益", "总计": "总计", @@ -1071,20 +1531,30 @@ "想起来了?": "想起来了?", "成功": "成功", "成功兑换额度:": "成功兑换额度:", + "成功后切换亲和": "成功后切换亲和", "成功时自动启用通道": "成功时自动启用通道", "我已了解禁用两步验证将永久删除所有相关设置和备用码,此操作不可撤销": "我已了解禁用两步验证将永久删除所有相关设置和备用码,此操作不可撤销", "我已阅读并同意": "我已阅读并同意", + "我的订阅": "我的订阅", + "我确认开启高危重试": "我确认开启高危重试", "或": "或", "或其兼容new-api-worker格式的其他版本": "或其兼容new-api-worker格式的其他版本", "或手动输入密钥:": "或手动输入密钥:", "所有上游数据均可信": "所有上游数据均可信", "所有密钥已复制到剪贴板": "所有密钥已复制到剪贴板", + "所有用户": "所有用户", "所有编辑均为覆盖操作,留空则不更改": "所有编辑均为覆盖操作,留空则不更改", + "所选模板已存在": "所选模板已存在", "手动禁用": "手动禁用", "手动编辑": "手动编辑", "手动输入": "手动输入", + "打开 CC Switch": "打开 CC Switch", "打开侧边栏": "打开侧边栏", + "打开授权页面": "打开授权页面", + "扣费": "扣费", + "执行 GC": "执行 GC", "执行中": "执行中", + "扩展价格": "扩展价格", "扫描二维码": "扫描二维码", "批量创建": "批量创建", "批量创建时会在名称后自动添加随机后缀": "批量创建时会在名称后自动添加随机后缀", @@ -1094,9 +1564,13 @@ "批量删除失败": "批量删除失败", "批量删除成功": "批量删除成功", "批量删除模型": "批量删除模型", + "批量处理失败": "批量处理失败", + "批量应用当前模型价格": "批量应用当前模型价格", "批量操作": "批量操作", "批量操作失败": "批量操作失败", "批量操作完成: {{success}}个成功, {{failed}}个失败": "批量操作完成: {{success}}个成功, {{failed}}个失败", + "批量检测失败": "批量检测失败", + "批量检测完成:渠道 {{channels}} 个,新增 {{add}} 个,删除 {{remove}} 个,失败 {{fails}} 个": "批量检测完成:渠道 {{channels}} 个,新增 {{add}} 个,删除 {{remove}} 个,失败 {{fails}} 个", "批量测试${count}个模型": "批量测试${count}个模型", "批量测试完成!成功: ${success}, 失败: ${fail}, 总计: ${total}": "批量测试完成!成功: ${success}, 失败: ${fail}, 总计: ${total}", "批量测试已停止": "批量测试已停止", @@ -1110,46 +1584,60 @@ "拉取新模型": "拉取新模型", "拉取模型": "拉取模型", "拉取进度": "拉取进度", + "拒绝提示模板(可选)": "拒绝提示模板(可选)", + "拦截原因": "拦截原因", "按K显示单位": "按K显示单位", "按价格设置": "按价格设置", "按倍率类型筛选": "按倍率类型筛选", "按倍率设置": "按倍率设置", "按次": "按次", + "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", + "按次:{{symbol}}{{price}}": "按次:{{symbol}}{{price}}", + "按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}", "按次计费": "按次计费", "按照如下格式输入:AccessKey|SecretAccessKey|Region": "按照如下格式输入:AccessKey|SecretAccessKey|Region", "按量计费": "按量计费", + "按量计费下需要先填写输入价格,才能保存其它价格项。": "按量计费下需要先填写输入价格,才能保存其它价格项。", "按顺序替换content中的变量占位符": "按顺序替换content中的变量占位符", "换脸": "换脸", "授权,需在遵守": "授权,需在遵守", "授权失败": "授权失败", + "授权端点": "授权端点", + "授权范围 (Scopes)": "授权范围 (Scopes)", + "排序": "排序", "排队中": "排队中", "接受未设置价格模型": "接受未设置价格模型", "接口凭证": "接口凭证", "接口密钥已过期": "接口密钥已过期", + "接收上游模型更新通知": "接收上游模型更新通知", "控制台": "控制台", "控制台区域": "控制台区域", "控制输出的随机性和创造性": "控制输出的随机性和创造性", "控制顶栏模块显示状态,全局生效": "控制顶栏模块显示状态,全局生效", + "推荐": "推荐", "推荐:用户可以选择是否使用指纹等验证": "推荐:用户可以选择是否使用指纹等验证", "推荐使用(用户可选)": "推荐使用(用户可选)", "描述": "描述", "提交": "提交", "提交时间": "提交时间", "提交结果": "提交结果", + "提供商名称": "提供商名称", "提升": "提升", "提示": "提示", "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}}", "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "提示:如需备份数据,只需复制上述目录即可": "提示:如需备份数据,只需复制上述目录即可", + "提示:此处配置仅用于控制「模型广场」对用户的展示效果,不会影响模型的实际调用与路由。若需配置真实调用行为,请前往「渠道管理」进行设置。": "提示:此处配置仅用于控制「模型广场」对用户的展示效果,不会影响模型的实际调用与路由。若需配置真实调用行为,请前往「渠道管理」进行设置。", + "提示:该功能为测试版,未来配置结构与功能行为可能发生变更,请勿在生产环境使用。": "提示:该功能为测试版,未来配置结构与功能行为可能发生变更,请勿在生产环境使用。", + "提示:语言偏好会同步到您登录的所有设备,并影响API返回的错误消息语言。": "提示:语言偏好会同步到您登录的所有设备,并影响API返回的错误消息语言。", "提示:链接中的{key}将被替换为API密钥,{address}将被替换为服务器地址": "提示:链接中的{key}将被替换为API密钥,{address}将被替换为服务器地址", "提示价格:{{symbol}}{{price}} / 1M tokens": "提示价格:{{symbol}}{{price}} / 1M tokens", "提示缓存倍率": "提示缓存倍率", - "缓存创建倍率": "缓存创建倍率", - "默认为 5m 缓存创建倍率;1h 缓存创建倍率按固定乘法自动计算(当前为 1.6x)": "默认为 5m 缓存创建倍率;1h 缓存创建倍率按固定乘法自动计算(当前为 1.6x)", "搜索供应商": "搜索供应商", "搜索关键字": "搜索关键字", "搜索失败": "搜索失败", + "搜索字段名 / 中文说明": "搜索字段名 / 中文说明", "搜索无结果": "搜索无结果", "搜索日志内容": "搜索日志内容", "搜索条件": "搜索条件", @@ -1159,21 +1647,36 @@ "搜索模型失败": "搜索模型失败", "搜索渠道名称或地址": "搜索渠道名称或地址", "搜索聊天应用名称": "搜索聊天应用名称", + "搜索规则(描述 / 类型 / 路径 / 来源 / 目标)": "搜索规则(描述 / 类型 / 路径 / 来源 / 目标)", "搜索部署名称": "搜索部署名称", "操作": "操作", "操作失败": "操作失败", "操作失败,请重试": "操作失败,请重试", "操作成功完成!": "操作成功完成!", "操作暂时被禁用": "操作暂时被禁用", + "操作确认": "操作确认", + "操作类型": "操作类型", "操练场": "操练场", "操练场和聊天功能": "操练场和聊天功能", + "支付": "支付", "支付地址": "支付地址", + "支付失败": "支付失败", + "支付完成后用户跳转的页面,留空则自动使用 服务器地址 + /console/topup": "支付完成后用户跳转的页面,留空则自动使用 服务器地址 + /console/topup", "支付宝": "支付宝", "支付方式": "支付方式", + "支付方式名称": "支付方式名称", + "支付方式名称不能为空": "支付方式名称不能为空", + "支付方式类型": "支付方式类型", + "支付渠道": "支付渠道", "支付设置": "支付设置", "支付请求失败": "支付请求失败", + "支付返回地址": "支付返回地址", "支付金额": "支付金额", "支持 Ctrl+V 粘贴图片": "支持 Ctrl+V 粘贴图片", + "支持 JSONPath,如 email, data.user.email": "支持 JSONPath,如 email, data.user.email", + "支持 JSONPath,如 name, display_name, data.user.name": "支持 JSONPath,如 name, display_name, data.user.name", + "支持 JSONPath,如 preferred_username, login, data.user.username": "支持 JSONPath,如 preferred_username, login, data.user.username", + "支持 JSONPath,如 sub, id, data.user.id": "支持 JSONPath,如 sub, id, data.user.id", "支持6位TOTP验证码或8位备用码,可到`个人设置-安全设置-两步验证设置`配置或查看。": "支持6位TOTP验证码或8位备用码,可到`个人设置-安全设置-两步验证设置`配置或查看。", "支持CIDR格式,如:8.8.8.8, 192.168.1.0/24": "支持CIDR格式,如:8.8.8.8, 192.168.1.0/24", "支持HTTP和HTTPS,填写Gotify服务器的完整URL地址": "支持HTTP和HTTPS,填写Gotify服务器的完整URL地址", @@ -1181,16 +1684,22 @@ "支持众多的大模型供应商": "支持众多的大模型供应商", "支持单个端口和端口范围,如:80, 443, 8000-8999": "支持单个端口和端口范围,如:80, 443, 8000-8999", "支持变量:": "支持变量:", + "支持周期性重置套餐权益额度": "支持周期性重置套餐权益额度", + "支持填写单个状态码或范围(含首尾),使用逗号分隔": "支持填写单个状态码或范围(含首尾),使用逗号分隔", + "支持填写单个状态码或范围(含首尾),使用逗号分隔;504 和 524 始终不重试,不受此处配置影响": "支持填写单个状态码或范围(含首尾),使用逗号分隔;504 和 524 始终不重试,不受此处配置影响", "支持备份": "支持备份", "支持拉取 Ollama 官方模型库中的所有模型,拉取过程可能需要几分钟时间": "支持拉取 Ollama 官方模型库中的所有模型,拉取过程可能需要几分钟时间", "支持搜索用户的 ID、用户名、显示名称和邮箱地址": "支持搜索用户的 ID、用户名、显示名称和邮箱地址", "支持的图像模型": "支持的图像模型", + "支持精确匹配;使用 regex: 开头可按正则匹配。": "支持精确匹配;使用 regex: 开头可按正则匹配。", "支持通配符格式,如:example.com, *.api.example.com": "支持通配符格式,如:example.com, *.api.example.com", + "支持逻辑 and/or 与嵌套 groups;操作符支持 eq/ne/gt/gte/lt/lte/in/not_in/contains/exists": "支持逻辑 and/or 与嵌套 groups;操作符支持 eq/ne/gt/gte/lt/lte/in/not_in/contains/exists", "收益": "收益", "收益统计": "收益统计", "收起": "收起", "收起侧边栏": "收起侧边栏", "收起内容": "收起内容", + "收起原生额度输入": "收起原生额度输入", "放大": "放大", "放大编辑": "放大编辑", "敏感信息不会发送到前端显示": "敏感信息不会发送到前端显示", @@ -1207,24 +1716,40 @@ "数据看板默认时间粒度": "数据看板默认时间粒度", "数据管理和日志查看": "数据管理和日志查看", "文件上传": "文件上传", + "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "文件搜索价格:{{symbol}}{{price}} / 1K 次": "文件搜索价格:{{symbol}}{{price}} / 1K 次", + "文件搜索调用 {{fileSearchCallCount}} 次": "文件搜索调用 {{fileSearchCallCount}} 次", "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", + "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "文字提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "文字提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", "文字输入": "文字输入", "文字输出": "文字输出", + "文字输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "文字输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "文心一言": "文心一言", "文档": "文档", "文档地址": "文档地址", "文生视频": "文生视频", + "新增": "新增", + "新增 Key 来源": "新增 Key 来源", "新增供应商": "新增供应商", + "新增失败": "新增失败", + "新增已选 {{selected}} / {{total}}": "新增已选 {{selected}} / {{total}}", + "新增成功": "新增成功", + "新增支付方式": "新增支付方式", + "新增条件": "新增条件", + "新增模型": "新增模型", + "新增规则": "新增规则", + "新增订阅": "新增订阅", "新密码": "新密码", "新密码需要和原密码不一致!": "新密码需要和原密码不一致!", "新建": "新建", + "新建套餐": "新建套餐", "新建容器": "新建容器", "新建容器部署": "新建容器部署", "新建数量": "新建数量", "新建组": "新建组", "新格式(支持条件判断与json自定义):": "新格式(支持条件判断与json自定义):", + "新格式(规则 + 条件)": "新格式(规则 + 条件)", "新格式模板": "新格式模板", "新版本": "新版本", "新用户使用邀请码奖励额度": "新用户使用邀请码奖励额度", @@ -1242,42 +1767,52 @@ "无法复制到剪贴板,请手动复制": "无法复制到剪贴板,请手动复制", "无法添加图片": "无法添加图片", "无法获取容器详情": "无法获取容器详情", + "无法读取剪贴板": "无法读取剪贴板", "无法连接 io.net": "无法连接 io.net", + "无生效": "无生效", "无邀请人": "无邀请人", "无限制": "无限制", "无限额度": "无限额度", + "日": "日", "日志导出成功": "日志导出成功", "日志已下载": "日志已下载", "日志已加载": "日志已加载", "日志已复制到剪贴板": "日志已复制到剪贴板", - "日志时间范围": "日志时间范围", "日志总大小": "日志总大小", "日志文件数": "日志文件数", + "日志时间范围": "日志时间范围", "日志流": "日志流", "日志清理失败:": "日志清理失败:", "日志目录": "日志目录", "日志类型": "日志类型", "日志设置": "日志设置", "日志详情": "日志详情", + "旧格式(JSON 对象)": "旧格式(JSON 对象)", "旧格式(直接覆盖):": "旧格式(直接覆盖):", + "旧格式必须是 JSON 对象": "旧格式必须是 JSON 对象", "旧格式模板": "旧格式模板", "旧的备用码已失效,请保存新的备用码": "旧的备用码已失效,请保存新的备用码", "早上好": "早上好", "时间": "时间", "时间信息": "时间信息", "时间粒度": "时间粒度", + "易支付": "易支付", "易支付商户ID": "易支付商户ID", "易支付商户密钥": "易支付商户密钥", "是": "是", "是否为企业账户": "是否为企业账户", "是否同时重置对话消息?选择\"是\"将清空所有对话记录并恢复默认示例;选择\"否\"将保留当前对话记录。": "是否同时重置对话消息?选择\"是\"将清空所有对话记录并恢复默认示例;选择\"否\"将保留当前对话记录。", "是否将该订单标记为成功并为用户入账?": "是否将该订单标记为成功并为用户入账?", + "是否检测上游模型更新": "是否检测上游模型更新", "是否确认充值?": "是否确认充值?", + "是否自动同步上游模型更新": "是否自动同步上游模型更新", "是否自动禁用": "是否自动禁用", "是否要求指纹/面容等生物识别": "是否要求指纹/面容等生物识别", "显示倍率": "显示倍率", "显示最新20条": "显示最新20条", "显示名称": "显示名称", + "显示名称字段": "显示名称字段", + "显示名称字段(可选)": "显示名称字段(可选)", "显示完整内容": "显示完整内容", "显示操作项": "显示操作项", "显示更多": "显示更多", @@ -1287,9 +1822,11 @@ "晚上好": "晚上好", "普通环境变量": "普通环境变量", "普通用户": "普通用户", + "普通输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "普通输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "智能体ID": "智能体ID", "智能熔断": "智能熔断", "智谱": "智谱", + "暂存错误": "暂存错误", "暂无": "暂无", "暂无API信息": "暂无API信息", "暂无SSE响应数据": "暂无SSE响应数据", @@ -1297,16 +1834,23 @@ "暂无保存的配置": "暂无保存的配置", "暂无充值记录": "暂无充值记录", "暂无公告": "暂无公告", + "暂无分组,点击下方按钮添加": "暂无分组,点击下方按钮添加", "暂无匹配模型": "暂无匹配模型", + "暂无参数覆盖记录": "暂无参数覆盖记录", + "暂无可复制 JSON": "暂无可复制 JSON", "暂无可复制的版本信息": "暂无可复制的版本信息", + "暂无可展示数据": "暂无可展示数据", "暂无可用的支付方式,请联系管理员配置": "暂无可用的支付方式,请联系管理员配置", + "暂无可购买套餐": "暂无可购买套餐", "暂无响应数据": "暂无响应数据", "暂无容器信息": "暂无容器信息", "暂无容器详情": "暂无容器详情", "暂无密钥数据": "暂无密钥数据", "暂无差异化倍率显示": "暂无差异化倍率显示", + "暂无已绑定项": "暂无已绑定项", "暂无常见问答": "暂无常见问答", "暂无成功模型": "暂无成功模型", + "暂无支付方式,点击上方按钮新增": "暂无支付方式,点击上方按钮新增", "暂无数据": "暂无数据", "暂无数据,点击下方按钮添加键值对": "暂无数据,点击下方按钮添加键值对", "暂无日志": "暂无日志", @@ -1319,6 +1863,11 @@ "暂无监控数据": "暂无监控数据", "暂无系统公告": "暂无系统公告", "暂无缺失模型": "暂无缺失模型", + "暂无自动分组,点击下方按钮添加": "暂无自动分组,点击下方按钮添加", + "暂无自定义 OAuth 提供商": "暂无自定义 OAuth 提供商", + "暂无规则,点击下方按钮添加": "暂无规则,点击下方按钮添加", + "暂无订阅套餐": "暂无订阅套餐", + "暂无订阅记录": "暂无订阅记录", "暂无请求数据": "暂无请求数据", "暂无项目": "暂无项目", "暂无预填组": "暂无预填组", @@ -1330,6 +1879,7 @@ "更新": "更新", "更新 Creem 设置": "更新 Creem 设置", "更新 Stripe 设置": "更新 Stripe 设置", + "更新 Waffo 设置": "更新 Waffo 设置", "更新SSRF防护设置": "更新SSRF防护设置", "更新Worker设置": "更新Worker设置", "更新令牌信息": "更新令牌信息", @@ -1337,8 +1887,10 @@ "更新名称失败": "更新名称失败", "更新失败": "更新失败", "更新失败,请检查输入信息": "更新失败,请检查输入信息", + "更新套餐信息": "更新套餐信息", "更新容器配置": "更新容器配置", "更新容器配置可能会导致容器重启,请确保在合适的时间进行此操作。": "更新容器配置可能会导致容器重启,请确保在合适的时间进行此操作。", + "更新成功": "更新成功", "更新所有已启用通道余额": "更新所有已启用通道余额", "更新支付设置": "更新支付设置", "更新时间": "更新时间", @@ -1350,27 +1902,39 @@ "更新配置后,容器可能需要重启以应用新的设置。请确保您了解这些更改的影响。": "更新配置后,容器可能需要重启以应用新的设置。请确保您了解这些更改的影响。", "更新配置失败": "更新配置失败", "更新预填组": "更新预填组", + "替换": "替换", + "月": "月", "有 Reasoning": "有 Reasoning", + "有序字符串数组": "有序字符串数组", + "有效期": "有效期", + "有效期单位": "有效期单位", + "有效期数值": "有效期数值", + "有效期设置": "有效期设置", "服务可用性": "服务可用性", "服务商": "服务商", + "服务器IP": "服务器IP", "服务器地址": "服务器地址", "服务器日志功能未启用(未配置日志目录)": "服务器日志功能未启用(未配置日志目录)", "服务器日志管理": "服务器日志管理", "服务显示名称": "服务显示名称", + "未匹配到模型,按回车键可将「{{name}}」作为自定义模型名添加": "未匹配到模型,按回车键可将「{{name}}」作为自定义模型名添加", "未发现新增模型": "未发现新增模型", "未发现重复密钥": "未发现重复密钥", "未启动": "未启动", "未启用": "未启用", "未命名": "未命名", + "未在 Discovery 响应中找到可用的 OAuth 端点": "未在 Discovery 响应中找到可用的 OAuth 端点", "未备份": "未备份", "未开始": "未开始", "未找到匹配的模型": "未找到匹配的模型", "未找到可用的容器访问地址": "未找到可用的容器访问地址", "未找到差异化倍率,无需同步": "未找到差异化倍率,无需同步", + "未授权": "未授权", "未提交": "未提交", "未检测到 Fluent 容器": "未检测到 Fluent 容器", "未检测到 FluentRead(流畅阅读),请确认扩展已启用": "未检测到 FluentRead(流畅阅读),请确认扩展已启用", "未测试": "未测试", + "未添加附加条件时,仅使用上方 type 进行清理。": "未添加附加条件时,仅使用上方 type 进行清理。", "未登录或登录已过期,请重新登录": "未登录或登录已过期,请重新登录", "未知": "未知", "未知供应商": "未知供应商", @@ -1385,13 +1949,16 @@ "未绑定": "未绑定", "未获取到授权码": "未获取到授权码", "未设置": "未设置", - "未设置倍率模型": "未设置倍率模型", + "未设置价格": "未设置价格", "未设置价格模型": "未设置价格模型", + "未设置倍率模型": "未设置倍率模型", + "未设置路径": "未设置路径", "未配置模型": "未配置模型", "未配置的模型列表": "未配置的模型列表", "本地": "本地", "本地数据存储": "本地数据存储", "本地计费": "本地计费", + "本月获得": "本月获得", "本设备:手机指纹/面容,外接:USB安全密钥": "本设备:手机指纹/面容,外接:USB安全密钥", "本设备内置": "本设备内置", "本项目根据": "本项目根据", @@ -1403,11 +1970,20 @@ "条": "条", "条 - 第": "条 - 第", "条,共": "条,共", + "条件取反": "条件取反", + "条件数": "条件数", + "条件规则": "条件规则", + "条件项设置": "条件项设置", "条日志已清理!": "条日志已清理!", + "条规则": "条规则", + "来源": "来源", "来源于 IO.NET 部署": "来源于 IO.NET 部署", + "来源端点": "来源端点", "来自模型重定向,尚未加入模型列表": "来自模型重定向,尚未加入模型列表", "某些配置更改可能需要几分钟才能生效。": "某些配置更改可能需要几分钟才能生效。", "查看": "查看", + "查看 Codex 帐号信息与用量": "查看 Codex 帐号信息与用量", + "查看 JSON 示例": "查看 JSON 示例", "查看关联部署": "查看关联部署", "查看图片": "查看图片", "查看密钥": "查看密钥", @@ -1415,8 +1991,10 @@ "查看所有可用的AI模型供应商,包括众多知名供应商的模型。": "查看所有可用的AI模型供应商,包括众多知名供应商的模型。", "查看日志": "查看日志", "查看渠道密钥": "查看渠道密钥", + "查看示例": "查看示例", "查看详情": "查看详情", "查询": "查询", + "标准价格": "标准价格", "标签": "标签", "标签不能为空!": "标签不能为空!", "标签信息": "标签信息", @@ -1425,31 +2003,50 @@ "标签组": "标签组", "标签聚合": "标签聚合", "标签聚合模式": "标签聚合模式", + "标识符 (Slug)": "标识符 (Slug)", "标识颜色": "标识颜色", + "核心概念": "核心概念", + "核心配置": "核心配置", "核采样,控制词汇选择的多样性": "核采样,控制词汇选择的多样性", + "根据 Anthropic 协定,/v1/messages 的输入 tokens 仅统计非缓存输入,不包含缓存读取与缓存写入 tokens。": "根据 Anthropic 协定,/v1/messages 的输入 tokens 仅统计非缓存输入,不包含缓存读取与缓存写入 tokens。", "根据模型名称和匹配规则查找模型元数据,优先级:精确 > 前缀 > 后缀 > 包含": "根据模型名称和匹配规则查找模型元数据,优先级:精确 > 前缀 > 后缀 > 包含", "格式化": "格式化", + "格式化 JSON": "格式化 JSON", "格式正确": "格式正确", "格式示例:": "格式示例:", - "前:": "前:", "格式错误": "格式错误", "检查更新": "检查更新", + "检测全部渠道上游更新": "检测全部渠道上游更新", "检测到 FluentRead(流畅阅读)": "检测到 FluentRead(流畅阅读)", + "检测到以下高危状态码重定向规则": "检测到以下高危状态码重定向规则", + "检测到剪贴板中的连接信息": "检测到剪贴板中的连接信息", "检测到多个密钥,您可以单独复制每个密钥,或点击复制全部获取完整内容。": "检测到多个密钥,您可以单独复制每个密钥,或点击复制全部获取完整内容。", "检测到该消息后有AI回复,是否删除后续回复并重新生成?": "检测到该消息后有AI回复,是否删除后续回复并重新生成?", + "检测失败": "检测失败", + "检测完成:新增 {{add}} 个,删除 {{remove}} 个": "检测完成:新增 {{add}} 个,删除 {{remove}} 个", "检测必须等待绘图成功才能进行放大等操作": "检测必须等待绘图成功才能进行放大等操作", + "概览": "概览", "模型": "模型", + "模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率": "模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率", + "模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率": "模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率", "模型: {{ratio}}": "模型: {{ratio}}", "模型专用区域": "模型专用区域", "模型价格": "模型价格", + "模型价格 {{price}}": "模型价格 {{price}}", + "模型价格 {{symbol}}{{price}} / 次": "模型价格 {{symbol}}{{price}} / 次", + "模型价格 {{symbol}}{{price}} / 次 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "模型价格 {{symbol}}{{price}} / 次 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "模型价格 {{symbol}}{{price}},{{ratioType}} {{ratio}}": "模型价格 {{symbol}}{{price}},{{ratioType}} {{ratio}}", + "模型价格:{{symbol}}{{price}}": "模型价格:{{symbol}}{{price}}", "模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}", - "按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}", + "模型价格:{{symbol}}{{price}} / 次": "模型价格:{{symbol}}{{price}} / 次", + "模型价格未配置": "模型价格未配置", "模型倍率": "模型倍率", "模型倍率 {{modelRatio}}": "模型倍率 {{modelRatio}}", "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}", "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}},Web 搜索调用 {{webSearchCallCount}} 次": "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}},Web 搜索调用 {{webSearchCallCount}} 次", "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},图片输入倍率 {{imageRatio}},{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},图片输入倍率 {{imageRatio}},{{ratioType}} {{ratio}}", + "模型倍率 {{modelRatio}},补全倍率 {{completionRatio}},音频倍率 {{audioRatio}},音频补全倍率 {{audioCompletionRatio}},{{cachePart}}{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},补全倍率 {{completionRatio}},音频倍率 {{audioRatio}},音频补全倍率 {{audioCompletionRatio}},{{cachePart}}{{ratioType}} {{ratio}}", + "模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},{{ratioType}} {{ratio}}", "模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},缓存创建倍率 {{cacheCreationRatio}},{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},缓存创建倍率 {{cacheCreationRatio}},{{ratioType}} {{ratio}}", "模型倍率值": "模型倍率值", "模型倍率和补全倍率": "模型倍率和补全倍率", @@ -1468,13 +2065,18 @@ "模型固定价格": "模型固定价格", "模型图标": "模型图标", "模型定价,需要登录访问": "模型定价,需要登录访问", + "模型定价设置": "模型定价设置", "模型广场": "模型广场", "模型拉取失败: {{error}}": "模型拉取失败: {{error}}", + "模型排行": "模型排行", "模型支持的接口端点信息": "模型支持的接口端点信息", "模型数据分析": "模型数据分析", "模型映射必须是合法的 JSON 格式!": "模型映射必须是合法的 JSON 格式!", "模型更新成功!": "模型更新成功!", "模型未加入列表,可能无法调用": "模型未加入列表,可能无法调用", + "模型正则": "模型正则", + "模型正则(每行一个)": "模型正则(每行一个)", + "模型正则不能为空": "模型正则不能为空", "模型消耗分布": "模型消耗分布", "模型消耗趋势": "模型消耗趋势", "模型版本": "模型版本", @@ -1484,6 +2086,7 @@ "模型管理": "模型管理", "模型组": "模型组", "模型补全倍率(仅对自定义模型有效)": "模型补全倍率(仅对自定义模型有效)", + "模型计费编辑器": "模型计费编辑器", "模型请求速率限制": "模型请求速率限制", "模型调用次数占比": "模型调用次数占比", "模型调用次数排行": "模型调用次数排行", @@ -1496,20 +2099,32 @@ "模型重定向": "模型重定向", "模型重定向里的下列模型尚未添加到“模型”列表,调用时会因为缺少可用模型而失败:": "模型重定向里的下列模型尚未添加到“模型”列表,调用时会因为缺少可用模型而失败:", "模型限制列表": "模型限制列表", + "模式": "模式", + "模板": "模板", + "模板应用失败": "模板应用失败", "模板示例": "模板示例", "模糊搜索模型名称": "模糊搜索模型名称", "次": "次", "欢迎使用,请完成以下设置以开始使用系统": "欢迎使用,请完成以下设置以开始使用系统", "欧元": "欧元", + "正则替换": "正则替换", "正在加载可用部署位置...": "正在加载可用部署位置...", + "正在加载签到状态...": "正在加载签到状态...", + "正在处理,请稍候": "正在处理,请稍候", "正在处理大内容...": "正在处理大内容...", + "正在批量处理,请稍候": "正在批量处理,请稍候", + "正在批量检测,请稍候": "正在批量检测,请稍候", "正在提交": "正在提交", "正在构造请求体预览...": "正在构造请求体预览...", "正在检查 io.net 连接...": "正在检查 io.net 连接...", + "正在检测,请稍候": "正在检测,请稍候", "正在测试第 ${current} - ${end} 个模型 (共 ${total} 个)": "正在测试第 ${current} - ${end} 个模型 (共 ${total} 个)", "正在跟随最新日志": "正在跟随最新日志", "正在跳转 GitHub...": "正在跳转 GitHub...", "正在跳转...": "正在跳转...", + "正常": "正常", + "正常情况下,令牌的计费倍率由令牌所选的分组决定。特殊倍率可以根据「用户所在分组」进一步覆盖这个倍率。": "正常情况下,令牌的计费倍率由令牌所选的分组决定。特殊倍率可以根据「用户所在分组」进一步覆盖这个倍率。", + "正数为增加,负数为减少": "正数为增加,负数为减少", "此代理仅用于图片请求转发,Webhook通知发送等,AI API请求仍然由服务器直接发出,可在渠道设置中单独配置代理": "此代理仅用于图片请求转发,Webhook通知发送等,AI API请求仍然由服务器直接发出,可在渠道设置中单独配置代理", "此修改将不可逆": "此修改将不可逆", "此操作不可恢复,请仔细确认时间后再操作!": "此操作不可恢复,请仔细确认时间后再操作!", @@ -1524,9 +2139,11 @@ "此操作将解绑用户当前的 Passkey,下次登录需要重新注册。": "此操作将解绑用户当前的 Passkey,下次登录需要重新注册。", "此操作将降低用户的权限级别": "此操作将降低用户的权限级别", "此支付方式最低充值金额为": "此支付方式最低充值金额为", + "此时用户创建令牌时只能看到 standard 和 premium:": "此时用户创建令牌时只能看到 standard 和 premium:", "此渠道由 IO.NET 自动同步,类型、密钥和 API 地址已锁定。": "此渠道由 IO.NET 自动同步,类型、密钥和 API 地址已锁定。", "此设置用于系统内部计算,默认值500000是为了精确到6位小数点设计,不推荐修改。": "此设置用于系统内部计算,默认值500000是为了精确到6位小数点设计,不推荐修改。", "此页面仅显示未设置价格或倍率的模型,设置后将自动从列表中移除": "此页面仅显示未设置价格或倍率的模型,设置后将自动从列表中移除", + "此页面仅显示未设置价格或基础倍率的模型,设置后会自动从列表中移出": "此页面仅显示未设置价格或基础倍率的模型,设置后会自动从列表中移出", "此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改": "此项只读,需要用户通过个人设置页面的相关绑定按钮进行绑定,不可直接修改", "此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,例如:": "此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,例如:", "此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,留空则不更改": "此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,留空则不更改", @@ -1534,16 +2151,35 @@ "此项可选,用于覆盖请求参数。不支持覆盖 stream 参数": "此项可选,用于覆盖请求参数。不支持覆盖 stream 参数", "此项可选,用于覆盖请求头参数": "此项可选,用于覆盖请求头参数", "此项可选,用于通过自定义API地址来进行 API 调用,末尾不要带/v1和/": "此项可选,用于通过自定义API地址来进行 API 调用,末尾不要带/v1和/", + "每个充值单位对应的 USD 金额,默认 1.0": "每个充值单位对应的 USD 金额,默认 1.0", + "每个分组代表一个价格档位。管理员创建分组后,可以选择哪些档位对用户开放自选。": "每个分组代表一个价格档位。管理员创建分组后,可以选择哪些档位对用户开放自选。", + "每个用户最多可创建的令牌数量,默认 1000,设置过大可能会影响性能": "每个用户最多可创建的令牌数量,默认 1000,设置过大可能会影响性能", + "每周": "每周", + "每天": "每天", "每容器GPU数": "每容器GPU数", + "每日仅可签到一次,请勿重复签到": "每日仅可签到一次,请勿重复签到", + "每日签到": "每日签到", + "每日签到可获得随机额度奖励": "每日签到可获得随机额度奖励", + "每月": "每月", + "每美元对应 Token 数": "每美元对应 Token 数", "每隔多少分钟测试一次所有通道": "每隔多少分钟测试一次所有通道", "永不过期": "永不过期", "永久删除您的两步验证设置": "永久删除您的两步验证设置", "永久删除所有备用码(包括未使用的)": "永久删除所有备用码(包括未使用的)", + "汇率": "汇率", + "沙盒模式": "沙盒模式", + "沙盒环境 RSA 私钥 Base64 (PKCS#8 DER)": "沙盒环境 RSA 私钥 Base64 (PKCS#8 DER)", + "沙盒环境 Waffo API 密钥": "沙盒环境 Waffo API 密钥", + "沙盒环境 Waffo 公钥 Base64 (X.509 DER)": "沙盒环境 Waffo 公钥 Base64 (X.509 DER)", + "没有匹配的字段": "没有匹配的字段", "没有匹配的日志条目": "没有匹配的日志条目", + "没有匹配的规则": "没有匹配的规则", "没有可用令牌用于填充": "没有可用令牌用于填充", "没有可用模型": "没有可用模型", "没有找到匹配的模型": "没有找到匹配的模型", + "没有未设置定价的模型": "没有未设置定价的模型", "没有未设置的模型": "没有未设置的模型", + "没有条件时,默认总是执行该操作。": "没有条件时,默认总是执行该操作。", "没有模型可以复制": "没有模型可以复制", "没有账户?": "没有账户?", "注 册": "注 册", @@ -1554,11 +2190,15 @@ "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用", "注销": "注销", "注销成功!": "注销成功!", + "活跃文件": "活跃文件", + "活跃缓存数": "活跃缓存数", "流": "流", + "流式": "流式", "流式响应完成": "流式响应完成", "流式输出": "流式输出", - "流式": "流式", + "流状态": "流状态", "流量端口": "流量端口", + "流错误详情": "流错误详情", "浅色": "浅色", "浅色模式": "浅色模式", "测活": "测活", @@ -1568,9 +2208,9 @@ "测试单个渠道操作项目组": "测试单个渠道操作项目组", "测试失败": "测试失败", "测试失败:": "测试失败:", + "测试所有未手动禁用渠道": "测试所有未手动禁用渠道", "测试所有渠道的最长响应时间": "测试所有渠道的最长响应时间", "测试所有通道": "测试所有通道", - "测试所有未手动禁用渠道": "测试所有未手动禁用渠道", "测试模式": "测试模式", "测试连接": "测试连接", "测速": "测速", @@ -1587,17 +2227,22 @@ "深色": "深色", "深色模式": "深色模式", "添加": "添加", + "添加 (+:)": "添加 (+:)", + "添加 OAuth 提供商": "添加 OAuth 提供商", "添加API": "添加API", "添加产品": "添加产品", "添加令牌": "添加令牌", "添加兑换码": "添加兑换码", "添加公告": "添加公告", "添加分类": "添加分类", + "添加分组": "添加分组", + "添加分组规则": "添加分组规则", "添加后提交": "添加后提交", "添加启动参数": "添加启动参数", "添加启动命令": "添加启动命令", "添加密钥环境变量": "添加密钥环境变量", "添加成功": "添加成功", + "添加提供商": "添加提供商", "添加模型": "添加模型", "添加模型区域": "添加模型区域", "添加渠道": "添加渠道", @@ -1607,24 +2252,24 @@ "添加键值对": "添加键值对", "添加问答": "添加问答", "添加额度": "添加额度", - "减少": "减少", - "覆盖": "覆盖", - "调整额度": "调整额度", - "调整额度成功": "调整额度成功", - "当前额度": "当前额度", - "变更": "变更", - "预计结果": "预计结果", - "正数为增加,负数为减少": "正数为增加,负数为减少", + "清理不活跃缓存": "清理不活跃缓存", + "清理失败": "清理失败", "清理方式": "清理方式", "清理日志文件": "清理日志文件", "清空": "清空", + "清空全部缓存": "清空全部缓存", + "清空该规则缓存": "清空该规则缓存", "清空重定向": "清空重定向", + "清除": "清除", "清除历史日志": "清除历史日志", "清除失效兑换码": "清除失效兑换码", "清除所有模型": "清除所有模型", "渠道": "渠道", "渠道 ID": "渠道 ID", "渠道ID,名称,密钥,API地址": "渠道ID,名称,密钥,API地址", + "渠道亲和性": "渠道亲和性", + "渠道亲和性:上游缓存命中": "渠道亲和性:上游缓存命中", + "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。", "渠道优先级": "渠道优先级", "渠道信息": "渠道信息", "渠道创建成功!": "渠道创建成功!", @@ -1642,37 +2287,48 @@ "渠道的模型测试": "渠道的模型测试", "渠道的高级配置选项": "渠道的高级配置选项", "渠道管理": "渠道管理", + "渠道行为": "渠道行为", "渠道额外设置": "渠道额外设置", "源地址": "源地址", + "满足任一条件(OR)": "满足任一条件(OR)", "演示站点": "演示站点", "演示站点模式": "演示站点模式", "点击 + 按钮添加图片URL进行多模态对话": "点击 + 按钮添加图片URL进行多模态对话", "点击\"确认延长\"后将立即扣除费用并延长容器运行时间": "点击\"确认延长\"后将立即扣除费用并延长容器运行时间", "点击上传文件或拖拽文件到这里": "点击上传文件或拖拽文件到这里", "点击下方按钮通过 Telegram 完成绑定": "点击下方按钮通过 Telegram 完成绑定", + "点击处理删除模型": "点击处理删除模型", + "点击处理新增模型": "点击处理新增模型", "点击复制ID": "点击复制ID", "点击复制模型名称": "点击复制模型名称", "点击查看差异": "点击查看差异", "点击此处": "点击此处", "点击预览视频": "点击预览视频", "点击预览音乐": "点击预览音乐", - "音乐预览": "音乐预览", - "音频无法播放": "音频无法播放", "点击验证按钮,使用您的生物特征或安全密钥": "点击验证按钮,使用您的生物特征或安全密钥", "版权所有": "版权所有", + "特殊倍率": "特殊倍率", + "特殊可用分组规则": "特殊可用分组规则", "状态": "状态", + "状态码": "状态码", "状态码复写": "状态码复写", "状态码复写包含无效的状态码": "状态码复写包含无效的状态码", "状态筛选": "状态筛选", "状态页面Slug": "状态页面Slug", "环境变量": "环境变量", + "生产环境 RSA 私钥 Base64 (PKCS#8 DER)": "生产环境 RSA 私钥 Base64 (PKCS#8 DER)", + "生产环境 Waffo API 密钥": "生产环境 Waffo API 密钥", + "生产环境 Waffo 公钥 Base64 (X.509 DER)": "生产环境 Waffo 公钥 Base64 (X.509 DER)", "生成令牌": "生成令牌", + "生成并填入": "生成并填入", "生成数量": "生成数量", "生成数量必须大于0": "生成数量必须大于0", "生成新的备用码": "生成新的备用码", "生成歌词": "生成歌词", "生成音乐": "生成音乐", + "生效": "生效", "用于API调用的身份验证令牌,请妥善保管": "用于API调用的身份验证令牌,请妥善保管", + "用于唯一标识用户的字段路径": "用于唯一标识用户的字段路径", "用于配置网络代理,支持 socks5 协议": "用于配置网络代理,支持 socks5 协议", "用于验证回调 new-api 的 webhook 请求的密钥,敏感信息不显示": "用于验证回调 new-api 的 webhook 请求的密钥,敏感信息不显示", "用以支持基于 WebAuthn 的无密码登录注册": "用以支持基于 WebAuthn 的无密码登录注册", @@ -1686,34 +2342,57 @@ "用以支持通过微信进行登录注册": "用以支持通过微信进行登录注册", "用以防止恶意用户利用临时邮箱批量注册": "用以防止恶意用户利用临时邮箱批量注册", "用户": "用户", + "用户 ID 字段": "用户 ID 字段", + "用户 ID 字段(可选)": "用户 ID 字段(可选)", "用户个人功能": "用户个人功能", "用户主页,展示系统信息": "用户主页,展示系统信息", "用户优先:如果用户在请求中指定了系统提示词,将优先使用用户的设置": "用户优先:如果用户在请求中指定了系统提示词,将优先使用用户的设置", "用户信息": "用户信息", "用户信息更新成功!": "用户信息更新成功!", + "用户信息端点": "用户信息端点", + "用户信息缺失": "用户信息缺失", + "用户最大令牌数量": "用户最大令牌数量", "用户分组": "用户分组", "用户分组和额度管理": "用户分组和额度管理", + "用户分组的联动作用": "用户分组的联动作用", "用户分组配置": "用户分组配置", + "用户创建令牌 → 选择分组下拉框:": "用户创建令牌 → 选择分组下拉框:", + "用户创建令牌时选择的分组,决定该令牌的实际计费倍率。一个用户可以创建多个令牌,使用不同分组。": "用户创建令牌时选择的分组,决定该令牌的实际计费倍率。一个用户可以创建多个令牌,使用不同分组。", "用户协议": "用户协议", "用户协议已更新": "用户协议已更新", "用户协议更新失败": "用户协议更新失败", + "用户可选": "用户可选", "用户可选分组": "用户可选分组", + "用户可选分组的名称和描述(只包含勾选了用户可选的分组)": "用户可选分组的名称和描述(只包含勾选了用户可选的分组)", "用户名": "用户名", + "用户名字段": "用户名字段", + "用户名字段(可选)": "用户名字段(可选)", "用户名或邮箱": "用户名或邮箱", "用户名称": "用户名称", + "用户在充值页面看到的支付方式名称,例如:Credit Card": "用户在充值页面看到的支付方式名称,例如:Credit Card", "用户控制面板,管理账户": "用户控制面板,管理账户", "用户新建令牌时可选的分组,格式为 JSON 字符串,例如:{\"vip\": \"VIP 用户\", \"test\": \"测试\"},表示用户可以选择 vip 分组和 test 分组": "用户新建令牌时可选的分组,格式为 JSON 字符串,例如:{\"vip\": \"VIP 用户\", \"test\": \"测试\"},表示用户可以选择 vip 分组和 test 分组", "用户每周期最多请求完成次数": "用户每周期最多请求完成次数", "用户每周期最多请求次数": "用户每周期最多请求次数", "用户注册时看到的网站名称,比如'我的网站'": "用户注册时看到的网站名称,比如'我的网站'", + "用户消耗排行": "用户消耗排行", + "用户消耗趋势": "用户消耗趋势", "用户的基本账户信息": "用户的基本账户信息", "用户管理": "用户管理", "用户组": "用户组", + "用户订阅管理": "用户订阅管理", "用户账户创建成功!": "用户账户创建成功!", "用户账户管理": "用户账户管理", "用时/首字": "用时/首字", + "由全站货币展示设置统一控制": "由全站货币展示设置统一控制", + "由管理员分配,决定用户身份等级(如 default、vip)。": "由管理员分配,决定用户身份等级(如 default、vip)。", + "由订阅抵扣": "由订阅抵扣", + "界面语言和其他个人偏好": "界面语言和其他个人偏好", + "留空使用系统临时目录": "留空使用系统临时目录", "留空则使用账号绑定的邮箱": "留空则使用账号绑定的邮箱", "留空则使用默认端点;支持 {path, method}": "留空则使用默认端点;支持 {path, method}", + "留空则保持原有密钥": "留空则保持原有密钥", + "留空则自动使用 服务器地址 + /api/waffo/webhook": "留空则自动使用 服务器地址 + /api/waffo/webhook", "留空则默认使用服务器地址,注意不能携带http://或者https://": "留空则默认使用服务器地址,注意不能携带http://或者https://", "登 录": "登 录", "登录": "登录", @@ -1722,8 +2401,15 @@ "白名单": "白名单", "的前提下使用。": "的前提下使用。", "监控设置": "监控设置", + "目录总大小": "目录总大小", + "目录文件数": "目录文件数", + "目标分组": "目标分组", "目标用户:{{username}}": "目标用户:{{username}}", + "目标端点": "目标端点", + "目标路径(可选)": "目标路径(可选)", "直接提交": "直接提交", + "直接编辑 JSON 文本,保存时会校验格式。": "直接编辑 JSON 文本,保存时会校验格式。", + "直接追加(和添加类似,但无前缀)": "直接追加(和添加类似,但无前缀)", "相关项目": "相关项目", "相当于删除用户,此修改将不可逆": "相当于删除用户,此修改将不可逆", "矛盾": "矛盾", @@ -1746,6 +2432,7 @@ "确定是否要复制此渠道?": "确定是否要复制此渠道?", "确定是否要注销此用户?": "确定是否要注销此用户?", "确定清除所有失效兑换码?": "确定清除所有失效兑换码?", + "确定要仅检测全部渠道上游模型更新吗?(不执行新增/删除)": "确定要仅检测全部渠道上游模型更新吗?(不执行新增/删除)", "确定要修改所有子渠道优先级为 ": "确定要修改所有子渠道优先级为 ", "确定要修改所有子渠道权重为 ": "确定要修改所有子渠道权重为 ", "确定要充值 $": "确定要充值 $", @@ -1753,44 +2440,68 @@ "确定要删除所有已自动禁用的密钥吗?": "确定要删除所有已自动禁用的密钥吗?", "确定要删除所选的 {{count}} 个令牌吗?_other": "确定要删除所选的 {{count}} 个令牌吗?", "确定要删除所选的 {{count}} 个模型吗?_other": "确定要删除所选的 {{count}} 个模型吗?", + "确定要删除此 OAuth 提供商吗?": "确定要删除此 OAuth 提供商吗?", "确定要删除此API信息吗?": "确定要删除此API信息吗?", "确定要删除此公告吗?": "确定要删除此公告吗?", "确定要删除此分类吗?": "确定要删除此分类吗?", "确定要删除此密钥吗?": "确定要删除此密钥吗?", "确定要删除此问答吗?": "确定要删除此问答吗?", + "确定要删除该提供商吗?": "确定要删除该提供商吗?", "确定要删除这条消息吗?": "确定要删除这条消息吗?", "确定要删除选中的": "确定要删除选中的", "确定要启用所有密钥吗?": "确定要启用所有密钥吗?", "确定要启用此用户吗?": "确定要启用此用户吗?", + "确定要对全部渠道执行上游模型更新吗?": "确定要对全部渠道执行上游模型更新吗?", "确定要提升此用户吗?": "确定要提升此用户吗?", "确定要更新所有已启用通道余额吗?": "确定要更新所有已启用通道余额吗?", - "确定要测试所有通道吗?": "确定要测试所有通道吗?", "确定要测试所有未手动禁用渠道吗?": "确定要测试所有未手动禁用渠道吗?", + "确定要测试所有通道吗?": "确定要测试所有通道吗?", "确定要禁用所有的密钥吗?": "确定要禁用所有的密钥吗?", "确定要禁用此用户吗?": "确定要禁用此用户吗?", + "确定要解绑 {{name}} 吗?": "确定要解绑 {{name}} 吗?", "确定要降级此用户吗?": "确定要降级此用户吗?", "确定重置": "确定重置", "确定重置模型倍率吗?": "确定重置模型倍率吗?", "确认": "确认", + "确认作废": "确认作废", + "确认关闭提示": "确认关闭提示", "确认冲突项修改": "确认冲突项修改", "确认删除": "确认删除", "确认删除模型": "确认删除模型", + "确认删除该分组?": "确认删除该分组?", + "确认删除该分组的所有规则?": "确认删除该分组的所有规则?", + "确认删除该规则?": "确认删除该规则?", "确认取消密码登录": "确认取消密码登录", + "确认启用": "确认启用", "确认密码": "确认密码", "确认导入配置": "确认导入配置", "确认延长": "确认延长", "确认延长容器时长": "确认延长容器时长", "确认操作": "确认操作", "确认新密码": "确认新密码", + "确认清理不活跃的磁盘缓存?": "确认清理不活跃的磁盘缓存?", "确认清理日志文件?": "确认清理日志文件?", + "确认清空全部渠道亲和性缓存": "确认清空全部渠道亲和性缓存", + "确认清空该规则缓存": "确认清空该规则缓存", "确认清除历史日志": "确认清除历史日志", "确认禁用": "确认禁用", + "确认移除?": "确认移除?", "确认补单": "确认补单", + "确认解绑": "确认解绑", "确认解绑 Passkey": "确认解绑 Passkey", "确认设置并完成初始化": "确认设置并完成初始化", "确认重置 Passkey": "确认重置 Passkey", "确认重置两步验证": "确认重置两步验证", "确认重置密码": "确认重置密码", + "磁盘 阈值 (%)": "磁盘 阈值 (%)", + "磁盘使用率超过此值时拒绝请求": "磁盘使用率超过此值时拒绝请求", + "磁盘可用空间小于缓存最大总量设置": "磁盘可用空间小于缓存最大总量设置", + "磁盘命中": "磁盘命中", + "磁盘缓存最大总量 (MB)": "磁盘缓存最大总量 (MB)", + "磁盘缓存占用的最大空间": "磁盘缓存占用的最大空间", + "磁盘缓存已清理": "磁盘缓存已清理", + "磁盘缓存设置(磁盘换内存)": "磁盘缓存设置(磁盘换内存)", + "磁盘缓存阈值 (MB)": "磁盘缓存阈值 (MB)", "示例": "示例", "示例:{\"default\": [200, 100], \"vip\": [0, 1000]}。": "示例:{\"default\": [200, 100], \"vip\": [0, 1000]}。", "视频": "视频", @@ -1802,6 +2513,7 @@ "禁用两步验证": "禁用两步验证", "禁用全部": "禁用全部", "禁用原因": "禁用原因", + "禁用后用户端不再展示,但历史订单不受影响。是否继续?": "禁用后用户端不再展示,但历史订单不受影响。是否继续?", "禁用后的影响:": "禁用后的影响:", "禁用密钥失败": "禁用密钥失败", "禁用思考处理的模型列表": "禁用思考处理的模型列表", @@ -1812,40 +2524,82 @@ "私有镜像仓库的密码": "私有镜像仓库的密码", "私有镜像仓库的用户名": "私有镜像仓库的用户名", "秒": "秒", + "移动": "移动", + "移动请求头": "移动请求头", + "移除": "移除", + "移除 (-:)": "移除 (-:)", "移除 functionResponse.id 字段": "移除 functionResponse.id 字段", "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目", + "空": "空", "窗口处理": "窗口处理", "窗口等待": "窗口等待", + "立即签到": "立即签到", + "立即订阅": "立即订阅", + "站点所有额度将以原始 Token 数显示,不做货币换算": "站点所有额度将以原始 Token 数显示,不做货币换算", + "站点所有额度将以美元 ($) 显示": "站点所有额度将以美元 ($) 显示", + "站点所有额度将按汇率换算为人民币 (¥) 显示": "站点所有额度将按汇率换算为人民币 (¥) 显示", + "站点所有额度将按汇率换算为自定义货币显示": "站点所有额度将按汇率换算为自定义货币显示", "站点额度展示类型及汇率": "站点额度展示类型及汇率", "端口号必须在1-65535之间": "端口号必须在1-65535之间", "端口配置详细说明": "限制外部请求只能访问指定端口。支持单个端口(80, 443)或端口范围(8000-8999)。空列表允许所有端口。默认包含常用Web端口。", "端点": "端点", + "端点 URL 必须以 http:// 或 https:// 开头:": "端点 URL 必须以 http:// 或 https:// 开头:", + "端点 URL 必须是完整地址(以 http:// 或 https:// 开头)": "端点 URL 必须是完整地址(以 http:// 或 https:// 开头)", "端点映射": "端点映射", - "在模型广场向用户展示的端点": "在模型广场向用户展示的端点", "端点类型": "端点类型", "端点组": "端点组", + "第 {{line}} 条 prune_objects 缺少条件": "第 {{line}} 条 prune_objects 缺少条件", + "第 {{line}} 条 prune_objects 需要至少一个匹配条件": "第 {{line}} 条 prune_objects 需要至少一个匹配条件", + "第 {{line}} 条 return_error 需要 message 字段": "第 {{line}} 条 return_error 需要 message 字段", + "第 {{line}} 条操作缺少值": "第 {{line}} 条操作缺少值", + "第 {{line}} 条操作缺少来源字段": "第 {{line}} 条操作缺少来源字段", + "第 {{line}} 条操作缺少目标字段": "第 {{line}} 条操作缺少目标字段", + "第 {{line}} 条操作缺少目标路径": "第 {{line}} 条操作缺少目标路径", + "第 {{line}} 条请求头透传格式无效": "第 {{line}} 条请求头透传格式无效", + "第 {{line}} 条请求头透传缺少请求头名称": "第 {{line}} 条请求头透传缺少请求头名称", + "第三方支付配置": "第三方支付配置", "第三方账户绑定状态(只读)": "第三方账户绑定状态(只读)", "等价金额:": "等价金额:", "等待中": "等待中", "等待获取邮箱信息...": "等待获取邮箱信息...", "筛选": "筛选", + "签到最大额度": "签到最大额度", + "签到最小额度": "签到最小额度", + "签到功能允许用户每日签到获取随机额度奖励": "签到功能允许用户每日签到获取随机额度奖励", + "签到失败": "签到失败", + "签到奖励将直接添加到您的账户余额": "签到奖励将直接添加到您的账户余额", + "签到奖励的最大额度": "签到奖励的最大额度", + "签到奖励的最小额度": "签到奖励的最小额度", + "签到成功!获得": "签到成功!获得", + "签到设置": "签到设置", + "简单来说:同一个令牌分组,不同等级的用户可以享受不同的价格。": "简单来说:同一个令牌分组,不同等级的用户可以享受不同的价格。", + "简洁": "简洁", + "简洁模式:按 type 全量清理对象,例如 redacted_thinking。": "简洁模式:按 type 全量清理对象,例如 redacted_thinking。", + "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。": "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。", "管理": "管理", "管理 Ollama 模型的拉取和删除": "管理 Ollama 模型的拉取和删除", "管理你的 LinuxDO OAuth App": "管理你的 LinuxDO OAuth App", "管理员": "管理员", + "管理员分配的优惠分组": "管理员分配的优惠分组", + "管理员分配的基础分组": "管理员分配的基础分组", "管理员区域": "管理员区域", "管理员暂时未设置任何关于内容": "管理员暂时未设置任何关于内容", "管理员未开启 Creem 充值!": "管理员未开启 Creem 充值!", "管理员未开启Stripe充值!": "管理员未开启Stripe充值!", "管理员未开启在线充值!": "管理员未开启在线充值!", "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。", + "管理员未开启在线支付功能,请联系管理员配置。": "管理员未开启在线支付功能,请联系管理员配置。", "管理员未设置用户可选分组": "管理员未设置用户可选分组", + "管理员给用户分配的分组(如 vip)不仅决定用户身份,还会影响后续两个功能:": "管理员给用户分配的分组(如 vip)不仅决定用户身份,还会影响后续两个功能:", "管理员设置了外部链接,点击下方按钮访问": "管理员设置了外部链接,点击下方按钮访问", "管理员账号": "管理员账号", "管理员账号已经初始化过,请继续设置其他参数": "管理员账号已经初始化过,请继续设置其他参数", - "管理模型、标签、端点等预填组": "管理模型、标签、端点等预填组", "管理服务器运行日志文件。日志文件会随运行时间不断累积,建议定期清理以释放磁盘空间。": "管理服务器运行日志文件。日志文件会随运行时间不断累积,建议定期清理以释放磁盘空间。", + "管理模型、标签、端点等预填组": "管理模型、标签、端点等预填组", + "管理用户已绑定的第三方账户,支持筛选与解绑": "管理用户已绑定的第三方账户,支持筛选与解绑", + "管理绑定": "管理绑定", "类型": "类型", + "类型(常用)": "类型(常用)", "粘贴图片失败": "粘贴图片失败", "精确": "精确", "系统": "系统", @@ -1854,6 +2608,10 @@ "系统信息": "系统信息", "系统公告": "系统公告", "系统公告管理,可以发布系统通知和重要消息(最多100个,前端显示最新20条)": "系统公告管理,可以发布系统通知和重要消息(最多100个,前端显示最新20条)", + "系统内存": "系统内存", + "系统内部以美元 (USD) 为基准计价。用户余额、充值金额、模型定价、用量日志等所有金额显示均按此汇率换算为人民币,不影响内部计费": "系统内部以美元 (USD) 为基准计价。用户余额、充值金额、模型定价、用量日志等所有金额显示均按此汇率换算为人民币,不影响内部计费", + "系统内部以美元 (USD) 为基准计价。用户余额、充值金额、模型定价、用量日志等所有金额显示均按此汇率换算为自定义货币,不影响内部计费": "系统内部以美元 (USD) 为基准计价。用户余额、充值金额、模型定价、用量日志等所有金额显示均按此汇率换算为自定义货币,不影响内部计费", + "系统内部计费精度,默认 500000,修改可能导致计费异常,请谨慎操作": "系统内部计费精度,默认 500000,修改可能导致计费异常,请谨慎操作", "系统初始化": "系统初始化", "系统初始化失败,请重试": "系统初始化失败,请重试", "系统初始化成功,正在跳转...": "系统初始化成功,正在跳转...", @@ -1862,29 +2620,23 @@ "系统名称已更新": "系统名称已更新", "系统名称更新失败": "系统名称更新失败", "系统已为该部署准备 Ollama 镜像与随机 API Key": "系统已为该部署准备 Ollama 镜像与随机 API Key", + "系统性能监控": "系统性能监控", "系统提示覆盖": "系统提示覆盖", "系统提示词": "系统提示词", "系统提示词拼接": "系统提示词拼接", "系统数据统计": "系统数据统计", "系统文档和帮助信息": "系统文档和帮助信息", "系统消息": "系统消息", + "系统版本": "系统版本", "系统管理功能": "系统管理功能", - "系统性能监控": "系统性能监控", - "启用性能监控后,当系统资源使用率超过设定阈值时,将拒绝新的 Relay 请求 (/v1, /v1beta 等),以保护系统稳定性。": "启用性能监控后,当系统资源使用率超过设定阈值时,将拒绝新的 Relay 请求 (/v1, /v1beta 等),以保护系统稳定性。", - "启用性能监控": "启用性能监控", - "超过阈值时拒绝新请求": "超过阈值时拒绝新请求", - "CPU 阈值 (%)": "CPU 阈值 (%)", - "CPU 使用率超过此值时拒绝请求": "CPU 使用率超过此值时拒绝请求", - "内存 阈值 (%)": "内存 阈值 (%)", - "内存使用率超过此值时拒绝请求": "内存使用率超过此值时拒绝请求", - "磁盘 阈值 (%)": "磁盘 阈值 (%)", - "磁盘使用率超过此值时拒绝请求": "磁盘使用率超过此值时拒绝请求", - "保存性能设置": "保存性能设置", "系统设置": "系统设置", "系统访问令牌": "系统访问令牌", "约": "约", "索引": "索引", "紧凑列表": "紧凑列表", + "纯字符串会直接覆盖整条请求头,或者点击“查看 JSON 示例”按 token 规则处理。": "纯字符串会直接覆盖整条请求头,或者点击“查看 JSON 示例”按 token 规则处理。", + "累计签到": "累计签到", + "累计获得": "累计获得", "线路描述": "线路描述", "组列表": "组列表", "组名": "组名", @@ -1895,20 +2647,26 @@ "绑定": "绑定", "绑定 Telegram": "绑定 Telegram", "绑定信息": "绑定信息", + "绑定后会立即生成用户订阅(无需支付),有效期按套餐配置计算。": "绑定后会立即生成用户订阅(无需支付),有效期按套餐配置计算。", "绑定微信账户": "绑定微信账户", "绑定成功!": "绑定成功!", + "绑定订阅套餐": "绑定订阅套餐", "绑定邮箱地址": "绑定邮箱地址", + "结束": "结束", "结束时间": "结束时间", "结果图片": "结果图片", + "结算差额": "结算差额", "绘图": "绘图", "绘图任务记录": "绘图任务记录", "绘图日志": "绘图日志", "绘图设置": "绘图设置", "统一的": "统一的", "统计Tokens": "统计Tokens", + "统计已重置": "统计已重置", "统计次数": "统计次数", "统计额度": "统计额度", "继续": "继续", + "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})", "缓存 Tokens": "缓存 Tokens", "缓存: {{cacheRatio}}": "缓存: {{cacheRatio}}", @@ -1916,19 +2674,40 @@ "缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})", "缓存倍率": "缓存倍率", "缓存倍率 {{cacheRatio}}": "缓存倍率 {{cacheRatio}}", + "缓存写": "缓存写", + "缓存创建 {{price}} / 1M tokens": "缓存创建 {{price}} / 1M tokens", + "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})", "缓存创建 Tokens": "缓存创建 Tokens", "缓存创建: {{cacheCreationRatio}}": "缓存创建: {{cacheCreationRatio}}", "缓存创建: 1h {{cacheCreationRatio1h}}": "缓存创建: 1h {{cacheCreationRatio1h}}", "缓存创建: 5m {{cacheCreationRatio5m}}": "缓存创建: 5m {{cacheCreationRatio5m}}", "缓存创建: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "缓存创建: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", + "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "缓存创建价格": "缓存创建价格", + "缓存创建价格 {{symbol}}{{price}} / 1M tokens": "缓存创建价格 {{symbol}}{{price}} / 1M tokens", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})", + "缓存创建价格:{{symbol}}{{price}} / 1M tokens": "缓存创建价格:{{symbol}}{{price}} / 1M tokens", "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens", + "缓存创建倍率": "缓存创建倍率", "缓存创建倍率 {{cacheCreationRatio}}": "缓存创建倍率 {{cacheCreationRatio}}", "缓存创建倍率 1h {{cacheCreationRatio1h}}": "缓存创建倍率 1h {{cacheCreationRatio1h}}", "缓存创建倍率 5m {{cacheCreationRatio5m}}": "缓存创建倍率 5m {{cacheCreationRatio5m}}", "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", + "缓存条目数": "缓存条目数", + "缓存目录": "缓存目录", + "缓存目录磁盘空间": "缓存目录磁盘空间", + "缓存读": "缓存读", + "缓存读 {{price}} / 1M tokens": "缓存读 {{price}} / 1M tokens", + "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "缓存读取价格": "缓存读取价格", + "缓存读取价格 {{symbol}}{{price}} / 1M tokens": "缓存读取价格 {{symbol}}{{price}} / 1M tokens", + "缓存读取价格:{{symbol}}{{price}} / 1M tokens": "缓存读取价格:{{symbol}}{{price}} / 1M tokens", + "缓存读取价格:{{symbol}}{{total}} / 1M tokens": "缓存读取价格:{{symbol}}{{total}} / 1M tokens", + "缓存读取倍率": "缓存读取倍率", + "缓存输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "缓存输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "编辑": "编辑", + "编辑 OAuth 提供商": "编辑 OAuth 提供商", "编辑API": "编辑API", "编辑产品": "编辑产品", "编辑供应商": "编辑供应商", @@ -1936,11 +2715,15 @@ "编辑公告内容": "编辑公告内容", "编辑分类": "编辑分类", "编辑成功": "编辑成功", + "编辑提供商": "编辑提供商", + "编辑支付方式": "编辑支付方式", + "编辑方式": "编辑方式", "编辑标签": "编辑标签", "编辑模型": "编辑模型", "编辑模式": "编辑模式", "编辑用户": "编辑用户", "编辑聊天配置": "编辑聊天配置", + "编辑规则": "编辑规则", "编辑问答": "编辑问答", "缩词": "缩词", "缺省 MaxTokens": "缺省 MaxTokens", @@ -1961,66 +2744,56 @@ "聊天链接配置错误,请联系管理员": "聊天链接配置错误,请联系管理员", "联系我们": "联系我们", "腾讯混元": "腾讯混元", + "自动分组": "自动分组", "自动分组auto,从第一个开始选择": "自动分组auto,从第一个开始选择", + "自动分组选择": "自动分组选择", "自动刷新": "自动刷新", "自动刷新中": "自动刷新中", + "自动填充字段": "自动填充字段", + "自动填入": "自动填入", + "自动检测": "自动检测", "自动模式": "自动模式", "自动测试所有通道间隔时间": "自动测试所有通道间隔时间", + "自动生成:": "自动生成:", "自动禁用": "自动禁用", "自动禁用关键词": "自动禁用关键词", "自动禁用状态码": "自动禁用状态码", "自动禁用状态码格式不正确": "自动禁用状态码格式不正确", + "自动选择": "自动选择", "自动重试状态码": "自动重试状态码", "自动重试状态码格式不正确": "自动重试状态码格式不正确", - "支持填写单个状态码或范围(含首尾),使用逗号分隔": "支持填写单个状态码或范围(含首尾),使用逗号分隔", - "支持填写单个状态码或范围(含首尾),使用逗号分隔;504 和 524 始终不重试,不受此处配置影响": "支持填写单个状态码或范围(含首尾),使用逗号分隔;504 和 524 始终不重试,不受此处配置影响", - "高危操作确认": "高危操作确认", - "检测到以下高危状态码重定向规则": "检测到以下高危状态码重定向规则", - "操作确认": "操作确认", - "我确认开启高危重试": "我确认开启高危重试", - "高危状态码重试风险告知与免责声明Markdown": "### ⚠️ 高危操作:504/524 状态码重试风险告知与免责声明\n本项目默认对 `400 (请求错误)`、`504 (网关超时)`和 `524 (cdn发生超时)`状态码不进行重试。\n504 和 524 错误通常意味着**请求已成功送达上游 AI 服务,且上游正在处理,但因处理时间过长导致连接断开**。\n\n开启对此类超时状态码的重定向/重试属于**极高风险操作**。作为本开源项目的使用者,在开启该功能前,您必须仔细阅读并知悉以下严重后果:\n\n#### 一、 核心风险告知(请仔细阅读)\n1. 💸 双重/多重计费风险: 绝大多数 AI 上游厂商对于已经开始处理但因网络原因中断(504/524)的请求**依然会进行扣费**。此时若触发重试,将会向上游发起全新请求,导致您被**双重甚至多重计费**。\n2. ⏳ 客户端严重超时: 单次请求已经触发超时,叠加重试机制将会使总请求耗时成倍增加,导致您的最终客户端(或调用方)出现严重甚至完全无法接受的超时现象。\n3. 💥 请求积压与系统崩溃风险: 强制重试超时请求会长时间占用系统线程和连接数。在高并发场景下,这会导致严重的**请求积压**,进而耗尽系统资源,引发雪崩效应,导致您的整个代理服务崩溃。\n\n#### 二、 风险确认声明\n如果您坚持开启该功能,即代表您作出以下确认:", - "高危状态码重试风险确认输入文本": "我已了解多重计费与崩溃风险,确认开启", - "高危状态码重试风险确认项1": "我已充分阅读并理解:本人已完整阅读上述全部风险提示,完全理解强制重试 504 和 524 状态码可能带来的破坏性后果。", - "高危状态码重试风险确认项2": "我已与上游沟通并确认:本人确认,当前出现的超时问题属于上游服务的瓶颈。本人已与上游提供商进行过沟通,确认上游无法解决该超时问题,因此才采取强制重试方案作为妥协手段。", - "高危状态码重试风险确认项3": "我自愿承担计费损失:本人知晓并接受由此产生的全部双重/多重计费风险,承诺不会因重试导致的账单异常在本项目仓库中提交 Issue 或抱怨。", - "高危状态码重试风险确认项4": "我自愿承担系统稳定性风险:本人知晓该操作可能导致客户端严重超时及服务崩溃。若因本人开启此功能导致请求积压或服务不可用,后果由本人自行承担。", - "高危状态码重试风险输入框占位文案": "请完整输入上方文字", - "高危状态码重试风险输入不匹配提示": "输入内容与要求不一致", - "例如:401, 403, 429, 500-599": "例如:401,403,429,500-599", - "自动选择": "自动选择", + "自定义": "自定义", + "自定义 JSON": "自定义 JSON", + "自定义 OAuth 提供商": "自定义 OAuth 提供商", "自定义充值数量选项": "自定义充值数量选项", "自定义充值数量选项不是合法的 JSON 数组": "自定义充值数量选项不是合法的 JSON 数组", "自定义变焦-提交": "自定义变焦-提交", "自定义模型名称": "自定义模型名称", "自定义模式下不可用": "自定义模式下不可用", + "自定义秒数": "自定义秒数", "自定义请求体模式": "自定义请求体模式", "自定义货币": "自定义货币", "自定义货币符号": "自定义货币符号", "自定义货币符号将显示在所有额度数值前,例如 €1.50": "自定义货币符号将显示在所有额度数值前,例如 €1.50", - "额度展示类型": "额度展示类型", - "站点所有额度将以美元 ($) 显示": "站点所有额度将以美元 ($) 显示", - "站点所有额度将按汇率换算为人民币 (¥) 显示": "站点所有额度将按汇率换算为人民币 (¥) 显示", - "站点所有额度将以原始 Token 数显示,不做货币换算": "站点所有额度将以原始 Token 数显示,不做货币换算", - "站点所有额度将按汇率换算为自定义货币显示": "站点所有额度将按汇率换算为自定义货币显示", - "汇率": "汇率", - "每美元对应 Token 数": "每美元对应 Token 数", - "预览效果": "预览效果", - "请输入汇率": "请输入汇率", - "系统内部以美元 (USD) 为基准计价。用户余额、充值金额、模型定价、用量日志等所有金额显示均按此汇率换算为人民币,不影响内部计费": "系统内部以美元 (USD) 为基准计价。用户余额、充值金额、模型定价、用量日志等所有金额显示均按此汇率换算为人民币,不影响内部计费", - "系统内部计费精度,默认 500000,修改可能导致计费异常,请谨慎操作": "系统内部计费精度,默认 500000,修改可能导致计费异常,请谨慎操作", - "系统内部以美元 (USD) 为基准计价。用户余额、充值金额、模型定价、用量日志等所有金额显示均按此汇率换算为自定义货币,不影响内部计费": "系统内部以美元 (USD) 为基准计价。用户余额、充值金额、模型定价、用量日志等所有金额显示均按此汇率换算为自定义货币,不影响内部计费", + "自定义错误响应": "自定义错误响应", "自定义镜像": "自定义镜像", "自用模式": "自用模式", "自适应列表": "自适应列表", + "至": "至", "节省": "节省", "花费": "花费", "花费时间": "花费时间", "若你的 OIDC Provider 支持 Discovery Endpoint,你可以仅填写 OIDC Well-Known URL,系统会自动获取 OIDC 配置": "若你的 OIDC Provider 支持 Discovery Endpoint,你可以仅填写 OIDC Well-Known URL,系统会自动获取 OIDC 配置", + "获取 Discovery 配置": "获取 Discovery 配置", + "获取 Discovery 配置失败:": "获取 Discovery 配置失败:", "获取 io.net API Key": "获取 io.net API Key", "获取 OIDC 配置失败,请检查网络状况和 Well-Known URL 是否正确": "获取 OIDC 配置失败,请检查网络状况和 Well-Known URL 是否正确", "获取 OIDC 配置成功!": "获取 OIDC 配置成功!", "获取 Ollama 版本失败": "获取 Ollama 版本失败", "获取2FA状态失败": "获取2FA状态失败", + "获取令牌密钥失败": "获取令牌密钥失败", + "获取充值配置失败": "获取充值配置失败", + "获取充值配置异常": "获取充值配置异常", "获取初始化状态失败": "获取初始化状态失败", "获取可用资源失败: ": "获取可用资源失败: ", "获取启用模型失败": "获取启用模型失败", @@ -2037,43 +2810,80 @@ "获取模型列表失败": "获取模型列表失败", "获取渠道失败:": "获取渠道失败:", "获取硬件类型失败: ": "获取硬件类型失败: ", + "获取签到状态失败": "获取签到状态失败", "获取组列表失败": "获取组列表失败", + "获取绑定信息失败": "获取绑定信息失败", + "获取自定义 OAuth 提供商列表失败": "获取自定义 OAuth 提供商列表失败", "获取详情失败": "获取详情失败", "获取部署列表失败": "获取部署列表失败", "获取金额失败": "获取金额失败", "获取验证码": "获取验证码", + "获得": "获得", "补全": "补全", + "补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "补全 {{completion}} tokens * 输出倍率 {{completionRatio}}", "补全 {{completion}} tokens / 1M tokens * {{symbol}}{{price}}": "补全 {{completion}} tokens / 1M tokens * {{symbol}}{{price}}", + "补全价格": "补全价格", "补全价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "补全价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})", "补全价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens": "补全价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens", + "补全价格已锁定": "补全价格已锁定", "补全倍率": "补全倍率", + "补全倍率 {{completionRatio}}": "补全倍率 {{completionRatio}}", "补全倍率值": "补全倍率值", "补单": "补单", "补单失败": "补单失败", "补单成功": "补单成功", "表单引用错误,请刷新页面重试": "表单引用错误,请刷新页面重试", "表格视图": "表格视图", + "覆盖": "覆盖", "覆盖模式:将完全替换现有的所有密钥": "覆盖模式:将完全替换现有的所有密钥", + "覆盖模板": "覆盖模板", "覆盖现有密钥": "覆盖现有密钥", + "规则": "规则", + "规则 JSON": "规则 JSON", + "规则 JSON 格式不正确": "规则 JSON 格式不正确", + "规则 ttl_seconds 为 0 时使用。0 表示使用后端默认 TTL:3600 秒。": "规则 ttl_seconds 为 0 时使用。0 表示使用后端默认 TTL:3600 秒。", + "规则为 JSON 数组;可视化与 JSON 模式共用同一份数据。": "规则为 JSON 数组;可视化与 JSON 模式共用同一份数据。", + "规则名称(可读性更好,也会出现在管理侧日志中)。": "规则名称(可读性更好,也会出现在管理侧日志中)。", + "规则导航": "规则导航", + "规则描述(可选)": "规则描述(可选)", + "规则未找到,请刷新后重试": "规则未找到,请刷新后重试", "角色": "角色", "解析响应数据时发生错误": "解析响应数据时发生错误", "解析密钥文件失败: {{msg}}": "解析密钥文件失败: {{msg}}", "解析错误": "解析错误", + "解绑": "解绑", "解绑 Passkey": "解绑 Passkey", "解绑后将无法使用 Passkey 登录,确定要继续吗?": "解绑后将无法使用 Passkey 登录,确定要继续吗?", + "解绑成功": "解绑成功", "计价币种": "计价币种", "计算中": "计算中", "计算成本": "计算成本", "计算费用中...": "计算费用中...", + "计费乘数,倍率越低费用越低。例如倍率 0.5 表示半价。": "计费乘数,倍率越低费用越低。例如倍率 0.5 表示半价。", "计费开始": "计费开始", + "计费摘要": "计费摘要", + "计费方式": "计费方式", + "计费显示模式": "计费显示模式", "计费模式": "计费模式", "计费类型": "计费类型", "计费过程": "计费过程", "订单号": "订单号", + "订单支付方式": "订单支付方式", + "订阅": "订阅", + "订阅剩余": "订阅剩余", + "订阅套餐": "订阅套餐", + "订阅套餐管理": "订阅套餐管理", + "订阅实例": "订阅实例", + "订阅抵扣": "订阅抵扣", + "订阅管理": "订阅管理", + "订阅结算": "订阅结算", + "订阅说明": "订阅说明", + "认证方式": "认证方式", "讯飞星火": "讯飞星火", "记录请求与错误日志IP": "记录请求与错误日志IP", "设备": "设备", "设备类型偏好": "设备类型偏好", + "设置": "设置", "设置 Logo": "设置 Logo", "设置2FA失败": "设置2FA失败", "设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}": "设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}", @@ -2102,6 +2912,7 @@ "设置首页内容": "设置首页内容", "设置默认地区和特定模型的专用地区": "设置默认地区和特定模型的专用地区", "设计与开发由": "设计与开发由", + "设请求头": "设请求头", "访问 io.net 控制台的 API Keys 页面": "访问 io.net 控制台的 API Keys 页面", "访问容器": "访问容器", "访问模型部署功能需要先启用 io.net 部署服务": "访问模型部署功能需要先启用 io.net 部署服务", @@ -2109,26 +2920,49 @@ "该供应商提供多种AI模型,适用于不同的应用场景。": "该供应商提供多种AI模型,适用于不同的应用场景。", "该分类下没有可用模型": "该分类下没有可用模型", "该域名已存在于白名单中": "该域名已存在于白名单中", + "该套餐未配置 Creem": "该套餐未配置 Creem", + "该套餐未配置 Stripe": "该套餐未配置 Stripe", "该数据可能不可信,请谨慎使用": "该数据可能不可信,请谨慎使用", "该服务器地址将影响支付回调地址以及默认首页展示的地址,请确保正确配置": "该服务器地址将影响支付回调地址以及默认首页展示的地址,请确保正确配置", "该模型存在固定价格与倍率计费方式冲突,请确认选择": "该模型存在固定价格与倍率计费方式冲突,请确认选择", + "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。", "该渠道已开启请求透传,参数覆写、模型重定向等 NewAPI 内置功能将失效,非最佳实践。": "该渠道已开启请求透传,参数覆写、模型重定向等 NewAPI 内置功能将失效,非最佳实践。", "该渠道已开启请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "该渠道已开启请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。", + "该渠道暂无可处理的上游模型更新": "该渠道暂无可处理的上游模型更新", + "该渠道未开启上游模型更新检测": "该渠道未开启上游模型更新检测", + "该规则未启用“作用域:包含规则名称”,无法按规则清空缓存。": "该规则未启用“作用域:包含规则名称”,无法按规则清空缓存。", + "该规则未设置参数覆盖模板": "该规则未设置参数覆盖模板", + "该规则的缓存保留时长;0 表示使用默认 TTL:": "该规则的缓存保留时长;0 表示使用默认 TTL:", + "该记录不包含可用的 token 统计口径。": "该记录不包含可用的 token 统计口径。", "详情": "详情", + "详见「特殊倍率」和「可用分组」标签页。": "详见「特殊倍率」和「可用分组」标签页。", + "语言偏好": "语言偏好", + "语言偏好已保存": "语言偏好已保存", "语音输入": "语音输入", "语音输出": "语音输出", "说明": "说明", "说明:": "说明:", + "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。": "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。", + "说明:生成结果是可直接粘贴到渠道密钥里的 JSON(包含 access_token / refresh_token / account_id)。": "说明:生成结果是可直接粘贴到渠道密钥里的 JSON(包含 access_token / refresh_token / account_id)。", "说明信息": "说明信息", "请上传密钥文件": "请上传密钥文件", "请上传密钥文件!": "请上传密钥文件!", "请为渠道命名": "请为渠道命名", "请使用 Project 为 io.cloud 的密钥": "请使用 Project 为 io.cloud 的密钥", + "请先勾选需要批量设置的模型": "请先勾选需要批量设置的模型", "请先在设置中启用图片功能": "请先在设置中启用图片功能", "请先填写 API Key": "请先填写 API Key", + "请先填写 Discovery URL 或 Issuer URL": "请先填写 Discovery URL 或 Issuer URL", + "请先填写 Issuer URL,以自动生成完整的端点 URL": "请先填写 Issuer URL,以自动生成完整的端点 URL", "请先填写 Ollama API 地址": "请先填写 Ollama API 地址", "请先填写服务器地址": "请先填写服务器地址", + "请先填写服务器地址,以自动生成完整的端点 URL": "请先填写服务器地址,以自动生成完整的端点 URL", + "请先开启并填写音频输入价格。": "请先开启并填写音频输入价格。", + "请先新增模型或从左侧列表选择一个模型": "请先新增模型或从左侧列表选择一个模型", + "请先粘贴回调 URL": "请先粘贴回调 URL", "请先输入密钥": "请先输入密钥", + "请先选择一个作为模板的模型": "请先选择一个作为模板的模型", + "请先选择一条规则": "请先选择一条规则", "请先选择同步渠道": "请先选择同步渠道", "请先选择模型!": "请先选择模型!", "请先选择硬件类型": "请先选择硬件类型", @@ -2140,6 +2974,7 @@ "请再次输入新密码": "请再次输入新密码", "请前往个人设置 → 安全设置进行配置。": "请前往个人设置 → 安全设置进行配置。", "请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用", + "请在 Waffo 后台获取 API 密钥、商户 ID 以及 RSA 密钥对,并配置回调地址。": "请在 Waffo 后台获取 API 密钥、商户 ID 以及 RSA 密钥对,并配置回调地址。", "请在系统设置页面编辑分组倍率以添加新的分组:": "请在系统设置页面编辑分组倍率以添加新的分组:", "请填写完整的产品信息": "请填写完整的产品信息", "请填写完整的管理员账号信息": "请填写完整的管理员账号信息", @@ -2152,10 +2987,12 @@ "请检查表单填写是否正确": "请检查表单填写是否正确", "请检查输入": "请检查输入", "请求体 JSON": "请求体 JSON", + "请求体内存缓存": "请求体内存缓存", + "请求体磁盘缓存": "请求体磁盘缓存", + "请求体超过此大小时使用磁盘缓存": "请求体超过此大小时使用磁盘缓存", "请求参数无效": "请求参数无效", "请求发生错误": "请求发生错误", "请求发生错误: ": "请求发生错误: ", - "模型价格未配置": "模型价格未配置", "请求后端接口失败:": "请求后端接口失败:", "请求失败": "请求失败", "请求头覆盖": "请求头覆盖", @@ -2166,14 +3003,14 @@ "请求超时,请刷新页面后重新发起 GitHub 登录": "请求超时,请刷新页面后重新发起 GitHub 登录", "请求路径": "请求路径", "请求转换": "请求转换", - "原生格式": "原生格式", - "转换": "转换", + "请求配置": "请求配置", "请求预扣费额度": "请求预扣费额度", "请点击我": "请点击我", "请确认以下设置信息,点击\"初始化系统\"开始配置": "请确认以下设置信息,点击\"初始化系统\"开始配置", "请确认您已了解禁用两步验证的后果": "请确认您已了解禁用两步验证的后果", "请确认管理员密码": "请确认管理员密码", "请稍后几秒重试,Turnstile 正在检查用户环境!": "请稍后几秒重试,Turnstile 正在检查用户环境!", + "请粘贴完整回调 URL(包含 code 与 state)": "请粘贴完整回调 URL(包含 code 与 state)", "请联系管理员在系统设置中配置API信息": "请联系管理员在系统设置中配置API信息", "请联系管理员在系统设置中配置Uptime": "请联系管理员在系统设置中配置Uptime", "请联系管理员在系统设置中配置公告信息": "请联系管理员在系统设置中配置公告信息", @@ -2186,11 +3023,18 @@ "请至少选择一个渠道": "请至少选择一个渠道", "请输入 API Key,一行一个,格式:APIKey|Region": "请输入 API Key,一行一个,格式:APIKey|Region", "请输入 API Key,格式:APIKey|Region": "请输入 API Key,格式:APIKey|Region", + "请输入 Authorization Endpoint": "请输入 Authorization Endpoint", "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com": "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com", + "请输入 Client ID": "请输入 Client ID", + "请输入 Client Secret": "请输入 Client Secret", "请输入 io.net API Key": "请输入 io.net API Key", "请输入 io.net API Key(敏感信息不显示)": "请输入 io.net API Key(敏感信息不显示)", + "请输入 JSON 格式的 OAuth 凭据,例如:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}": "请输入 JSON 格式的 OAuth 凭据,例如:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}", "请输入 JSON 格式的密钥内容,例如:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}": "请输入 JSON 格式的密钥内容,例如:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}", "请输入 OIDC 的 Well-Known URL": "请输入 OIDC 的 Well-Known URL", + "请输入 Slug": "请输入 Slug", + "请输入 Token Endpoint": "请输入 Token Endpoint", + "请输入 User Info Endpoint": "请输入 User Info Endpoint", "请输入6位验证码或8位备用码": "请输入6位验证码或8位备用码", "请输入API地址": "请输入API地址", "请输入API地址!": "请输入API地址!", @@ -2225,6 +3069,7 @@ "请输入图标名称": "请输入图标名称", "请输入填充值": "请输入填充值", "请输入备注(仅管理员可见)": "请输入备注(仅管理员可见)", + "请输入套餐标题": "请输入套餐标题", "请输入完整的 JSON 格式密钥内容": "请输入完整的 JSON 格式密钥内容", "请输入完整的URL,例如:https://api.openai.com/v1/chat/completions": "请输入完整的URL,例如:https://api.openai.com/v1/chat/completions", "请输入完整的URL链接": "请输入完整的URL链接", @@ -2235,6 +3080,7 @@ "请输入密钥,一行一个,格式:AccessKey|SecretAccessKey|Region": "请输入密钥,一行一个,格式:AccessKey|SecretAccessKey|Region", "请输入密钥!": "请输入密钥!", "请输入延长时长": "请输入延长时长", + "请输入总额度": "请输入总额度", "请输入您的密码": "请输入您的密码", "请输入您的用户名以确认删除": "请输入您的用户名以确认删除", "请输入您的用户名或邮箱地址": "请输入您的用户名或邮箱地址", @@ -2263,12 +3109,14 @@ "请输入模型名称,例如: llama3.2, qwen2.5:7b": "请输入模型名称,例如: llama3.2, qwen2.5:7b", "请输入模型名称,如:gpt-4": "请输入模型名称,如:gpt-4", "请输入模型描述": "请输入模型描述", + "请输入汇率": "请输入汇率", "请输入消息内容...": "请输入消息内容...", "请输入状态页面Slug": "请输入状态页面Slug", "请输入状态页面的Slug,如:my-status": "请输入状态页面的Slug,如:my-status", "请输入生成数量": "请输入生成数量", "请输入用户名": "请输入用户名", "请输入私有部署地址,格式为:https://fastgpt.run/api/openapi": "请输入私有部署地址,格式为:https://fastgpt.run/api/openapi", + "请输入秒数": "请输入秒数", "请输入管理员密码": "请输入管理员密码", "请输入管理员用户名": "请输入管理员用户名", "请输入线路描述": "请输入线路描述", @@ -2289,6 +3137,7 @@ "请输入部署名称": "请输入部署名称", "请输入部署名称以完成二次确认": "请输入部署名称以完成二次确认", "请输入部署地区,例如:us-central1\n支持使用模型映射格式\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}": "请输入部署地区,例如:us-central1\n支持使用模型映射格式\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}", + "请输入金额": "请输入金额", "请输入镜像地址": "请输入镜像地址", "请输入问题标题": "请输入问题标题", "请输入预警阈值": "请输入预警阈值", @@ -2298,6 +3147,8 @@ "请输入验证码或备用码": "请输入验证码或备用码", "请输入默认 API 版本,例如:2025-04-01-preview": "请输入默认 API 版本,例如:2025-04-01-preview", "请选择API地址": "请选择API地址", + "请选择一条规则进行编辑。": "请选择一条规则进行编辑。", + "请选择主模型": "请选择主模型", "请选择产品": "请选择产品", "请选择你的复制方式": "请选择你的复制方式", "请选择使用模式": "请选择使用模式", @@ -2310,6 +3161,7 @@ "请选择多密钥使用策略": "请选择多密钥使用策略", "请选择密钥更新模式": "请选择密钥更新模式", "请选择密钥格式": "请选择密钥格式", + "请选择支付方式": "请选择支付方式", "请选择日志记录时间": "请选择日志记录时间", "请选择模型": "请选择模型", "请选择模型。": "请选择模型。", @@ -2318,18 +3170,18 @@ "请选择硬件类型": "请选择硬件类型", "请选择组类型": "请选择组类型", "请选择至少一个部署位置": "请选择至少一个部署位置", + "请选择订阅套餐": "请选择订阅套餐", "请选择该令牌支持的模型,留空支持所有模型": "请选择该令牌支持的模型,留空支持所有模型", "请选择该渠道所支持的模型": "请选择该渠道所支持的模型", "请选择该渠道所支持的模型,留空则不更改": "请选择该渠道所支持的模型,留空则不更改", "请选择过期时间": "请选择过期时间", "请选择通知方式": "请选择通知方式", + "调整额度": "调整额度", + "调整额度成功": "调整额度成功", "调用次数": "调用次数", "调用次数分布": "调用次数分布", "调用次数排行": "调用次数排行", "调用趋势": "调用趋势", - "模型排行": "模型排行", - "用户消耗排行": "用户消耗排行", - "用户消耗趋势": "用户消耗趋势", "调试信息": "调试信息", "谨慎": "谨慎", "警告": "警告", @@ -2344,25 +3196,40 @@ "账户管理": "账户管理", "账户绑定": "账户绑定", "账户绑定、安全设置和身份验证": "账户绑定、安全设置和身份验证", + "账户绑定管理": "账户绑定管理", "账户统计": "账户统计", "货币": "货币", "货币单位": "货币单位", + "购买上限": "购买上限", "购买兑换码": "购买兑换码", + "购买套餐后即可享受模型权益": "购买套餐后即可享受模型权益", + "购买或手动新增订阅会升级到该分组;当套餐失效/过期或手动作废/删除后,将回退到升级前分组。回退不会立即生效,通常会有几分钟延迟。": "购买或手动新增订阅会升级到该分组;当套餐失效/过期或手动作废/删除后,将回退到升级前分组。回退不会立即生效,通常会有几分钟延迟。", + "购买订阅套餐": "购买订阅套餐", "费用信息": "费用信息", "费用预估": "费用预估", "资源消耗": "资源消耗", "起始时间": "起始时间", "超级管理员": "超级管理员", "超级管理员未设置充值链接!": "超级管理员未设置充值链接!", + "超过阈值时拒绝新请求": "超过阈值时拒绝新请求", "跟随日志": "跟随日志", "跟随系统主题设置": "跟随系统主题设置", "跨分组": "跨分组", + "跨分组特殊倍率": "跨分组特殊倍率", "跨分组重试": "跨分组重试", + "路径正则": "路径正则", + "路径正则(每行一个)": "路径正则(每行一个)", "跳转": "跳转", + "转大写": "转大写", + "转小写": "转小写", + "转换": "转换", "轮询": "轮询", "轮询模式": "轮询模式", "轮询模式必须搭配Redis和内存缓存功能使用,否则性能将大幅降低,并且无法实现轮询功能": "轮询模式必须搭配Redis和内存缓存功能使用,否则性能将大幅降低,并且无法实现轮询功能", + "软错误": "软错误", "输入": "输入", + "输入 {{price}} / 1M tokens": "输入 {{price}} / 1M tokens", + "输入 $/1M tokens": "输入 $/1M tokens", "输入 OIDC 的 Authorization Endpoint": "输入 OIDC 的 Authorization Endpoint", "输入 OIDC 的 Client ID": "输入 OIDC 的 Client ID", "输入 OIDC 的 Token Endpoint": "输入 OIDC 的 Token Endpoint", @@ -2370,16 +3237,22 @@ "输入IP地址后回车,如:8.8.8.8": "输入IP地址后回车,如:8.8.8.8", "输入JSON对象": "输入JSON对象", "输入价格": "输入价格", + "输入价格 {{symbol}}{{price}} / 1M tokens": "输入价格 {{symbol}}{{price}} / 1M tokens", + "输入价格:{{symbol}}{{price}} / 1M tokens": "输入价格:{{symbol}}{{price}} / 1M tokens", "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}", "输入你注册的 LinuxDO OAuth APP 的 ID": "输入你注册的 LinuxDO OAuth APP 的 ID", "输入你的账户名{{username}}以确认删除": "输入你的账户名{{username}}以确认删除", + "输入倍率": "输入倍率", "输入域名后回车": "输入域名后回车", "输入域名后回车,如:example.com": "输入域名后回车,如:example.com", + "输入基础 URL": "输入基础 URL", "输入密码,最短 8 位,最长 20 位": "输入密码,最短 8 位,最长 20 位", "输入数字": "输入数字", "输入标签或使用\",\"分隔多个标签": "输入标签或使用\",\"分隔多个标签", "输入模型倍率": "输入模型倍率", + "输入模型名称,例如 gpt-4.1": "输入模型名称,例如 gpt-4.1", "输入每次价格": "输入每次价格", + "输入每次调用价格": "输入每次调用价格", "输入端口后回车,如:80 或 8000-8999": "输入端口后回车,如:80 或 8000-8999", "输入系统提示词,用户的系统提示词将优先于此设置": "输入系统提示词,用户的系统提示词将优先于此设置", "输入自定义模型名称": "输入自定义模型名称", @@ -2388,54 +3261,23 @@ "输入要添加的邮箱域名": "输入要添加的邮箱域名", "输入认证器应用显示的6位数字验证码": "输入认证器应用显示的6位数字验证码", "输入邮箱地址": "输入邮箱地址", + "输入金额": "输入金额", "输入项目名称,按回车添加": "输入项目名称,按回车添加", + "输入额度": "输入额度", "输入验证码": "输入验证码", "输入验证码完成设置": "输入验证码完成设置", "输出": "输出", "输出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}": "输出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}", - "磁盘缓存设置(磁盘换内存)": "磁盘缓存设置(磁盘换内存)", - "启用磁盘缓存后,大请求体将临时存储到磁盘而非内存,可显著降低内存占用,适用于处理包含大量图片/文件的请求。建议在 SSD 环境下使用。": "启用磁盘缓存后,大请求体将临时存储到磁盘而非内存,可显著降低内存占用,适用于处理包含大量图片/文件的请求。建议在 SSD 环境下使用。", - "启用磁盘缓存": "启用磁盘缓存", - "将大请求体临时存储到磁盘": "将大请求体临时存储到磁盘", - "磁盘缓存阈值 (MB)": "磁盘缓存阈值 (MB)", - "请求体超过此大小时使用磁盘缓存": "请求体超过此大小时使用磁盘缓存", - "磁盘缓存最大总量 (MB)": "磁盘缓存最大总量 (MB)", - "可用空间: {{free}} / 总空间: {{total}}": "可用空间: {{free}} / 总空间: {{total}}", - "磁盘缓存占用的最大空间": "磁盘缓存占用的最大空间", - "留空使用系统临时目录": "留空使用系统临时目录", - "例如 /var/cache/new-api": "例如 /var/cache/new-api", - "性能监控": "性能监控", - "刷新统计": "刷新统计", - "重置统计": "重置统计", - "执行 GC": "执行 GC", - "请求体磁盘缓存": "请求体磁盘缓存", - "活跃文件": "活跃文件", - "磁盘命中": "磁盘命中", - "请求体内存缓存": "请求体内存缓存", - "当前缓存大小": "当前缓存大小", - "活跃缓存数": "活跃缓存数", - "内存命中": "内存命中", - "缓存目录磁盘空间": "缓存目录磁盘空间", - "磁盘可用空间小于缓存最大总量设置": "磁盘可用空间小于缓存最大总量设置", - "已分配内存": "已分配内存", - "总分配内存": "总分配内存", - "系统内存": "系统内存", - "GC 次数": "GC 次数", - "Goroutine 数": "Goroutine 数", - "目录文件数": "目录文件数", - "目录总大小": "目录总大小", - "磁盘缓存已清理": "磁盘缓存已清理", - "清理失败": "清理失败", - "统计已重置": "统计已重置", - "重置失败": "重置失败", - "GC 已执行": "GC 已执行", - "GC 执行失败": "GC 执行失败", - "缓存目录": "缓存目录", - "可用": "可用", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "输出价格": "输出价格", + "输出价格 {{symbol}}{{price}} / 1M tokens": "输出价格 {{symbol}}{{price}} / 1M tokens", "输出价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "输出价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})", + "输出价格:{{symbol}}{{price}} / 1M tokens": "输出价格:{{symbol}}{{price}} / 1M tokens", + "输出价格:{{symbol}}{{total}} / 1M tokens": "输出价格:{{symbol}}{{total}} / 1M tokens", "输出倍率 {{completionRatio}}": "输出倍率 {{completionRatio}}", "边栏设置": "边栏设置", + "过期于": "过期于", "过期时间": "过期时间", "过期时间不能早于当前时间!": "过期时间不能早于当前时间!", "过期时间快捷设置": "过期时间快捷设置", @@ -2447,29 +3289,44 @@ "运行时长(小时)": "运行时长(小时)", "返回修改": "返回修改", "返回登录": "返回登录", - "违规扣费金额": "违规扣费金额", - "这是重复键中的最后一个,其值将被使用": "这是重复键中的最后一个,其值将被使用", + "返回错误": "返回错误", + "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。", + "这些价格都是可选项,不填也可以。": "这些价格都是可选项,不填也可以。", + "这将删除超过 10 分钟未使用的临时缓存文件": "这将删除超过 10 分钟未使用的临时缓存文件", "这是基础金额,实际扣费 = 基础金额 x 系统分组倍率。": "这是基础金额,实际扣费 = 基础金额 x 系统分组倍率。", + "这是重复键中的最后一个,其值将被使用": "这是重复键中的最后一个,其值将被使用", + "这里直接编辑 JSON 对象。适合简单覆盖参数的场景。": "这里直接编辑 JSON 对象。适合简单覆盖参数的场景。", "进度": "进度", "进行中": "进行中", "进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用": "进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用", + "违规扣费": "违规扣费", + "违规扣费金额": "违规扣费金额", "连接保活设置": "连接保活设置", + "连接信息已填入": "连接信息已填入", "连接已断开": "连接已断开", "连接测试中...": "连接测试中...", + "追加": "追加", "追加到现有密钥": "追加到现有密钥", "追加模式:将新密钥添加到现有密钥列表末尾": "追加模式:将新密钥添加到现有密钥列表末尾", "追加模式:新密钥将添加到现有密钥列表的末尾": "追加模式:新密钥将添加到现有密钥列表的末尾", + "追加模板": "追加模板", "退出": "退出", + "退款": "退款", + "适合 MJ / 任务类等按次收费模型。": "适合 MJ / 任务类等按次收费模型。", + "适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。": "适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。", "适用于个人使用的场景,不需要设置模型价格": "适用于个人使用的场景,不需要设置模型价格", "适用于为多个用户提供服务的场景": "适用于为多个用户提供服务的场景", "适用于展示系统功能的场景,提供基础功能演示": "适用于展示系统功能的场景,提供基础功能演示", "适配 -thinking、-thinking-预算数字 和 -nothinking 后缀": "适配 -thinking、-thinking-预算数字、-nothinking 以及 -low/-medium/-high 后缀", + "选择 premium 创建的令牌,调用 API 时费用为 standard 的 50%。": "选择 premium 创建的令牌,调用 API 时费用为 standard 的 50%。", + "选择使用分组": "选择使用分组", "选择充值额度": "选择充值额度", "选择分组": "选择分组", "选择同步来源": "选择同步来源", "选择同步渠道": "选择同步渠道", "选择同步语言": "选择同步语言", "选择容器": "选择容器", + "选择您的首选界面语言,设置将自动保存并同步到所有设备": "选择您的首选界面语言,设置将自动保存并同步到所有设备", "选择成功": "选择成功", "选择支付方式": "选择支付方式", "选择支持的认证设备类型": "选择支持的认证设备类型", @@ -2480,15 +3337,22 @@ "选择模型后可一键填充当前选中令牌(或本页第一个令牌)。": "选择模型后可一键填充当前选中令牌(或本页第一个令牌)。", "选择模型开始对话": "选择模型开始对话", "选择状态": "选择状态", + "选择用户分组": "选择用户分组", "选择硬件类型": "选择硬件类型", "选择端点类型": "选择端点类型", "选择系统运行模式": "选择系统运行模式", "选择组类型": "请选择组类型", "选择要覆盖的冲突项": "选择要覆盖的冲突项", + "选择订阅套餐": "选择订阅套餐", "选择语言": "选择语言", "选择过期时间(可选,留空为永久)": "选择过期时间(可选,留空为永久)", "选择部署位置(可多选)": "选择部署位置(可多选)", + "选择预设...": "选择预设...", + "选择预设模板(可选)": "选择预设模板(可选)", "透传请求体": "透传请求体", + "透传请求头": "透传请求头", + "递归": "递归", + "递归策略": "递归策略", "通义千问": "通义千问", "通用设置": "通用设置", "通知": "通知", @@ -2500,12 +3364,15 @@ "通知类型 (quota_exceed: 额度预警)": "通知类型 (quota_exceed: 额度预警)", "通知邮箱": "通知邮箱", "通知配置": "通知配置", + "通过分组可以实现不同用户等级的差异化定价,例如 VIP 用户享受更低的 API 调用费用。": "通过分组可以实现不同用户等级的差异化定价,例如 VIP 用户享受更低的 API 调用费用。", "通过划转功能将奖励额度转入到您的账户余额中": "通过划转功能将奖励额度转入到您的账户余额中", "通过密码注册时需要进行邮箱验证": "通过密码注册时需要进行邮箱验证", + "通过此功能,可以根据用户所在分组,为不同等级的用户展示不同的可选列表。": "通过此功能,可以根据用户所在分组,为不同等级的用户展示不同的可选列表。", "通道 ${name} 余额更新成功!": "通道 ${name} 余额更新成功!", "通道 ${name} 测试成功,模型 ${model} 耗时 ${time.toFixed(2)} 秒。": "通道 ${name} 测试成功,模型 ${model} 耗时 ${time.toFixed(2)} 秒。", "通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。": "通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。", "速率限制设置": "速率限制设置", + "逻辑": "逻辑", "邀请": "邀请", "邀请人": "邀请人", "邀请人数": "邀请人数", @@ -2524,6 +3391,8 @@ "邮箱地址": "邮箱地址", "邮箱域名格式不正确,请输入有效的域名,如 gmail.com": "邮箱域名格式不正确,请输入有效的域名,如 gmail.com", "邮箱域名白名单格式不正确": "邮箱域名白名单格式不正确", + "邮箱字段": "邮箱字段", + "邮箱字段(可选)": "邮箱字段(可选)", "邮箱账户绑定成功!": "邮箱账户绑定成功!", "部分保存失败": "部分保存失败", "部分保存失败,请重试": "部分保存失败,请重试", @@ -2552,8 +3421,13 @@ "配置 SMTP": "配置 SMTP", "配置 Telegram 登录": "配置 Telegram 登录", "配置 Turnstile": "配置 Turnstile", + "配置 Waffo 充值时可用的支付方式,保存后在充值页面展示给用户。": "配置 Waffo 充值时可用的支付方式,保存后在充值页面展示给用户。", "配置 WeChat Server": "配置 WeChat Server", + "配置:": "配置:", + "配置后的效果:": "配置后的效果:", "配置和消息已全部重置": "配置和消息已全部重置", + "配置套餐的有效时长": "配置套餐的有效时长", + "配置如何从用户信息 API 响应中提取用户数据,支持 JSONPath 语法": "配置如何从用户信息 API 响应中提取用户数据,支持 JSONPath 语法", "配置完成后刷新页面即可使用模型部署功能": "配置完成后刷新页面即可使用模型部署功能", "配置导入成功": "配置导入成功", "配置已导出到下载文件夹": "配置已导出到下载文件夹", @@ -2563,7 +3437,9 @@ "配置有效的 io.net API Key": "配置有效的 io.net API Key", "配置服务器端请求伪造(SSRF)防护,用于保护内网资源安全": "配置服务器端请求伪造(SSRF)防护,用于保护内网资源安全", "配置模型部署服务提供商的API密钥和启用状态": "配置模型部署服务提供商的API密钥和启用状态", + "配置特殊倍率后:": "配置特殊倍率后:", "配置登录注册": "配置登录注册", + "配置自定义 OAuth 提供商,支持 GitHub Enterprise、GitLab、Gitea、Nextcloud、Keycloak、ORY 等兼容 OAuth 2.0 协议的身份提供商": "配置自定义 OAuth 提供商,支持 GitHub Enterprise、GitLab、Gitea、Nextcloud、Keycloak、ORY 等兼容 OAuth 2.0 协议的身份提供商", "配置说明": "配置说明", "配置邮箱域名白名单": "配置邮箱域名白名单", "重启部署失败": "重启部署失败", @@ -2572,6 +3448,7 @@ "重复的键名": "重复的键名", "重复的键名,此值将被后面的同名键覆盖": "重复的键名,此值将被后面的同名键覆盖", "重定向 URL 填": "重定向 URL 填", + "重新上传": "重新上传", "重新发送": "重新发送", "重新生成": "重新生成", "重新生成备用码": "重新生成备用码", @@ -2582,31 +3459,34 @@ "重置 2FA": "重置 2FA", "重置 Passkey": "重置 Passkey", "重置为默认": "重置为默认", + "重置周期": "重置周期", + "重置失败": "重置失败", "重置模型倍率": "重置模型倍率", + "重置统计": "重置统计", "重置选项": "重置选项", "重置邮件发送成功,请检查邮箱!": "重置邮件发送成功,请检查邮箱!", "重置配置": "重置配置", "重要提醒": "重要提醒", "重试": "重试", - "不重试": "不重试", - "失败后是否重试": "失败后是否重试", + "重试建议": "重试建议", "重试连接": "重试连接", + "金额": "金额", "钱包管理": "钱包管理", "链接中的{key}将自动替换为sk-xxxx,{address}将自动替换为系统设置的服务器地址,末尾不带/和/v1": "链接中的{key}将自动替换为sk-xxxx,{address}将自动替换为系统设置的服务器地址,末尾不带/和/v1", "销毁容器": "销毁容器", "销毁容器失败": "销毁容器失败", "错误": "错误", - "退款": "退款", + "错误代码(可选)": "错误代码(可选)", + "错误消息(必填)": "错误消息(必填)", + "错误类型(可选)": "错误类型(可选)", "错误详情": "错误详情", - "异步任务退款": "异步任务退款", - "任务ID": "任务ID", - "失败原因": "失败原因", "键为分组名称,值为另一个 JSON 对象,键为分组名称,值为该分组的用户的特殊分组倍率,例如:{\"vip\": {\"default\": 0.5, \"test\": 1}},表示 vip 分组的用户在使用default分组的令牌时倍率为0.5,使用test分组时倍率为1": "键为分组名称,值为另一个 JSON 对象,键为分组名称,值为该分组的用户的特殊分组倍率,例如:{\"vip\": {\"default\": 0.5, \"test\": 1}},表示 vip 分组的用户在使用default分组的令牌时倍率为0.5,使用test分组时倍率为1", "键为原状态码,值为要复写的状态码,仅影响本地判断": "键为原状态码,值为要复写的状态码,仅影响本地判断", "键为用户分组名称,值为操作映射对象。内层键以\"+:\"开头表示添加指定分组(键值为分组名称,值为描述),以\"-:\"开头表示移除指定分组(键值为分组名称),不带前缀的键直接添加该分组。例如:{\"vip\": {\"+:premium\": \"高级分组\", \"special\": \"特殊分组\", \"-:default\": \"默认分组\"}},表示 vip 分组的用户可以使用 premium 和 special 分组,同时移除 default 分组的访问权限": "键为用户分组名称,值为操作映射对象。内层键以\"+:\"开头表示添加指定分组(键值为分组名称,值为描述),以\"-:\"开头表示移除指定分组(键值为分组名称),不带前缀的键直接添加该分组。例如:{\"vip\": {\"+:premium\": \"高级分组\", \"special\": \"特殊分组\", \"-:default\": \"默认分组\"}},表示 vip 分组的用户可以使用 premium 和 special 分组,同时移除 default 分组的访问权限", "键为端点类型,值为路径和方法对象": "键为端点类型,值为路径和方法对象", "键为请求中的模型名称,值为要替换的模型名称": "键为请求中的模型名称,值为要替换的模型名称", "键名": "键名", + "键的前缀 +: 表示添加,-: 表示移除,无前缀表示追加。值为分组描述(移除时填 \"remove\")。": "键的前缀 +: 表示添加,-: 表示移除,无前缀表示追加。值为分组描述(移除时填 \"remove\")。", "镜像仓库密码": "镜像仓库密码", "镜像仓库用户名": "镜像仓库用户名", "镜像仓库配置": "镜像仓库配置", @@ -2615,10 +3495,13 @@ "镜像配置": "镜像配置", "问题标题": "问题标题", "队列中": "队列中", + "附加条件": "附加条件", "降低您账户的安全性": "降低您账户的安全性", "降级": "降级", "限制周期": "限制周期", "限制周期统一使用上方配置的“限制周期”值。": "限制周期统一使用上方配置的“限制周期”值。", + "限流": "限流", + "限购": "限购", "隐私政策": "隐私政策", "隐私政策已更新": "隐私政策已更新", "隐私政策更新失败": "隐私政策更新失败", @@ -2636,35 +3519,58 @@ "需要重新完整设置才能再次启用": "需要重新完整设置才能再次启用", "非必要,不建议启用模型限制": "非必要,不建议启用模型限制", "非流": "非流", + "音乐预览": "音乐预览", + "音频倍率 {{audioRatio}}": "音频倍率 {{audioRatio}}", "音频倍率(仅部分模型支持该计费)": "音频倍率(仅部分模型支持该计费)", "音频提示 {{input}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{completion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} = {{symbol}}{{total}}": "音频提示 {{input}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{completion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} = {{symbol}}{{total}}", "音频提示价格:{{symbol}}{{price}} * {{audioRatio}} = {{symbol}}{{total}} / 1M tokens (音频倍率: {{audioRatio}})": "音频提示价格:{{symbol}}{{price}} * {{audioRatio}} = {{symbol}}{{total}} / 1M tokens (音频倍率: {{audioRatio}})", + "音频无法播放": "音频无法播放", + "音频补全价格": "音频补全价格", "音频补全价格:{{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (音频补全倍率: {{audioCompRatio}})": "音频补全价格:{{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (音频补全倍率: {{audioCompRatio}})", + "音频补全价格:{{symbol}}{{price}} / 1M tokens": "音频补全价格:{{symbol}}{{price}} / 1M tokens", + "音频补全倍率": "音频补全倍率", "音频补全倍率(仅部分模型支持该计费)": "音频补全倍率(仅部分模型支持该计费)", + "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "音频输入价格": "音频输入价格", + "音频输入价格:{{symbol}}{{price}} / 1M tokens": "音频输入价格:{{symbol}}{{price}} / 1M tokens", + "音频输入倍率": "音频输入倍率", "音频输入相关的倍率设置,键为模型名称,值为倍率": "音频输入相关的倍率设置,键为模型名称,值为倍率", + "音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "音频输出补全相关的倍率设置,键为模型名称,值为倍率", "页脚": "页脚", "页面未找到,请检查您的浏览器地址是否正确": "页面未找到,请检查您的浏览器地址是否正确", + "页面渲染出错,请刷新页面重试": "页面渲染出错,请刷新页面重试", "顶栏管理": "顶栏管理", + "项": "项", "项目": "项目", "项目内容": "项目内容", "项目操作按钮组": "项目操作按钮组", "预估总费用": "预估总费用", "预估费用仅供参考,实际费用可能略有差异": "预估费用仅供参考,实际费用可能略有差异", "预填组管理": "预填组管理", + "预扣": "预扣", "预览失败": "预览失败", + "预览效果": "预览效果", "预览更新": "预览更新", + "预览模板": "预览模板", "预览请求体": "预览请求体", "预计结束": "预计结束", + "预计结果": "预计结果", + "预设模板": "预设模板", "预警阈值必须为正数": "预警阈值必须为正数", "频率惩罚,减少重复词汇的出现": "频率惩罚,减少重复词汇的出现", "频率限制的周期(分钟)": "频率限制的周期(分钟)", "颜色": "颜色", + "额外价格项": "额外价格项", + "额外设置": "额外设置", "额度": "额度", + "额度充值": "额度充值", + "额度展示类型": "额度展示类型", "额度必须大于0": "额度必须大于0", "额度提醒阈值": "额度提醒阈值", "额度查询接口返回令牌额度而非用户额度": "额度查询接口返回令牌额度而非用户额度", "额度设置": "额度设置", + "额度重置": "额度重置", "额度预警阈值": "额度预警阈值", "首尾生视频": "首尾生视频", "首页": "首页", @@ -2679,328 +3585,36 @@ "验证设置": "验证设置", "验证身份": "验证身份", "验证配置错误": "验证配置错误", + "高危操作确认": "高危操作确认", + "高危状态码重试风险告知与免责声明Markdown": "### ⚠️ 高危操作:504/524 状态码重试风险告知与免责声明\n本项目默认对 `400 (请求错误)`、`504 (网关超时)`和 `524 (cdn发生超时)`状态码不进行重试。\n504 和 524 错误通常意味着**请求已成功送达上游 AI 服务,且上游正在处理,但因处理时间过长导致连接断开**。\n\n开启对此类超时状态码的重定向/重试属于**极高风险操作**。作为本开源项目的使用者,在开启该功能前,您必须仔细阅读并知悉以下严重后果:\n\n#### 一、 核心风险告知(请仔细阅读)\n1. 💸 双重/多重计费风险: 绝大多数 AI 上游厂商对于已经开始处理但因网络原因中断(504/524)的请求**依然会进行扣费**。此时若触发重试,将会向上游发起全新请求,导致您被**双重甚至多重计费**。\n2. ⏳ 客户端严重超时: 单次请求已经触发超时,叠加重试机制将会使总请求耗时成倍增加,导致您的最终客户端(或调用方)出现严重甚至完全无法接受的超时现象。\n3. 💥 请求积压与系统崩溃风险: 强制重试超时请求会长时间占用系统线程和连接数。在高并发场景下,这会导致严重的**请求积压**,进而耗尽系统资源,引发雪崩效应,导致您的整个代理服务崩溃。\n\n#### 二、 风险确认声明\n如果您坚持开启该功能,即代表您作出以下确认:", + "高危状态码重试风险确认输入文本": "我已了解多重计费与崩溃风险,确认开启", + "高危状态码重试风险确认项1": "我已充分阅读并理解:本人已完整阅读上述全部风险提示,完全理解强制重试 504 和 524 状态码可能带来的破坏性后果。", + "高危状态码重试风险确认项2": "我已与上游沟通并确认:本人确认,当前出现的超时问题属于上游服务的瓶颈。本人已与上游提供商进行过沟通,确认上游无法解决该超时问题,因此才采取强制重试方案作为妥协手段。", + "高危状态码重试风险确认项3": "我自愿承担计费损失:本人知晓并接受由此产生的全部双重/多重计费风险,承诺不会因重试导致的账单异常在本项目仓库中提交 Issue 或抱怨。", + "高危状态码重试风险确认项4": "我自愿承担系统稳定性风险:本人知晓该操作可能导致客户端严重超时及服务崩溃。若因本人开启此功能导致请求积压或服务不可用,后果由本人自行承担。", + "高危状态码重试风险输入不匹配提示": "输入内容与要求不一致", + "高危状态码重试风险输入框占位文案": "请完整输入上方文字", + "高级": "高级", + "高级套餐,半价优惠": "高级套餐,半价优惠", + "高级文本编辑": "高级文本编辑", "高级设置": "高级设置", + "高级选项": "高级选项", "高级配置": "高级配置", "黑名单": "黑名单", "默认": "默认", "默认 API 版本": "默认 API 版本", "默认 Responses API 版本,为空则使用上方版本": "默认 Responses API 版本,为空则使用上方版本", + "默认 TTL(秒)": "默认 TTL(秒)", + "默认为 5m 缓存创建倍率;1h 缓存创建倍率按固定乘法自动计算(当前为 1.6x)": "默认为 5m 缓存创建倍率;1h 缓存创建倍率按固定乘法自动计算(当前为 1.6x)", + "默认使用auto分组": "默认使用auto分组", "默认使用系统名称": "默认使用系统名称", "默认助手消息": "你好!有什么我可以帮助你的吗?", "默认区域": "默认区域", "默认区域,如: us-central1": "默认区域,如: us-central1", + "默认情况下,所有用户创建令牌时看到的可选分组列表是一样的(即「用户可选」列勾选的分组)。": "默认情况下,所有用户创建令牌时看到的可选分组列表是一样的(即「用户可选」列勾选的分组)。", "默认折叠侧边栏": "默认折叠侧边栏", "默认测试模型": "默认测试模型", "默认用户消息": "你好", - "默认补全倍率": "默认补全倍率", - "每日签到": "每日签到", - "今日已签到,累计签到": "今日已签到,累计签到", - "每日签到可获得随机额度奖励": "每日签到可获得随机额度奖励", - "今日已签到": "今日已签到", - "立即签到": "立即签到", - "正在加载签到状态...": "正在加载签到状态...", - "获取签到状态失败": "获取签到状态失败", - "签到成功!获得": "签到成功!获得", - "签到失败": "签到失败", - "获得": "获得", - "累计签到": "累计签到", - "本月获得": "本月获得", - "累计获得": "累计获得", - "签到奖励将直接添加到您的账户余额": "签到奖励将直接添加到您的账户余额", - "每日仅可签到一次,请勿重复签到": "每日仅可签到一次,请勿重复签到", - "签到设置": "签到设置", - "签到功能允许用户每日签到获取随机额度奖励": "签到功能允许用户每日签到获取随机额度奖励", - "启用签到功能": "启用签到功能", - "签到最小额度": "签到最小额度", - "签到奖励的最小额度": "签到奖励的最小额度", - "签到最大额度": "签到最大额度", - "签到奖励的最大额度": "签到奖励的最大额度", - "保存签到设置": "保存签到设置", - "ChatCompletions→Responses 兼容配置(Beta)": "ChatCompletions→Responses 兼容配置(Beta)", - "提示:该功能为测试版,未来配置结构与功能行为可能发生变更,请勿在生产环境使用。": "提示:该功能为测试版,未来配置结构与功能行为可能发生变更,请勿在生产环境使用。", - "填充模板(指定渠道)": "填充模板(指定渠道)", - "填充模板(全渠道)": "填充模板(全渠道)", - "格式化 JSON": "格式化 JSON", - "提示:此处配置仅用于控制「模型广场」对用户的展示效果,不会影响模型的实际调用与路由。若需配置真实调用行为,请前往「渠道管理」进行设置。": "提示:此处配置仅用于控制「模型广场」对用户的展示效果,不会影响模型的实际调用与路由。若需配置真实调用行为,请前往「渠道管理」进行设置。", - "确认关闭提示": "确认关闭提示", - "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?", - "关闭提示": "关闭提示", - "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。": "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。", - "Stripe/Creem 需在第三方平台创建商品并填入 ID": "Stripe/Creem 需在第三方平台创建商品并填入 ID", - "暂无订阅套餐": "暂无订阅套餐", - "订阅管理": "订阅管理", - "订阅套餐管理": "订阅套餐管理", - "新建套餐": "新建套餐", - "套餐": "套餐", - "支付渠道": "支付渠道", - "购买上限": "购买上限", - "有效期": "有效期", - "禁用后用户端不再展示,但历史订单不受影响。是否继续?": "禁用后用户端不再展示,但历史订单不受影响。是否继续?", - "启用后套餐将在用户端展示。是否继续?": "启用后套餐将在用户端展示。是否继续?", - "更新套餐信息": "更新套餐信息", - "创建新的订阅套餐": "创建新的订阅套餐", - "套餐的基本信息和定价": "套餐的基本信息和定价", - "套餐标题": "套餐标题", - "请输入套餐标题": "请输入套餐标题", - "套餐副标题": "套餐副标题", - "例如:适合轻度使用": "例如:适合轻度使用", - "请输入金额": "请输入金额", - "请输入总额度": "请输入总额度", - "0 表示不限": "0 表示不限", - "原生额度": "原生额度", - "使用原生额度输入": "使用原生额度输入", - "收起原生额度输入": "收起原生额度输入", - "升级分组": "升级分组", - "不升级": "不升级", - "购买或手动新增订阅会升级到该分组;当套餐失效/过期或手动作废/删除后,将回退到升级前分组。回退不会立即生效,通常会有几分钟延迟。": "购买或手动新增订阅会升级到该分组;当套餐失效/过期或手动作废/删除后,将回退到升级前分组。回退不会立即生效,通常会有几分钟延迟。", - "币种": "币种", - "由全站货币展示设置统一控制": "由全站货币展示设置统一控制", - "排序": "排序", - "启用状态": "启用状态", - "有效期设置": "有效期设置", - "配置套餐的有效时长": "配置套餐的有效时长", - "有效期单位": "有效期单位", - "自定义秒数": "自定义秒数", - "请输入秒数": "请输入秒数", - "有效期数值": "有效期数值", - "额度重置": "额度重置", - "支持周期性重置套餐权益额度": "支持周期性重置套餐权益额度", - "重置周期": "重置周期", - "第三方支付配置": "第三方支付配置", - "Stripe/Creem 商品ID(可选)": "Stripe/Creem 商品ID(可选)", - "生效": "生效", - "已作废": "已作废", - "用户订阅管理": "用户订阅管理", - "选择订阅套餐": "选择订阅套餐", - "新增订阅": "新增订阅", - "暂无订阅记录": "暂无订阅记录", - "来源": "来源", - "开始": "开始", - "结束": "结束", - "作废": "作废", - "确认作废": "确认作废", - "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "作废后该订阅将立即失效,历史记录不受影响。是否继续?", - "删除会彻底移除该订阅记录(含权益明细)。是否继续?": "删除会彻底移除该订阅记录(含权益明细)。是否继续?", - "绑定订阅套餐": "绑定订阅套餐", - "绑定后会立即生成用户订阅(无需支付),有效期按套餐配置计算。": "绑定后会立即生成用户订阅(无需支付),有效期按套餐配置计算。", - "订阅套餐": "订阅套餐", - "额度充值": "额度充值", - "优先订阅": "优先订阅", - "优先钱包": "优先钱包", - "仅用订阅": "仅用订阅", - "仅用钱包": "仅用钱包", - "我的订阅": "我的订阅", - "个生效中": "个生效中", - "无生效": "无生效", - "已保存偏好为": "已保存偏好为", - ",当前无生效订阅,将自动使用钱包": ",当前无生效订阅,将自动使用钱包", - "个已过期": "个已过期", - "订阅": "订阅", - "至": "至", - "过期于": "过期于", - "作废于": "作废于", - "下一次重置": "下一次重置", - "购买套餐后即可享受模型权益": "购买套餐后即可享受模型权益", - "限购": "限购", - "推荐": "推荐", - "已达到购买上限": "已达到购买上限", - "已达上限": "已达上限", - "立即订阅": "立即订阅", - "暂无可购买套餐": "暂无可购买套餐", - "该套餐未配置 Stripe": "该套餐未配置 Stripe", - "已打开支付页面": "已打开支付页面", - "支付失败": "支付失败", - "该套餐未配置 Creem": "该套餐未配置 Creem", - "已发起支付": "已发起支付", - "购买订阅套餐": "购买订阅套餐", - "套餐名称": "套餐名称", - "应付金额": "应付金额", - "支付": "支付", - "管理员未开启在线支付功能,请联系管理员配置。": "管理员未开启在线支付功能,请联系管理员配置。", - "偏好设置": "偏好设置", - "界面语言和其他个人偏好": "界面语言和其他个人偏好", - "语言偏好": "语言偏好", - "选择您的首选界面语言,设置将自动保存并同步到所有设备": "选择您的首选界面语言,设置将自动保存并同步到所有设备", - "语言偏好已保存": "语言偏好已保存", - "提示:语言偏好会同步到您登录的所有设备,并影响API返回的错误消息语言。": "提示:语言偏好会同步到您登录的所有设备,并影响API返回的错误消息语言。", - "自定义 OAuth 提供商": "自定义 OAuth 提供商", - "配置自定义 OAuth 提供商,支持 GitHub Enterprise、GitLab、Gitea、Nextcloud、Keycloak、ORY 等兼容 OAuth 2.0 协议的身份提供商": "配置自定义 OAuth 提供商,支持 GitHub Enterprise、GitLab、Gitea、Nextcloud、Keycloak、ORY 等兼容 OAuth 2.0 协议的身份提供商", - "回调 URL 格式": "回调 URL 格式", - "添加提供商": "添加提供商", - "编辑提供商": "编辑提供商", - "选择预设...": "选择预设...", - "输入基础 URL": "输入基础 URL", - "例如": "例如", - "提供商名称": "提供商名称", - "标识符 (Slug)": "标识符 (Slug)", - "授权端点": "授权端点", - "令牌端点": "令牌端点", - "用户信息端点": "用户信息端点", - "用户 ID 字段": "用户 ID 字段", - "支持 JSONPath,如 sub, id, data.user.id": "支持 JSONPath,如 sub, id, data.user.id", - "用户名字段": "用户名字段", - "支持 JSONPath,如 preferred_username, login, data.user.username": "支持 JSONPath,如 preferred_username, login, data.user.username", - "显示名称字段": "显示名称字段", - "支持 JSONPath,如 name, display_name, data.user.name": "支持 JSONPath,如 name, display_name, data.user.name", - "邮箱字段": "邮箱字段", - "支持 JSONPath,如 email, data.user.email": "支持 JSONPath,如 email, data.user.email", - "授权范围 (Scopes)": "授权范围 (Scopes)", - "认证方式": "认证方式", - "自动检测": "自动检测", - "参数传递": "参数传递", - "Basic Auth 头": "Basic Auth 头", - "暂无自定义 OAuth 提供商": "暂无自定义 OAuth 提供商", - "确定要删除该提供商吗?": "确定要删除该提供商吗?", - "创建成功": "创建成功", - "更新成功": "更新成功", - "确认解绑": "确认解绑", - "确定要解绑 {{name}} 吗?": "确定要解绑 {{name}} 吗?", - "解绑成功": "解绑成功", - "{{name}} ID": "{{name}} ID", - "使用 {{name}} 继续": "使用 {{name}} 继续", - "端点 URL 必须以 http:// 或 https:// 开头:": "端点 URL 必须以 http:// 或 https:// 开头:", - "OAuth 配置错误:授权端点必须是完整的 URL(以 http:// 或 https:// 开头)": "OAuth 配置错误:授权端点必须是完整的 URL(以 http:// 或 https:// 开头)", - "OAuth 登录失败:": "OAuth 登录失败:", - "必填:请输入服务器地址以自动生成完整端点 URL": "必填:请输入服务器地址以自动生成完整端点 URL", - "填写服务器地址后自动生成:": "填写服务器地址后自动生成:", - "自动生成:": "自动生成:", - "请先填写服务器地址,以自动生成完整的端点 URL": "请先填写服务器地址,以自动生成完整的端点 URL", - "端点 URL 必须是完整地址(以 http:// 或 https:// 开头)": "端点 URL 必须是完整地址(以 http:// 或 https:// 开头)", - "缓存读": "缓存读", - "缓存写": "缓存写", - "写": "写", - "根据 Anthropic 协定,/v1/messages 的输入 tokens 仅统计非缓存输入,不包含缓存读取与缓存写入 tokens。": "根据 Anthropic 协定,/v1/messages 的输入 tokens 仅统计非缓存输入,不包含缓存读取与缓存写入 tokens。", - "未匹配到模型,按回车键可将「{{name}}」作为自定义模型名添加": "未匹配到模型,按回车键可将「{{name}}」作为自定义模型名添加", - "分组相关设置": "分组相关设置", - "保存分组相关设置": "保存分组相关设置", - "此页面仅显示未设置价格或基础倍率的模型,设置后会自动从列表中移出": "此页面仅显示未设置价格或基础倍率的模型,设置后会自动从列表中移出", - "没有未设置定价的模型": "没有未设置定价的模型", - "当前没有未设置定价的模型": "当前没有未设置定价的模型", - "模型计费编辑器": "模型计费编辑器", - "价格摘要": "价格摘要", - "当前提示": "当前提示", - "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。", - "当前未启用,需要时再打开即可。": "当前未启用,需要时再打开即可。", - "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。", - "补全价格已锁定": "补全价格已锁定", - "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。", - "后:": "后:", - "这些价格都是可选项,不填也可以。": "这些价格都是可选项,不填也可以。", - "配置:": "配置:", - "请先开启并填写音频输入价格。": "请先开启并填写音频输入价格。", - "输入模型名称,例如 gpt-4.1": "输入模型名称,例如 gpt-4.1", - "当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。": "当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。", - "当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。": "当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。", - "按量计费下需要先填写输入价格,才能保存其它价格项。": "按量计费下需要先填写输入价格,才能保存其它价格项。", - "填写音频补全价格前,需要先填写音频输入价格。": "填写音频补全价格前,需要先填写音频输入价格。", - "模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率": "模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率", - "模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率": "模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率", - "批量应用当前模型价格": "批量应用当前模型价格", - "请先选择一个作为模板的模型": "请先选择一个作为模板的模型", - "请先勾选需要批量设置的模型": "请先勾选需要批量设置的模型", - "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型", - "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。", - "适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。": "适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。", - "已勾选": "已勾选", - "当前编辑": "当前编辑", - "已勾选 {{count}} 个模型": "已勾选 {{count}} 个模型", - "基础价格": "基础价格", - "扩展价格": "扩展价格", - "额外价格项": "额外价格项", - "补全价格": "补全价格", - "缓存读取价格": "缓存读取价格", - "缓存创建价格": "缓存创建价格", - "图片输入价格": "图片输入价格", - "音频输入价格": "音频输入价格", - "音频补全价格": "音频补全价格", - "适合 MJ / 任务类等按次收费模型。": "适合 MJ / 任务类等按次收费模型。", - "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。", - "计费显示模式": "计费显示模式", - "价格模式(默认)": "价格模式(默认)", - "模型价格 {{symbol}}{{price}} / 次": "模型价格 {{symbol}}{{price}} / 次", - "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "模型价格:{{symbol}}{{price}} / 次": "模型价格:{{symbol}}{{price}} / 次", - "按次:{{symbol}}{{price}}": "按次:{{symbol}}{{price}}", - "实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)", - "缓存读取价格:{{symbol}}{{price}} / 1M tokens": "缓存读取价格:{{symbol}}{{price}} / 1M tokens", - "缓存读取价格 {{symbol}}{{price}} / 1M tokens": "缓存读取价格 {{symbol}}{{price}} / 1M tokens", - "缓存创建价格:{{symbol}}{{price}} / 1M tokens": "缓存创建价格:{{symbol}}{{price}} / 1M tokens", - "缓存创建价格 {{symbol}}{{price}} / 1M tokens": "缓存创建价格 {{symbol}}{{price}} / 1M tokens", - "5m缓存创建价格:{{symbol}}{{price}} / 1M tokens": "5m缓存创建价格:{{symbol}}{{price}} / 1M tokens", - "5m缓存创建价格 {{symbol}}{{price}} / 1M tokens": "5m缓存创建价格 {{symbol}}{{price}} / 1M tokens", - "1h缓存创建价格:{{symbol}}{{price}} / 1M tokens": "1h缓存创建价格:{{symbol}}{{price}} / 1M tokens", - "1h缓存创建价格 {{symbol}}{{price}} / 1M tokens": "1h缓存创建价格 {{symbol}}{{price}} / 1M tokens", - "图片输入价格:{{symbol}}{{price}} / 1M tokens": "图片输入价格:{{symbol}}{{price}} / 1M tokens", - "图片输入价格 {{symbol}}{{price}} / 1M tokens": "图片输入价格 {{symbol}}{{price}} / 1M tokens", - "输入价格 {{symbol}}{{price}} / 1M tokens": "输入价格 {{symbol}}{{price}} / 1M tokens", - "音频输入价格:{{symbol}}{{price}} / 1M tokens": "音频输入价格:{{symbol}}{{price}} / 1M tokens", - "音频补全价格:{{symbol}}{{price}} / 1M tokens": "音频补全价格:{{symbol}}{{price}} / 1M tokens", - "Web 搜索调用 {{webSearchCallCount}} 次": "Web 搜索调用 {{webSearchCallCount}} 次", - "文件搜索调用 {{fileSearchCallCount}} 次": "文件搜索调用 {{fileSearchCallCount}} 次", - "图片倍率 {{imageRatio}}": "图片倍率 {{imageRatio}}", - "音频倍率 {{audioRatio}}": "音频倍率 {{audioRatio}}", - "普通输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "普通输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "缓存输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "缓存输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "Web 搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Web 搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", - "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", - "图片生成:1 次 * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "图片生成:1 次 * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", - "合计:{{total}}": "合计:{{total}}", - "模型倍率 {{modelRatio}},补全倍率 {{completionRatio}},音频倍率 {{audioRatio}},音频补全倍率 {{audioCompletionRatio}},{{cachePart}}{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},补全倍率 {{completionRatio}},音频倍率 {{audioRatio}},音频补全倍率 {{audioCompletionRatio}},{{cachePart}}{{ratioType}} {{ratio}}", - "文字输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "文字输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}", - "模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},{{ratioType}} {{ratio}}", - "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", - "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "空": "空", - "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", - "模型价格:{{symbol}}{{price}}": "模型价格:{{symbol}}{{price}}", - "模型价格 {{price}}": "模型价格 {{price}}", - "缓存读 {{price}} / 1M tokens": "缓存读 {{price}} / 1M tokens", - "5m缓存创建 {{price}} / 1M tokens": "5m缓存创建 {{price}} / 1M tokens", - "1h缓存创建 {{price}} / 1M tokens": "1h缓存创建 {{price}} / 1M tokens", - "缓存创建 {{price}} / 1M tokens": "缓存创建 {{price}} / 1M tokens", - "图片输入 {{price}} / 1M tokens": "图片输入 {{price}} / 1M tokens", - "输入 {{price}} / 1M tokens": "输入 {{price}} / 1M tokens", - "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", - "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", - "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", - "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", - "Key": "Key", - "Key 摘要": "Key 摘要", - "扣费": "扣费", - "渠道亲和性": "渠道亲和性", - "由订阅抵扣": "由订阅抵扣", - "规则": "规则", - "订阅抵扣": "订阅抵扣", - "违规扣费": "违规扣费", - "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}", - "图片输入价格:{{symbol}}{{total}} / 1M tokens": "图片输入价格:{{symbol}}{{total}} / 1M tokens", - "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "模型价格 {{symbol}}{{price}} / 次 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "模型价格 {{symbol}}{{price}} / 次 * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "缓存读取价格:{{symbol}}{{total}} / 1M tokens": "缓存读取价格:{{symbol}}{{total}} / 1M tokens", - "补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "补全 {{completion}} tokens * 输出倍率 {{completionRatio}}", - "补全倍率 {{completionRatio}}": "补全倍率 {{completionRatio}}", - "输入价格:{{symbol}}{{price}} / 1M tokens": "输入价格:{{symbol}}{{price}} / 1M tokens", - "输出价格 {{symbol}}{{price}} / 1M tokens": "输出价格 {{symbol}}{{price}} / 1M tokens", - "输出价格:{{symbol}}{{price}} / 1M tokens": "输出价格:{{symbol}}{{price}} / 1M tokens", - "输出价格:{{symbol}}{{total}} / 1M tokens": "输出价格:{{symbol}}{{total}} / 1M tokens", - "复制密钥": "复制密钥", - "复制连接信息": "复制连接信息", - "检测到剪贴板中的连接信息": "检测到剪贴板中的连接信息", - "自动填入": "自动填入", - "忽略": "忽略", - "从剪贴板粘贴配置": "从剪贴板粘贴配置", - "剪贴板中未检测到连接信息": "剪贴板中未检测到连接信息", - "连接信息已填入": "连接信息已填入", - "无法读取剪贴板": "无法读取剪贴板", - "页面渲染出错,请刷新页面重试": "页面渲染出错,请刷新页面重试", - "刷新页面": "刷新页面" + "默认补全倍率": "默认补全倍率" } } diff --git a/web/src/i18n/locales/zh-TW.json b/web/src/i18n/locales/zh-TW.json index 1f9351c6..6ff17075 100644 --- a/web/src/i18n/locales/zh-TW.json +++ b/web/src/i18n/locales/zh-TW.json @@ -6,26 +6,50 @@ " 个模型设置相同的值": " 個模型設定相同的值", " 吗?": " 嗎?", " 秒": " 秒", - "$/1M tokens": "$/1M tokens", + " 秒。": "", + ",当前无生效订阅,将自动使用钱包": ",當前無生效訂閱,將自動使用錢包", + ",时间:": ",時間:", + ",点击更新": ",點擊更新", + "(共 {{total}} 个,省略 {{omit}} 个)": "", + "(共 {{total}} 个)": "", + "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(當前僅支援易支付接口,預設使用上方伺服器位址作為回調位址!)", "(筛选后显示 {{count}} 条)_other": "(篩選後顯示 {{count}} 條)", "(输入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "(輸入 {{input}} tokens / 1M tokens * {{symbol}}{{price}}", "(输入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音频输入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}": "(輸入 {{nonAudioInput}} tokens / 1M tokens * {{symbol}}{{price}} + 音訊輸入 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioPrice}}", "(输入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}": "(輸入 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 快取 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}}", "(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(輸入 {{nonImageInput}} tokens + 圖片輸入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}", + "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "[最多請求次數]和[最多請求完成次數]的最大值為2147483647。", + "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[最多請求次數]必須大於等於0,[最多請求完成次數]必須大於等於1。", + "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", + "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", + "{{count}} 项操作_other": "", + "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", + "{{name}} ID": "{{name}} ID", + "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", + "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", + "「用户可选」决定用户创建令牌时能否自主选择该分组。未勾选的分组只能由管理员在后台分配给用户,不会出现在用户的令牌创建页面中。": "「使用者可選」決定使用者建立令牌時能否自主選擇該分組。未勾選的分組只能由管理員在後台分配給使用者,不會出現在使用者的令牌建立頁面中。", "/api/option/": "/api/option/", + "• 视频服务商的跨域限制": "• 影片服務商的跨域限制", + "• 防盗链保护机制": "• 防盜鏈保護機制", + "• 需要特定的请求头或认证": "• 需要特定的請求頭或認證", + "© {{currentYear}}": "© {{currentYear}}", + "| 基于": "| 基於", + "$/1M tokens": "$/1M tokens", + "$/次": "", "0 - 最低": "0 - 最低", "0 表示不限": "0 表示不限", "0.002-1之间的小数": "0.002-1之間的小數", "0.1以上的小数": "0.1以上的小數", "1. 管理员在此创建分组并设置倍率": "1. 管理員在此建立分組並設定倍率", + "1) 点击「打开授权页面」完成登录;2) 浏览器会跳转到 localhost(页面打不开也没关系);3) 复制地址栏完整 URL 粘贴到下方;4) 点击「生成并填入」。": "", "10 - 最高": "10 - 最高", "1h缓存创建 {{price}} / 1M tokens": "1h快取建立 {{price}} / 1M tokens", "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "1h快取建立 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "1h快取建立 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})", + "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "1h快取建立:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h快取建立倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", "1h缓存创建价格 {{symbol}}{{price}} / 1M tokens": "1h快取建立價格 {{symbol}}{{price}} / 1M tokens", "1h缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h缓存创建倍率: {{cacheCreationRatio1h}})": "1h快取建立價格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h快取建立倍率: {{cacheCreationRatio1h}})", "1h缓存创建价格:{{symbol}}{{price}} / 1M tokens": "1h快取建立價格:{{symbol}}{{price}} / 1M tokens", - "1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "1h快取建立:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h快取建立倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}", "2 - 低": "2 - 低", "2. 管理员在用户管理中将用户分配到对应分组(如 vip)": "2. 管理員在使用者管理中將使用者分配到對應分組(如 vip)", "2025年5月10日后添加的渠道,不需要再在部署的时候移除模型名称中的\".\"": "2025年5月10日後添加的管道,不需要再在部署的時候移除模型名稱中的\".\"", @@ -36,22 +60,25 @@ "5m缓存创建 {{price}} / 1M tokens": "5m快取建立 {{price}} / 1M tokens", "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "5m快取建立 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "5m快取建立 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})", + "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "5m快取建立:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m快取建立倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", "5m缓存创建价格 {{symbol}}{{price}} / 1M tokens": "5m快取建立價格 {{symbol}}{{price}} / 1M tokens", "5m缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m缓存创建倍率: {{cacheCreationRatio5m}})": "5m快取建立價格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m快取建立倍率: {{cacheCreationRatio5m}})", "5m缓存创建价格:{{symbol}}{{price}} / 1M tokens": "5m快取建立價格:{{symbol}}{{price}} / 1M tokens", - "5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "5m快取建立:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m快取建立倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}", "8 - 高": "8 - 高", "AGPL v3.0协议": "AGPL v3.0協議", "AI 对话": "AI 對話", "AI模型测试环境": "AI模型測試環境", "AI模型配置": "AI模型設定", "AK/SK 模式:使用 AccessKey 和 SecretAccessKey;API Key 模式:使用 API Key": "AK/SK 模式:使用 AccessKey 和 SecretAccessKey;API Key 模式:使用 API Key", + "anthropic-beta JSON 示例": "", "API Key": "API Key", "API Key 模式下不支持批量创建": "API Key 模式下不支援批量建立", "API Key 验证失败": "API Key 驗證失敗", "API Key 验证成功!连接到 io.net 服务正常": "API Key 驗證成功!連接到 io.net 服務正常", "API 地址和相关配置": "API 位址和相關設定", "API 密钥": "API 密鑰", + "API 密钥 (沙盒)": "", + "API 密钥 (生产)": "", "API 文档": "API 文件", "API 配置": "API 設定", "API令牌管理": "API令牌管理", @@ -61,16 +88,19 @@ "API地址": "API位址", "API渠道配置": "API管道設定", "API端点": "API端點", - "Authorization Endpoint": "Authorization Endpoint", "Authorization callback URL 填": "Authorization callback URL 填", + "Authorization Endpoint": "Authorization Endpoint", + "auto分组调用链路": "auto分組調用鏈路", "Bark推送URL": "Bark推送URL", "Bark推送URL必须以http://或https://开头": "Bark推送URL必須以http://或https://開頭", "Bark通知": "Bark通知", "Basic Auth 头": "Basic Auth 頭", - "CPU 使用率超过此值时拒绝请求": "CPU 使用率超過此值時拒絕請求", - "CPU 阈值 (%)": "CPU 閾值 (%)", + "Cached tokens": "", + "Cached tokens 占比口径由后端返回:Claude 语义按 cached/(prompt+cached),其余按 cached/prompt。": "", "Changing batch type to:": "Changing batch type to:", + "ChatCompletions→Responses 兼容配置": "", "ChatCompletions→Responses 兼容配置(Beta)": "ChatCompletions→Responses 兼容設定(Beta)", + "Claude 强制 beta=true": "", "Claude会在原有请求头基础上追加这些值,不会覆盖已有同名请求头;重复值会自动忽略。": "Claude會在原有請求頭基礎上追加這些值,不會覆蓋已有同名請求頭;重複值會自動忽略。", "Claude思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Claude思考相容 BudgetTokens = MaxTokens * BudgetTokens 百分比", "Claude设置": "Claude設定", @@ -78,22 +108,35 @@ "Claude请求头追加": "Claude請求頭追加", "Client ID": "Client ID", "Client Secret": "Client Secret", + "Codex 授权": "", + "Codex 渠道不支持批量创建": "", + "common.changeLanguage": "common.changeLanguage", + "Completion tokens": "", + "Configuration": "", + "context_int/context_string 从请求上下文读取;gjson 从入口请求的 JSON body 按 gjson path 读取。": "", + "CPU 使用率超过此值时拒绝请求": "CPU 使用率超過此值時拒絕請求", + "CPU 阈值 (%)": "CPU 閾值 (%)", "Creem API 密钥,敏感信息不显示": "Creem API 密鑰,敏感資訊不顯示", "Creem Setting Tips": "Creem 只支援預設的固定金額產品,這產品以及價格需要提前在Creem網站內建立設定,所以不支援自訂動態金額儲值。在Creem端設定產品的名字以及價格,獲取Product Id 後填到下面的產品,在new-api為該產品設定儲值額度,以及展示價格。", "Creem 介绍": "Creem 是一個簡單的支付處理平臺,支援固定金額產品銷售,以及訂閱銷售。", "Creem 充值": "Creem 儲值", "Creem 设置": "Creem 設定", + "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。": "default 和 vip 只能由管理員在「使用者管理」中分配給使用者。適用於按使用者等級定價、內部測試等不希望使用者自主選擇的場景。", + "default为默认设置,可单独设置每个分类的安全等级": "default為預設設定,可單獨設定每個分類的安全等級", + "default为默认设置,可单独设置每个模型的版本": "default為預設設定,可單獨設定每個模型的版本", "Dify渠道只适配chatflow和agent,并且agent不支持图片!": "Dify管道只相容chatflow和agent,並且agent不支援圖片!", "Discord": "Discord", "Discord Client ID": "Discord Client ID", "Discord Client Secret": "Discord Client Secret", "Discord ID": "Discord ID", + "Discovery claims": "", + "Discovery scopes": "", + "Discovery 建议 scopes:": "", "EUR (欧元)": "EUR (歐元)", + "false": "false", "GC 已执行": "GC 已執行", "GC 执行失败": "GC 執行失敗", "GC 次数": "GC 次數", - "GPU/容器": "GPU/容器", - "GPU数量": "GPU數量", "Gemini安全设置": "Gemini安全設定", "Gemini思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比": "Gemini思考相容 BudgetTokens = MaxTokens * BudgetTokens 百分比", "Gemini思考适配设置": "Gemini思考相容設定", @@ -108,32 +151,48 @@ "Gotify服务器地址": "Gotify伺服器位址", "Gotify服务器地址必须以http://或https://开头": "Gotify伺服器位址必須以http://或https://開頭", "Gotify通知": "Gotify通知", + "GPU/容器": "GPU/容器", + "GPU数量": "GPU數量", "Grok设置": "Grok設定", "Homepage URL 填": "Homepage URL 填", "ID": "ID", + "include_obfuscation 用于控制 Responses 流混淆字段。默认关闭以避免客户端关闭该安全保护": "", + "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "inference_geo 字段用於控制 Claude 資料駐留推理區域。預設關閉以避免未經授權透傳地域資訊", "IP": "IP", "IP白名单": "IP白名單", "IP白名单(支持CIDR表达式)": "IP白名單(支援CIDR表達式)", "IP限制": "IP限制", "IP黑名单": "IP黑名單", "JSON": "JSON", + "JSON 已格式化": "", + "JSON 文本": "", + "JSON 无效": "", "JSON 格式参考": "JSON 格式參考", + "JSON 模式": "", "JSON 模式支持手动输入或上传服务账号 JSON": "JSON 模式支援手動輸入或上傳服務帳號 JSON", "JSON格式密钥,请确保格式正确": "JSON格式密鑰,請確保格式正確", "JSON格式错误": "JSON格式錯誤", "JSON编辑": "JSON編輯", "JSON解析错误:": "JSON解析錯誤:", "Key": "Key", + "Key 或 Path": "", + "Key 指纹": "", "Key 摘要": "Key 摘要", + "Key 来源": "", + "Key 来源类型": "", "Linux DO Client ID": "Linux DO Client ID", "Linux DO Client Secret": "Linux DO Client Secret", "LinuxDO": "LinuxDO", "LinuxDO ID": "LinuxDO ID", "Logo 图片地址": "Logo 圖片位址", - "MIT许可证": "MIT許可證", "Midjourney 任务记录": "Midjourney 任務記錄", + "MIT许可证": "MIT許可證", "New API项目仓库地址:": "New API項目倉庫位址:", + "NewAPI 默认不会将入口请求的 User-Agent 透传到上游渠道;该条件仅用于识别访问本站点的客户端。": "", + "OAuth Client ID": "", + "OAuth Client Secret": "", "OAuth 登录失败:": "OAuth 登錄失敗:", + "OAuth 端点": "", "OAuth 配置错误:授权端点必须是完整的 URL(以 http:// 或 https:// 开头)": "OAuth 設定錯誤:授權端點必須是完整的 URL(以 http:// 或 https:// 開頭)", "OIDC": "OIDC", "OIDC ID": "OIDC ID", @@ -146,18 +205,34 @@ "Passkey 注册失败,请重试": "Passkey 註冊失敗,請重試", "Passkey 注册成功": "Passkey 註冊成功", "Passkey 登录": "Passkey 登錄", + "Pay Method Name": "", + "Pay Method Type": "", "Ping间隔(秒)": "Ping間隔(秒)", + "POST 参数": "", + "price_xxx 的商品价格 ID,新建产品后可获得": "price_xxx 的商品價格 ID,新建產品後可獲得", + "Prompt cache hit tokens": "", + "Prompt tokens": "", "Reasoning Effort": "Reasoning Effort", + "Request ID": "", + "RSA 私钥 (沙盒)": "", + "RSA 私钥 (生产)": "", + "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "safety_identifier 字段用於幫助 OpenAI 識別可能違反使用政策的應用程式使用者。預設關閉以保護使用者隱私", + "Scopes(可选)": "", + "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "service_tier 字段用於指定服務層級,允許透傳可能導致實際計費高於預期。預設關閉以避免額外費用", + "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "sk_xxx 或 rk_xxx 的 Stripe 密鑰,敏感資訊不顯示", "SMTP 发送者邮箱": "SMTP 發送者信箱", "SMTP 服务器地址": "SMTP 伺服器位址", "SMTP 端口": "SMTP 端口", "SMTP 访问凭证": "SMTP 訪問憑證", "SMTP 账户": "SMTP 帳號", + "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "speed 字段用於控制 Claude 推理速度模式。預設關閉以避免意外切換到 fast 模式", "SSE 事件": "SSE 事件", "SSE数据流": "SSE數據流", "SSRF防护开关详细说明": "總開關控制是否啟用SSRF防護功能。關閉後將跳過所有SSRF檢查,允許訪問任意URL。⚠️ 僅在完全信任環境中關閉此功能。", "SSRF防护设置": "SSRF防護設定", "SSRF防护详细说明": "SSRF防護可防止惡意使用者利用您的伺服器訪問內網資源。您可以設定受信任域名/IP的白名單,並限制允許的端口。適用於檔案下載、Webhook回調和通知功能。", + "standard 已被移除,vip 用户看不到": "standard 已被移除,vip 使用者看不到", + "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "store 字段用於授權 OpenAI 存儲請求數據以評估和優化產品。預設關閉,開啟後可能導致 Codex 無法正常使用", "Stripe 设置": "Stripe 設定", "Stripe/Creem 商品ID(可选)": "Stripe/Creem 商品ID(可選)", "Stripe/Creem 需在第三方平台创建商品并填入 ID": "Stripe/Creem 需在第三方平臺建立商品並填入 ID", @@ -166,19 +241,34 @@ "Telegram Bot 名称": "Telegram Bot 名稱", "Telegram ID": "Telegram ID", "Token Endpoint": "Token Endpoint", + "token 会按倍率换算成“额度/次数”,请求结束后再做差额结算(补扣/返还)。": "", + "Total tokens": "", + "true": "true", + "TTL(秒,0 表示默认)": "", + "TTL(秒)": "", "Turnstile Secret Key": "Turnstile Secret Key", "Turnstile Site Key": "Turnstile Site Key", - "URL链接": "URL連結", - "USD (美元)": "USD (美元)", "Unix时间戳": "Unix時間戳", "Uptime Kuma地址": "Uptime Kuma位址", "Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)": "Uptime Kuma監控分類管理,可以設定多個監控分類用於服務狀態展示(最多20個)", + "URL 标识,只能包含小写字母、数字和连字符": "", + "URL链接": "URL連結", + "USD (美元)": "USD (美元)", "User Info Endpoint": "User Info Endpoint", + "User-Agent include(每行一个,可不写)": "", + "Value 正则": "", "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AI 不支援 functionResponse.id 字段,開啟後將自動移除該字段", - "WeChat Server 服务器地址": "WeChat Server 伺服器位址", - "WeChat Server 访问凭证": "WeChat Server 訪問憑證", - "Web 搜索调用 {{webSearchCallCount}} 次": "Web 搜尋呼叫 {{webSearchCallCount}} 次", + "Waffo API 参数,可空,例如:CREDITCARD,DEBITCARD(最多64位)": "", + "Waffo API 参数,可空(最多64位)": "", + "Waffo 充值": "", + "Waffo 充值的最低数量,默认 1": "", + "Waffo 公钥 (沙盒)": "", + "Waffo 公钥 (生产)": "", + "Waffo 商户 ID": "", + "Waffo 是一个支付聚合平台,支持多种支付方式。": "", + "Waffo 设置": "", "Web 搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Web 搜尋:{{count}} / 1K * 單價 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", + "Web 搜索调用 {{webSearchCallCount}} 次": "Web 搜尋呼叫 {{webSearchCallCount}} 次", "Webhook 密钥": "Webhook 密鑰", "Webhook 签名密钥": "Webhook 簽名密鑰", "Webhook地址": "Webhook位址", @@ -186,59 +276,38 @@ "Webhook请求结构说明": "Webhook請求結構說明", "Webhook通知": "Webhook通知", "Web搜索价格:{{symbol}}{{price}} / 1K 次": "Web搜尋價格:{{symbol}}{{price}} / 1K 次", + "WeChat Server 服务器地址": "WeChat Server 伺服器位址", + "WeChat Server 访问凭证": "WeChat Server 訪問憑證", "Well-Known URL": "Well-Known URL", "Well-Known URL 必须以 http:// 或 https:// 开头": "Well-Known URL 必須以 http:// 或 https:// 開頭", + "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示": "whsec_xxx 的 Webhook 簽名密鑰,敏感資訊不顯示", "Worker地址": "Worker位址", "Worker密钥": "Worker密鑰", - "[最多请求次数]和[最多请求完成次数]的最大值为2147483647。": "[最多請求次數]和[最多請求完成次數]的最大值為2147483647。", - "[最多请求次数]必须大于等于0,[最多请求完成次数]必须大于等于1。": "[最多請求次數]必須大於等於0,[最多請求完成次數]必須大於等於1。", - "auto分组调用链路": "auto分組調用鏈路", - "common.changeLanguage": "common.changeLanguage", - "default 和 vip 只能由管理员在「用户管理」中分配给用户。适用于按用户等级定价、内部测试等不希望用户自主选择的场景。": "default 和 vip 只能由管理員在「使用者管理」中分配給使用者。適用於按使用者等級定價、內部測試等不希望使用者自主選擇的場景。", - "default为默认设置,可单独设置每个分类的安全等级": "default為預設設定,可單獨設定每個分類的安全等級", - "default为默认设置,可单独设置每个模型的版本": "default為預設設定,可單獨設定每個模型的版本", - "false": "false", - "inference_geo 字段用于控制 Claude 数据驻留推理区域。默认关闭以避免未经授权透传地域信息": "inference_geo 字段用於控制 Claude 資料駐留推理區域。預設關閉以避免未經授權透傳地域資訊", - "price_xxx 的商品价格 ID,新建产品后可获得": "price_xxx 的商品價格 ID,新建產品後可獲得", - "safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私": "safety_identifier 字段用於幫助 OpenAI 識別可能違反使用政策的應用程式使用者。預設關閉以保護使用者隱私", - "service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用": "service_tier 字段用於指定服務層級,允許透傳可能導致實際計費高於預期。預設關閉以避免額外費用", - "speed 字段用于控制 Claude 推理速度模式。默认关闭以避免意外切换到 fast 模式": "speed 字段用於控制 Claude 推理速度模式。預設關閉以避免意外切換到 fast 模式", - "sk_xxx 或 rk_xxx 的 Stripe 密钥,敏感信息不显示": "sk_xxx 或 rk_xxx 的 Stripe 密鑰,敏感資訊不顯示", - "standard 已被移除,vip 用户看不到": "standard 已被移除,vip 使用者看不到", - "store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用": "store 字段用於授權 OpenAI 存儲請求數據以評估和優化產品。預設關閉,開啟後可能導致 Codex 無法正常使用", - "true": "true", - "whsec_xxx 的 Webhook 签名密钥,敏感信息不显示": "whsec_xxx 的 Webhook 簽名密鑰,敏感資訊不顯示", - "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}": "{\n \"default\": [200, 100],\n \"vip\": [0, 1000]\n}", - "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}": "{{inputDesc}} + {{outputDesc}}{{extraServices}} = {{symbol}}{{total}}", - "{{name}} ID": "{{name}} ID", - "{{ratioType}} {{ratio}}": "{{ratioType}} {{ratio}}", - "{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x", - "| 基于": "| 基於", - "© {{currentYear}}": "© {{currentYear}}", - "• 视频服务商的跨域限制": "• 影片服務商的跨域限制", - "• 防盗链保护机制": "• 防盜鏈保護機制", - "• 需要特定的请求头或认证": "• 需要特定的請求頭或認證", - "「用户可选」决定用户创建令牌时能否自主选择该分组。未勾选的分组只能由管理员在后台分配给用户,不会出现在用户的令牌创建页面中。": "「使用者可選」決定使用者建立令牌時能否自主選擇該分組。未勾選的分組只能由管理員在後台分配給使用者,不會出現在使用者的令牌建立頁面中。", "一个月": "一個月", "一天": "一天", "一小时": "一小時", "一次调用消耗多少刀,优先级大于模型倍率": "一次調用消耗多少刀,優先級大於模型倍率", - "一行一个屏蔽词,不需要符号分割": "一行一個屏蔽詞,不需要符號分割", "一行一个,不区分大小写": "一行一個,不區分大小寫", + "一行一个屏蔽词,不需要符号分割": "一行一個屏蔽詞,不需要符號分割", "一键填充到 FluentRead": "一鍵填充到 FluentRead", "三种操作的区别:": "三種操作的區別:", "上一个表单块": "上一個表單塊", "上一步": "上一步", + "上传 PNG/JPG/SVG 图片,建议尺寸 ≤ 128×128px": "", + "上传图片": "", "上次保存: ": "上次儲存: ", + "上次检测到可加入模型": "", + "上次检测时间": "", "上游倍率同步": "上游倍率同步", "上游模型管理": "上游模型管理", "上游返回": "上游返回", "下一个表单块": "下一個表單塊", + "下一次重置": "下一次重置", "下一步": "下一步", "下午好": "午安", "下载日志": "下載日誌", "下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "下方會顯示此模型儲存後將寫入哪些後端欄位,方便與原始 JSON 編輯框保持一致。", + "下面是带注释的示例,仅用于参考;实际保存时请删除注释。": "", "不会出现": "不會出現", "不再提醒": "不再提醒", "不勾选用户可选": "不勾選使用者可選", @@ -252,7 +321,9 @@ "不更改": "不更改", "不配置特殊倍率时:": "不配置特殊倍率時:", "不配置规则时,所有用户看到的下拉框一样:": "不配置規則時,所有使用者看到的下拉框一樣:", + "不重置": "", "不重试": "不重試", + "不限": "", "不限制": "不限制", "与本地相同": "與本地相同", "专属倍率": "專屬倍率", @@ -261,20 +332,22 @@ "两次输入的密码不一致": "兩次輸入的密碼不一致", "两次输入的密码不一致!": "兩次輸入的密碼不一致!", "两步验证": "兩步驗證", + "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。": "兩步驗證(2FA)為您的帳號提供額外的安全保護。啟用後,登錄時需要輸入密碼和驗證器應用生成的驗證碼。", "两步验证启用成功!": "兩步驗證啟用成功!", "两步验证已禁用": "兩步驗證已禁用", "两步验证设置": "兩步驗證設定", - "两步验证(2FA)为您的账户提供额外的安全保护。启用后,登录时需要输入密码和验证器应用生成的验证码。": "兩步驗證(2FA)為您的帳號提供額外的安全保護。啟用後,登錄時需要輸入密碼和驗證器應用生成的驗證碼。", "个": "個", "个GPU": "個GPU", "个人中心": "個人中心", "个人中心区域": "個人中心區域", "个人信息设置": "個人資訊設定", "个人设置": "個人設定", + "个字段": "", "个实例": "個實例", "个已过期": "個已過期", "个性化设置": "個性化設定", "个性化设置左侧边栏的显示内容": "個性化設定左側邊欄的顯示內容", + "个月": "", "个未配置模型": "個未設定模型", "个模型": "個模型", "个生效中": "個生效中", @@ -296,6 +369,7 @@ "为了保护账户安全,请验证您的两步验证码。": "為了保護帳號安全,請驗證您的兩步驗證碼。", "为了保护账户安全,请验证您的身份。": "為了保護帳號安全,請驗證您的身份。", "为什么 default 不勾选「用户可选」?": "為什麼 default 不勾選「使用者可選」?", + "为保证匹配准确,请确保客户端直连本站点(避免反向代理/网关改写 User-Agent)。": "", "为特定用户分组配置可用分组的增减规则。「添加」为该分组新增可用分组,「移除」移除默认可用分组,「追加」直接追加分组": "為特定使用者分組配置可用分組的增減規則。「新增」為該分組新增可用分組,「移除」移除預設可用分組,「追加」直接追加分組", "为空则默认使用服务器地址,多个 Origin 用逗号分隔,例如 https://newapi.pro,https://newapi.com ,注意不能携带[],需使用https": "為空則預設使用伺服器位址,多個 Origin 用逗號分隔,例如 https://newapi.pro,https://newapi.com ,注意不能攜帶[],需使用https", "主页链接填": "首頁連結填", @@ -315,19 +389,26 @@ "仅修改展示粒度,统计精确到小时": "僅修改展示粒度,統計精確到小時", "仅密钥": "僅密鑰", "仅对自定义模型有效": "僅對自訂模型有效", + "仅当前层": "", "仅当自动禁用开启时有效,关闭后不会自动禁用该渠道": "僅當自動禁用開啟時有效,關閉後不會自動禁用該管道", + "仅提交已勾选": "", "仅支持": "僅支援", + "仅支持 JSON 对象,必须包含 access_token 与 account_id": "", "仅支持 JSON 文件": "僅支援 JSON 檔案", "仅支持 JSON 文件,支持多文件": "僅支援 JSON 檔案,支援多檔案", "仅支持 OpenAI 接口格式": "僅支援 OpenAI 接口格式", + "仅显示已绑定": "", "仅显示矛盾倍率": "僅顯示矛盾倍率", + "仅检测上游模型更新": "", "仅用于开发环境,生产环境应使用 HTTPS": "僅用於開發環境,生產環境應使用 HTTPS", "仅用订阅": "僅用訂閱", "仅用钱包": "僅用錢包", + "仅管理员可用。开启后,当系统定时检测全部渠道发现上游模型变更或检测异常时,将按你选择的通知方式发送汇总通知;渠道或模型过多时会自动省略部分明细。": "", "仅重置配置": "僅重置設定", "今日关闭": "今日關閉", "今日已签到": "今日已簽到", "今日已签到,累计签到": "今日已簽到,累計簽到", + "仍有未处理项": "", "从 0.5 降到 0.3": "從 0.5 降到 0.3", "从剪贴板粘贴配置": "從剪貼簿貼上設定", "从官方模型库同步": "從官方模型庫同步", @@ -338,10 +419,11 @@ "代理设置": "代理設定", "代码已复制到剪贴板": "程式碼已複製到剪貼板", "令牌": "令牌", + "令牌不存在": "", "令牌分组": "令牌分組", + "令牌分组,默认为用户的分组": "令牌分組,預設為使用者的分組", "令牌分组设为 auto 时,按以下顺序依次尝试选择可用分组,排在前面的优先级更高": "令牌分組設為 auto 時,按以下順序依次嘗試選擇可用分組,排在前面的優先級更高", "令牌分组设为 auto 时,系统按优先级顺序自动选择一个可用分组。": "令牌分組設為 auto 時,系統按優先級順序自動選擇一個可用分組。", - "令牌分组,默认为用户的分组": "令牌分組,預設為使用者的分組", "令牌创建成功,请在列表页面点击复制获取令牌!": "令牌建立成功,請在列表頁面點擊複製獲取令牌!", "令牌名称": "令牌名稱", "令牌已重置并已复制到剪贴板": "令牌已重置並已複製到剪貼板", @@ -354,8 +436,11 @@ "以及": "以及", "仪表盘设置": "儀表盤設定", "价格": "價格", + "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "價格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}", + "价格:${{price}} * {{ratioType}}:{{ratio}}": "價格:${{price}} * {{ratioType}}:{{ratio}}", "价格摘要": "價格摘要", "价格暂时不可用,请稍后重试": "價格暫時不可用,請稍後重試", + "价格模式": "", "价格模式(默认)": "價格模式(預設)", "价格计算中...": "價格計算中...", "价格计算失败": "價格計算失敗", @@ -364,8 +449,7 @@ "价格设置方式": "價格設定方式", "价格重新计算中...": "價格重新計算中...", "价格预估": "價格預估", - "价格:${{price}} * {{ratioType}}:{{ratio}}": "價格:${{price}} * {{ratioType}}:{{ratio}}", - "价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}": "價格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}}", + "任一满足(OR)": "", "任务 ID": "任務 ID", "任务ID": "任務ID", "任务日志": "任務日誌", @@ -381,10 +465,15 @@ "余额充值管理": "餘額儲值管理", "作废": "作廢", "作废于": "作廢於", - "下一次重置": "下一次重置", "作废后该订阅将立即失效,历史记录不受影响。是否继续?": "作廢後該訂閱將立即失效,歷史記錄不受影響。是否繼續?", + "作用域": "", + "作用域:包含分组": "", + "作用域:包含模型名称": "", + "作用域:包含规则名称": "", "你似乎并没有修改什么": "你似乎並沒有修改什麼", "你可以在“自定义模型名称”处手动添加它们,然后点击填入后再提交,或者直接使用下方操作自动处理。": "你可以在「自訂模型名稱」處手動添加它們,然後點擊填入後再提交,或者直接使用下方操作自動處理。", + "你还没有处理{{type}}模型({{count}}个)。是否仅提交当前已勾选内容?_other": "", + "使用 {{name}} 继续": "使用 {{name}} 繼續", "使用 Discord 继续": "使用 Discord 繼續", "使用 GitHub 继续": "使用 GitHub 繼續", "使用 JSON 对象格式,格式为:{\"组名\": [最多请求次数, 最多请求完成次数]}": "使用 JSON 對象格式,格式為:{\"組名\": [最多請求次數, 最多請求完成次數]}", @@ -393,44 +482,62 @@ "使用 Passkey 实现免密且更安全的登录体验": "使用 Passkey 實現免密且更安全的登錄體驗", "使用 Passkey 登录": "使用 Passkey 登錄", "使用 Passkey 验证": "使用 Passkey 驗證", - "使用 {{name}} 继续": "使用 {{name}} 繼續", "使用 微信 继续": "使用 微信 繼續", "使用 用户名 注册": "使用 使用者名 註冊", "使用 邮箱或用户名 登录": "使用 信箱或使用者名 登錄", "使用ID排序": "使用ID排序", "使用分组": "使用分組", + "使用原生额度输入": "使用原生額度輸入", "使用日志": "使用日誌", "使用模式": "使用模式", "使用统计": "使用統計", - "使用认证器应用扫描二维码": "使用認證器應用掃描QR Code", "使用认证器应用(如 Google Authenticator、Microsoft Authenticator)扫描下方二维码:": "使用認證器應用(如 Google Authenticator、Microsoft Authenticator)掃描下方QR Code:", + "使用认证器应用扫描二维码": "使用認證器應用掃描QR Code", "使用说明": "使用說明", "例如": "例如", "例如 /var/cache/new-api": "例如 /var/cache/new-api", - "例如 https://docs.newapi.pro": "例如 https://docs.newapi.pro", "例如 €, £, Rp, ₩, ₹...": "例如 €, £, Rp, ₩, ₹...", + "例如 https://docs.newapi.pro": "例如 https://docs.newapi.pro", + "例如 https://example.com/api/waffo/webhook": "", + "例如 https://example.com/console/topup": "", + "例如:": "例如:", "例如: /bin/bash -c \"python app.py\"": "例如: /bin/bash -c \"python app.py\"", "例如: nginx:latest": "例如: nginx:latest", "例如: socks5://user:pass@host:port": "例如: socks5://user:pass@host:port", - "例如发卡网站的购买链接": "例如髮卡網站的購買連結", - "例如:": "例如:", "例如:-c": "例如:-c", "例如:/bin/bash": "例如:/bin/bash", "例如:0001": "例如:0001", "例如:1000": "例如:1000", "例如:100000": "例如:100000", - "例如:2000": "例如:2000", "例如:2,就是最低充值2$": "例如:2,就是最低儲值2$", + "例如:2000": "例如:2000", "例如:4.99": "例如:4.99", "例如:401, 403, 429, 500-599": "例如:401,403,429,500-599", "例如:7,就是7元/美金": "例如:7,就是7元/美金", + "例如:Credit Card": "", + "例如:email": "", "例如:example.com": "例如:example.com", + "例如:github / si:google / https://example.com/logo.png / 🐱": "", + "例如:GitHub Enterprise": "", + "例如:github-enterprise": "", + "例如:gpt-4.1-nano,regex:^claude-.*$,regex:^sora-.*$": "", + "例如:https://example.com/.well-known/openid-configuration": "", + "例如:https://gitea.example.com": "", "例如:https://yourdomain.com": "例如:https://yourdomain.com", + "例如:name、full_name": "", "例如:nginx:latest": "例如:nginx:latest", + "例如:preferred_username、login": "", "例如:preview": "例如:preview", "例如:prod_6I8rBerHpPxyoiU9WK4kot": "例如:prod_6I8rBerHpPxyoiU9WK4kot", + "例如:sub、id、data.user.id": "", "例如:基础套餐": "例如:基礎訂閱", + "例如:清理工具参数,避免上游校验错误": "", + "例如:该请求不满足准入策略": "", "例如:适合轻度使用": "例如:適合輕度使用", + "例如:需要等级 {{required}},你当前等级 {{current}}": "", + "例如(全渠道):": "", + "例如(指定渠道):": "", + "例如发卡网站的购买链接": "例如髮卡網站的購買連結", "供应商": "供應商", "供应商介绍": "供應商介紹", "供应商信息:": "供應商資訊:", @@ -441,6 +548,8 @@ "供应商更新成功!": "供應商更新成功!", "侧边栏管理(全局控制)": "側邊欄管理(全域控制)", "侧边栏设置保存成功": "側邊欄設定儲存成功", + "保前缀": "", + "保后缀": "", "保存": "儲存", "保存 Discord OAuth 设置": "儲存 Discord OAuth 設定", "保存 GitHub OAuth 设置": "儲存 GitHub OAuth 設定", @@ -456,8 +565,8 @@ "保存备用码": "儲存備用碼", "保存备用码以备不时之需": "儲存備用碼以備不時之需", "保存失败": "儲存失敗", - "保存失败:": "儲存失敗:", "保存失败,请重试": "儲存失敗,請重試", + "保存失败:": "儲存失敗:", "保存屏蔽词过滤设置": "儲存屏蔽詞過濾設定", "保存性能设置": "儲存性能設定", "保存成功": "儲存成功", @@ -472,12 +581,14 @@ "保存设置": "儲存設定", "保存通用设置": "儲存通用設定", "保存邮箱域名白名单设置": "儲存信箱域名白名單設定", + "保存预览": "", "保存额度设置": "儲存額度設定", "保留": "保留", - "保留天数": "保留天數", - "保留文件数": "保留檔案數", "保留最近N个文件": "保留最近N個檔案", "保留最近N天": "保留最近N天", + "保留原值(目标已有值时不覆盖)": "", + "保留天数": "保留天數", + "保留文件数": "保留檔案數", "修复数据库一致性": "修復資料庫一致性", "修改为": "修改為", "修改子渠道优先级": "修改子管道優先級", @@ -489,19 +600,22 @@ "倍率信息": "倍率資訊", "倍率是为了方便换算不同价格的模型": "倍率是為了方便換算不同價格的模型", "倍率模式": "倍率模式", + "倍率模式(默认)": "", "倍率用于计费乘数,勾选「用户可选」后用户可在创建令牌时选择该分组": "倍率用於計費乘數,勾選「使用者可選」後使用者可在建立令牌時選擇該分組", "倍率类型": "倍率類型", "假设再加两个分组 default 和 vip,但不勾选用户可选:": "假設再加兩個分組 default 和 vip,但不勾選使用者可選:", "偏好设置": "偏好設定", "停止测试": "停止測試", + "停止重试": "", "停用": "停用", "允许 AccountFilter 参数": "允許 AccountFilter 參數", "允许 HTTP 协议图片请求(适用于自部署代理)": "允許 HTTP 協議圖片請求(適用於自部署代理)", - "允许 Turnstile 用户校验": "允許 Turnstile 使用者校驗", + "允许 inference_geo 透传": "允許 inference_geo 透傳", "允许 safety_identifier 透传": "允許 safety_identifier 透傳", "允许 service_tier 透传": "允許 service_tier 透傳", - "允许 inference_geo 透传": "允許 inference_geo 透傳", "允许 speed 透传": "允許 speed 透傳", + "允许 stream_options.include_obfuscation 透传": "", + "允许 Turnstile 用户校验": "允許 Turnstile 使用者校驗", "允许不安全的 Origin(HTTP)": "允許不安全的 Origin(HTTP)", "允许回调(会泄露服务器 IP 地址)": "允許回調(會洩露伺服器 IP 位址)", "允许在 Stripe 支付中输入促销码": "允許在 Stripe 支付中輸入促銷碼", @@ -519,15 +633,16 @@ "允许通过密码进行注册": "允許透過密碼進行註冊", "允许通过密码进行登录": "允許透過密碼進行登錄", "允许通过微信登录 & 注册": "允許透過微信登錄 & 註冊", + "允许重试": "", "元": "元", "充值": "儲值", - "充值价格显示": "儲值價格顯示", "充值价格(x元/美金)": "儲值價格(x元/美金)", + "充值价格显示": "儲值價格顯示", "充值分组倍率": "儲值分組倍率", "充值分组倍率不是合法的 JSON 字符串": "儲值分組倍率不是合法的 JSON 字符串", "充值数量": "儲值數量", - "充值数量不能小于": "儲值數量不能小於", "充值数量,最低 ": "儲值數量,最低 ", + "充值数量不能小于": "儲值數量不能小於", "充值方式设置": "儲值方式設定", "充值方式设置不是合法的 JSON 字符串": "儲值方式設定不是合法的 JSON 字符串", "充值确认": "儲值確認", @@ -536,13 +651,15 @@ "充值金额折扣配置不是合法的 JSON 对象": "儲值金額折扣設定不是合法的 JSON 對象", "充值链接": "儲值連結", "充值额度": "儲值額度", + "先填写配置,再自动填充 OAuth 端点,能显著减少手工输入": "", + "先搜索,再一键复制字段名或填入当前规则。字段名为系统内部路径,可直接用于路径 / 来源 / 目标。": "", "免责声明:仅限个人使用,请勿分发或共享任何凭证。该渠道存在前置条件与使用门槛,请在充分了解流程与风险后使用,并遵守 OpenAI 的相关条款与政策。相关凭证与配置仅限接入 Codex CLI 使用,不适用于其他客户端、平台或渠道。": "免責聲明:僅限個人使用,請勿分發或共享任何憑證。該管道存在前置條件與使用門檻,請在充分了解流程與風險後使用,並遵守 OpenAI 的相關條款與政策。相關憑證與設定僅限接入 Codex CLI 使用,不適用於其他客戶端、平臺或管道。", "兑换人ID": "兌換人ID", "兑换成功!": "兌換成功!", "兑换码充值": "兌換碼儲值", "兑换码创建成功": "兌換碼建立成功", - "兑换码创建成功!": "兌換碼建立成功!", "兑换码创建成功,是否下载兑换码?": "兌換碼建立成功,是否下載兌換碼?", + "兑换码创建成功!": "兌換碼建立成功!", "兑换码将以文本文件的形式下载,文件名为兑换码的名称。": "兌換碼將以文本檔案的形式下載,檔案名為兌換碼的名稱。", "兑换码更新成功!": "兌換碼更新成功!", "兑换码生成管理": "兌換碼生成管理", @@ -551,15 +668,18 @@ "全局控制侧边栏区域和功能显示,管理员隐藏的功能用户无法启用": "全域控制側邊欄區域和功能顯示,管理員隱藏的功能使用者無法啟用", "全局设置": "全域設定", "全选": "全選", + "全选当前列表模型": "", "全部": "全部", "全部供应商": "全部供應商", "全部分组": "全部分組", "全部地区总可用资源": "全部地區總可用資源", + "全部填入": "", "全部容器": "全部容器", "全部展开": "全部展開", "全部收起": "全部收起", "全部标签": "全部標籤", "全部模型": "全部模型", + "全部满足(AND)": "", "全部状态": "全部狀態", "全部硬件总可用资源": "全部硬體總可用資源", "全部端点": "全部端點", @@ -584,8 +704,8 @@ "关闭": "關閉", "关闭侧边栏": "關閉側邊欄", "关闭公告": "關閉公告", - "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "關閉後將不再顯示此提示(僅對當前瀏覽器生效)。確定要關閉嗎?", "关闭后,此模型将不会被“同步官方”自动覆盖或创建": "關閉後,此模型將不會被「同步官方」自動覆蓋或建立", + "关闭后将不再显示此提示(仅对当前浏览器生效)。确定要关闭吗?": "關閉後將不再顯示此提示(僅對當前瀏覽器生效)。確定要關閉嗎?", "关闭弹窗,已停止批量测试": "關閉彈窗,已停止批量測試", "关闭提示": "關閉提示", "其他": "其他", @@ -596,14 +716,33 @@ "内存 阈值 (%)": "記憶體 閾值 (%)", "内存使用率超过此值时拒绝请求": "記憶體使用率超過此值時拒絕請求", "内存命中": "記憶體命中", + "内存缓存最大条目数。0 表示使用后端默认容量:100000。": "", "内容": "內容", "内容较大,已启用性能优化模式": "內容較大,已啟用性能優化模式", "内容较大,部分功能可能受限": "內容較大,部分功能可能受限", + "内置": "", "内置 Ollama 镜像": "內置 Ollama 鏡像", "再次输入部署名称": "再次輸入部署名稱", + "最低": "最低", + "最低充值数量": "", + "最低充值美元数量": "最低儲值美元數量", + "最后使用时间": "最後使用時間", + "最后更新": "最後更新", + "最后请求": "最後請求", + "最大GPU数量": "最大GPU數量", + "最大可用": "最大可用", + "最大条目数": "", + "最终抵扣": "", + "最近一次": "", + "最近事件": "最近事件", + "最高优先级": "最高優先級", "写": "寫", + "准入策略": "", + "准入策略 JSON(可选)": "", "准备中...": "準備中...", "准备完成初始化": "準備完成初始化", + "减少": "減少", + "凭证已刷新": "", "分类名称": "分類名稱", "分组": "分組", "分组JSON设置": "分組 JSON 設定", @@ -655,40 +794,50 @@ "创建用户": "建立使用者", "初始化失败,请重试": "初始化失敗,請重試", "初始化系统": "初始化系統", + "删请求头": "", "删除": "刪除", + "删除 Key 来源": "", "删除会彻底移除该订阅记录(含权益明细)。是否继续?": "刪除會徹底移除該訂閱記錄(含權益明細)。是否繼續?", "删除后无法恢复,确定要删除模型 \"{{name}}\" 吗?": "刪除後無法恢復,確定要刪除模型 \"{{name}}\" 嗎?", "删除失败": "刪除失敗", "删除密钥失败": "刪除密鑰失敗", + "删除已选 {{selected}} / {{total}}": "", "删除成功": "刪除成功", "删除所选": "刪除所選", "删除所选令牌": "刪除所選令牌", "删除所选通道": "刪除所選通道", + "删除条件": "", + "删除模型": "", "删除禁用密钥失败": "刪除禁用密鑰失敗", "删除禁用通道": "刪除禁用通道", "删除自动禁用密钥": "刪除自動禁用密鑰", + "删除规则": "", "删除账户": "刪除帳號", "删除账户确认": "刪除帳號確認", "删除部署失败": "刪除部署失敗", "刷新": "刷新", + "刷新凭证": "", "刷新失败": "刷新失敗", "刷新容器信息": "刷新容器資訊", "刷新日志": "刷新日誌", "刷新统计": "刷新統計", + "刷新缓存统计": "", + "刷新缓存统计失败": "", "刷新页面": "重新整理頁面", + "前:": "前:", "前往 io.net API Keys": "前往 io.net API Keys", "前往设置": "前往設定", "前往设置页面": "前往設定頁面", "前缀": "前綴", - "前:": "前:", + "前置": "", + "剪贴板中未检测到连接信息": "剪貼簿中未偵測到連線資訊", + "副本数量": "副本數量", "剩余": "剩餘", "剩余备用码:": "剩餘備用碼:", "剩余时间": "剩餘時間", "剩余额度": "剩餘額度", - "剩余额度$": "剩餘額度$", "剩余额度/总额度": "剩餘額度/總額度", - "剪贴板中未检测到连接信息": "剪貼簿中未偵測到連線資訊", - "副本数量": "副本數量", + "剩余额度$": "剩餘額度$", "功能特性": "功能特性", "加入渠道": "加入管道", "加入预填组": "加入預填組", @@ -713,10 +862,14 @@ "包含": "包含", "包含来自未知或未标明供应商的AI模型,这些模型可能来自小型供应商或开源项目。": "包含來自未知或未標明供應商的AI模型,這些模型可能來自小型供應商或開源項目。", "包括失败请求的次数,0代表不限制": "包括失敗請求的次數,0代表不限制", + "匹配值": "", + "匹配值(可选)": "", + "匹配方式": "", "匹配类型": "匹配類型", "区域": "區域", "升级分组": "升級分組", "单GPU小时费率": "單GPU小時費率", + "单价 (USD)": "", "历史消耗": "歷史消耗", "原价": "原價", "原价,和普通用户一样": "原價,和普通使用者一樣", @@ -724,22 +877,38 @@ "原密码": "原密碼", "原生格式": "原生格式", "原生额度": "原生額度", - "使用原生额度输入": "使用原生額度輸入", - "收起原生额度输入": "收起原生額度輸入", + "去前缀": "", + "去后缀": "", + "去处理{{type}}": "", + "去空格": "", "去重完成:去重前 {{before}} 个密钥,去重后 {{after}} 个密钥": "去重完成:去重前 {{before}} 個密鑰,去重後 {{after}} 個密鑰", "参与官方同步": "參與官方同步", "参数": "參數", "参数传递": "參數傳遞", "参数值": "參數值", "参数覆盖": "參數覆蓋", + "参数覆盖 JSON 已复制": "", + "参数覆盖已复制": "", + "参数覆盖必须是合法的 JSON 对象": "", + "参数覆盖必须是合法的 JSON 格式!": "", + "参数覆盖模板": "", + "参数覆盖模板 JSON 格式不正确": "", + "参数覆盖模板预览": "", + "参数覆盖详情": "", + "参数配置": "", + "参数配置有误": "", + "参数错误": "", "参照生视频": "參照生影片", "友情链接": "友情連結", "发布日期": "發佈日期", "发布时间": "發佈時間", + "发现文档地址(Discovery URL,可选)": "", + "发行者 URL(Issuer URL)": "", "取消": "取消", "取消全选": "取消全選", "取消选择": "取消選擇", "变换": "變換", + "变更": "變更", "变焦": "變焦", "变量值": "變數值", "变量名": "變數名", @@ -750,21 +919,31 @@ "可以根据用户分组增减令牌可选的分组范围。例如 vip 用户额外开放 premium 分组,或移除某个分组的选择权。": "可以根據使用者分組增減令牌可選的分組範圍。例如 vip 使用者額外開放 premium 分組,或移除某個分組的選擇權。", "可以根据用户分组设置不同的计费倍率。例如 vip 用户使用 standard 令牌时倍率从 1.0 降为 0.8。": "可以根據使用者分組設定不同的計費倍率。例如 vip 使用者使用 standard 令牌時倍率從 1.0 降為 0.8。", "可信": "可信", + "可勾选需要执行的变更:新增会加入渠道模型列表,删除会从渠道模型列表移除。": "", "可在设置页面设置关于内容,支持 HTML & Markdown": "可在設定頁面設定關於內容,支援 HTML & Markdown", + "可手动填写,多个 scope 用空格分隔": "", "可用": "可用", "可用令牌分组": "可用令牌分組", "可用分组": "可用分組", + "可用变量:{{provider}} {{field}} {{op}} {{required}} {{current}} 以及 {{current.path}}": "", "可用数量": "可用數量", "可用模型": "可用模型", "可用空间: {{free}} / 总空间: {{total}}": "可用空間: {{free}} / 總空間: {{total}}", "可用端点类型": "可用端點類型", "可用邀请额度": "可用邀請額度", + "可留空;留空时会尝试使用 Issuer URL + /.well-known/openid-configuration": "", "可视化": "視覺化", "可视化倍率设置": "視覺化倍率設定", "可视化编辑": "視覺化編輯", - "可选值": "可選值", + "可空": "", "可选,公告的补充说明": "可選,公告的補充說明", "可选,用于复现结果": "可選,用於復現結果", + "可选:基于用户信息 JSON 做组合条件准入,条件不满足时返回自定义提示": "", + "可选:用于自动生成端点或 Discovery URL": "", + "可选。匹配入口请求的 User-Agent;任意一行作为子串匹配(忽略大小写)即命中。": "", + "可选。对提取到的亲和 Key 做正则校验;不填表示不校验。": "", + "可选。对请求路径进行匹配;不填表示匹配所有路径。": "", + "可选值": "可選值", "合计:{{total}}": "合計:{{total}}", "合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "合計:文字部分 {{textTotal}} + 音訊部分 {{audioTotal}} = {{total}}", "同时重置消息": "同時重置消息", @@ -772,18 +951,21 @@ "同步到渠道": "同步到管道", "同步向导": "同步嚮導", "同步失败": "同步失敗", + "同步字段": "", "同步成功": "同步成功", "同步接口": "同步接口", "同步渠道失败": "同步管道失敗", "同步渠道失败:缺少部署信息": "同步管道失敗:缺少部署資訊", + "同步端点": "", "名称": "名稱", "名称+密钥": "名稱+密鑰", "名称不能为空": "名稱不能為空", "名称匹配类型": "名稱匹配類型", + "后:": "後:", + "后端固定": "", "后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "後端固定倍率:{{ratio}}。此欄位僅展示換算後的價格。", "后端请求失败": "後端請求失敗", "后缀": "後綴", - "后:": "後:", "向右展开": "向右展開", "向左展开": "向左展開", "否": "否", @@ -791,27 +973,31 @@ "启动参数 (Args)": "啟動參數 (Args)", "启动命令": "啟動命令", "启动命令 (Entrypoint)": "啟動命令 (Entrypoint)", + "启动授权失败": "", "启动时间": "啟動時間", "启动部署失败": "啟動部署失敗", "启动配置": "啟動設定", "启用": "啟用", - "启用 Prompt 检查": "啟用 Prompt 檢查", "启用 io.net 部署": "啟用 io.net 部署", "启用 io.net 部署开关": "啟用 io.net 部署開關", "启用 io.net 部署时必须填写 API Key": "啟用 io.net 部署時必須填寫 API Key", + "启用 Prompt 检查": "啟用 Prompt 檢查", + "启用 Waffo": "", "启用2FA失败": "啟用2FA失敗", "启用Claude思考适配(-thinking后缀)": "啟用Claude思考相容(-thinking後綴)", "启用FunctionCall思维签名填充": "啟用FunctionCall思維簽名填充", "启用Gemini思考后缀适配": "啟用Gemini思考後綴相容", "启用Ping间隔": "啟用Ping間隔", "启用SMTP SSL": "啟用SMTP SSL", - "强制使用 AUTH LOGIN": "強制使用 AUTH LOGIN", "启用SSRF防护(推荐开启以保护服务器安全)": "啟用SSRF防護(推薦開啟以保護伺服器安全)", + "启用供应商": "", "启用全部": "啟用全部", "启用后可接入 io.net GPU 资源": "啟用後可接入 io.net GPU 資源", "启用后可添加图片URL进行多模态对话": "啟用後可添加圖片URL進行多模態對話", "启用后套餐将在用户端展示。是否继续?": "啟用後訂閱將在使用者端展示。是否繼續?", + "启用后将优先复用上一次成功的渠道(粘滞选路)。": "", "启用后将使用 Creem Test Mode": "啟用後將使用 Creem Test Mode", + "启用后将使用 Waffo 沙盒环境": "", "启用密钥失败": "啟用密鑰失敗", "启用屏蔽词过滤功能": "啟用屏蔽詞過濾功能", "启用性能监控": "啟用性能監控", @@ -831,38 +1017,50 @@ "启用额度消费日志记录": "啟用額度消費日誌記錄", "启用验证": "啟用驗證", "周": "周", + "命中判定:usage 中存在 cached tokens(例如 cached_tokens/prompt_cache_hit_tokens)即视为命中。": "", + "命中率": "", + "命中该亲和规则后,会把此模板合并到渠道参数覆盖中(同名键由模板覆盖)。": "", "和": "和", "和Claude不同,默认情况下Gemini的思考模型会自动决定要不要思考,就算不开启适配模型也可以正常使用,如果您需要计费,推荐设置无后缀模型价格按思考价格设置。支持使用 gemini-2.5-pro-preview-06-05-thinking-128 格式来精确传递思考预算。": "和Claude不同,預設情況下Gemini的思考模型會自動決定要不要思考,就算不開啟相容模型也可以正常使用,如果您需要計費,推薦設定無後綴模型價格按思考價格設定。支援使用 gemini-2.5-pro-preview-06-05-thinking-128 格式來精確傳遞思考預算。", "响应": "響應", "响应时间": "響應時間", + "响应缺少凭据": "", + "响应缺少授权链接": "", "商品价格 ID": "商品價格 ID", + "商户 ID": "", "回答内容": "回答內容", "回调 URL 填": "回調 URL 填", "回调 URL 格式": "回調 URL 格式", "回调地址": "回調位址", + "回调支付方式": "回調支付方式", + "回调调用者IP": "回調調用者IP", + "回调通知地址": "", "固定价格": "固定價格", "固定价格(每次)": "固定價格(每次)", "固定价格值": "固定價格值", "图像生成": "圖像生成", "图标": "圖示", + "图标使用 react-icons(Simple Icons)或 URL/emoji,例如:github、gitlab、si:google": "", "图标使用@lobehub/icons库,如:OpenAI、Claude.Color,支持链式参数:OpenAI.Avatar.type={'platform'}、OpenRouter.Avatar.shape={'square'},查询所有可用图标请 ": "圖示使用@lobehub/icons庫,如:OpenAI、Claude.Color,支援鏈式參數:OpenAI.Avatar.type={'platform'}、OpenRouter.Avatar.shape={'square'},查詢所有可用圖示請 ", + "图标文件不能超过 100KB,请压缩后重新上传": "", "图混合": "圖混合", "图片倍率 {{imageRatio}}": "圖片倍率 {{imageRatio}}", "图片功能在自定义请求体模式下不可用": "圖片功能在自訂請求體模式下不可用", "图片地址": "圖片位址", "图片已添加": "圖片已添加", - "图片生成调用:{{symbol}}{{price}} / 1次": "圖片生成調用:{{symbol}}{{price}} / 1次", "图片生成:1 次 * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "圖片生成:1 次 * 單價 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", + "图片生成调用:{{symbol}}{{price}} / 1次": "圖片生成調用:{{symbol}}{{price}} / 1次", "图片输入 {{price}} / 1M tokens": "圖片輸入 {{price}} / 1M tokens", "图片输入: {{imageRatio}}": "圖片輸入: {{imageRatio}}", + "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "圖片輸入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 圖片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "图片输入价格": "圖片輸入價格", "图片输入价格 {{symbol}}{{price}} / 1M tokens": "圖片輸入價格 {{symbol}}{{price}} / 1M tokens", "图片输入价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (图片倍率: {{imageRatio}})": "圖片輸入價格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (圖片倍率: {{imageRatio}})", "图片输入价格:{{symbol}}{{price}} / 1M tokens": "圖片輸入價格:{{symbol}}{{price}} / 1M tokens", "图片输入价格:{{symbol}}{{total}} / 1M tokens": "圖片輸入價格:{{symbol}}{{total}} / 1M tokens", + "图片输入倍率": "", "图片输入倍率(仅部分模型支持该计费)": "圖片輸入倍率(僅部分模型支援該計費)", "图片输入相关的倍率设置,键为模型名称,值为倍率,仅部分模型支持该计费": "圖片輸入相關的倍率設定,鍵為模型名稱,值為倍率,僅部分模型支援該計費", - "图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "圖片輸入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 圖片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "图生文": "圖生文", "图生视频": "圖生影片", "在Gotify服务器创建应用后获得的令牌,用于发送通知": "在Gotify伺服器建立應用後獲得的令牌,用於發送通知", @@ -889,21 +1087,37 @@ "基本信息": "基本資訊", "基础价格": "基礎價格", "基础用户": "基礎使用者", + "填充 Codex CLI / Claude CLI 模版": "", + "填充新模板": "", + "填充旧模板": "", + "填充模板": "", + "填充模板:等级+激活": "", + "填充模板:等级提示": "", + "填充模板:组织或角色": "", + "填充模板:组织提示": "", "填充模板(全渠道)": "填充模板(全管道)", "填充模板(指定渠道)": "填充模板(指定管道)", "填入": "填入", + "填入 CC Switch": "", "填入所有模型": "填入所有模型", + "填入来源": "", "填入模板": "填入模板", + "填入目标": "", "填入相关模型": "填入相關模型", + "填入路径": "", "填入透传完整模版": "填入透傳完整模版", "填入透传模版": "填入透傳模版", + "填写 Issuer URL 后自动生成:": "", "填写Gotify服务器的完整URL地址": "填寫Gotify伺服器的完整URL位址", + "填写后会自动拼接预设端点": "", "填写带https的域名,逗号分隔": "填寫帶https的域名,逗號分隔", "填写服务器地址后自动生成:": "填寫伺服器位址後自動生成:", "填写用户协议内容后,用户注册时将被要求勾选已阅读用户协议": "填寫使用者協議內容後,使用者註冊時將被要求勾選已閱讀使用者協議", "填写隐私政策内容后,用户注册时将被要求勾选已阅读隐私政策": "填寫隱私政策內容後,使用者註冊時將被要求勾選已閱讀隱私政策", "填写音频补全价格前,需要先填写音频输入价格。": "填寫音訊補全價格前,需要先填寫音訊輸入價格。", + "处理上游模型更新": "", "处理中": "處理中", + "处理全部渠道上游更新": "", "备份支持": "備份支援", "备份状态": "備份狀態", "备注": "備註", @@ -913,6 +1127,7 @@ "复制": "複製", "复制代码": "複製程式碼", "复制令牌": "複製令牌", + "复制令牌失败": "", "复制全部": "複製全部", "复制名称": "複製名稱", "复制失败": "複製失敗", @@ -926,10 +1141,12 @@ "复制所有模型": "複製所有模型", "复制所选令牌": "複製所選令牌", "复制所选兑换码到剪贴板": "複製所選兌換碼到剪貼板", + "复制授权链接": "", "复制日志": "複製日誌", "复制渠道的所有信息": "複製管道的所有資訊", "复制版本号": "複製版本號", "复制生成的密钥并粘贴到此处": "複製生成的密鑰並貼上到此處", + "复制请求头": "", "复制连接信息": "複製連線資訊", "复制链接": "複製連結", "外接设备": "外接設備", @@ -942,6 +1159,7 @@ "天前": "天前", "失败": "失敗", "失败原因": "失敗原因", + "失败后不重试": "", "失败后是否重试": "失敗後是否重試", "失败时自动禁用通道": "失敗時自動禁用通道", "失败重试次数": "失敗重試次數", @@ -950,16 +1168,22 @@ "套餐副标题": "訂閱副標題", "套餐名称": "訂閱名稱", "套餐标题": "訂閱標題", + "套餐标题不能为空": "", "套餐的基本信息和定价": "訂閱的基本資訊和定價", + "如:大带宽批量分析图片推荐": "如:大頻寬批量分析圖片推薦", + "如:香港线路": "如:香港線路", + "如果亲和到的渠道失败,重试到其他渠道成功后,将亲和更新到成功的渠道。": "", "如果你对接的是上游One API或者New API等转发项目,请使用OpenAI类型,不要使用此类型,除非你知道你在做什么。": "如果你對接的是上游One API或者New API等轉發項目,請使用OpenAI類型,不要使用此類型,除非你知道你在做什麼。", "如果用户请求中包含系统提示词,则使用此设置拼接到用户的系统提示词前面": "如果使用者請求中包含系統提示詞,則使用此設定拼接到使用者的系統提示詞前面", "如果镜像为私有,请填写密码或Token": "如果鏡像為私有,請填寫密碼或Token", "如果镜像为私有,请填写用户名": "如果鏡像為私有,請填寫使用者名", - "如:大带宽批量分析图片推荐": "如:大頻寬批量分析圖片推薦", - "如:香港线路": "如:香港線路", "始终使用浅色主题": "始終使用淺色主題", "始终使用深色主题": "始終使用深色主題", + "字段映射": "", + "字段缺失视为命中": "", + "字段路径": "", "字段透传控制": "字段透傳控制", + "字段速查": "", "存在惩罚,鼓励讨论新话题": "存在懲罰,鼓勵討論新話題", "存在重复的分组名称:": "存在重複的分組名稱:", "存在重复的键名:": "存在重複的鍵名:", @@ -1018,12 +1242,17 @@ "密码重置确认": "密碼重置確認", "密码长度至少为8个字符": "密碼長度至少為8個字符", "密钥": "密鑰", + "密钥 JSON 必须包含 access_token": "", + "密钥 JSON 必须包含 account_id": "", + "密钥(编辑模式下,保存的密钥不会显示)": "密鑰(編輯模式下,儲存的密鑰不會顯示)", "密钥去重": "密鑰去重", "密钥将以Bearer方式添加到请求头中,用于验证webhook请求的合法性": "密鑰將以Bearer方式添加到請求頭中,用於驗證webhook請求的合法性", "密钥已删除": "密鑰已刪除", "密钥已启用": "密鑰已啟用", "密钥已复制到剪贴板": "密鑰已複製到剪貼板", "密钥已禁用": "密鑰已禁用", + "密钥必须是 JSON 对象": "", + "密钥必须是合法的 JSON 格式!": "", "密钥文件 (.json)": "密鑰檔案 (.json)", "密钥更新模式": "密鑰更新模式", "密钥格式": "密鑰格式", @@ -1033,12 +1262,12 @@ "密钥获取成功": "密鑰獲取成功", "密钥输入方式": "密鑰輸入方式", "密钥预览": "密鑰預覽", - "密钥(编辑模式下,保存的密钥不会显示)": "密鑰(編輯模式下,儲存的密鑰不會顯示)", "对于官方渠道,new-api已经内置地址,除非是第三方代理站点或者Azure的特殊接入地址,否则不需要填写": "對於官方管道,new-api已經內置位址,除非是第三方代理站點或者Azure的特殊接入位址,否則不需要填寫", "对免费模型启用预消耗": "對免費模型啟用預消耗", "对域名启用 IP 过滤(推荐开启)": "對域名啟用 IP 過濾(建議開啟)", "对外运营模式": "對外運營模式", "对比:不勾选「用户可选」的场景": "對比:不勾選「使用者可選」的場景", + "对象清理规则": "", "导入": "導入", "导入的配置将覆盖当前设置,是否继续?": "導入的設定將覆蓋當前設定,是否繼續?", "导入配置": "導入設定", @@ -1053,11 +1282,14 @@ "将删除": "將刪除", "将删除 {{value}} 天前的日志文件。": "將刪除 {{value}} 天前的日誌檔案。", "将删除已使用、已禁用及过期的兑换码,此操作不可撤销。": "將刪除已使用、已禁用及過期的兌換碼,此操作不可撤銷。", + "将删除所有仍在内存中的渠道亲和性缓存条目。": "", "将只保留最近 {{value}} 个日志文件,其余将被删除。": "將只保留最近 {{value}} 個日誌檔案,其餘將被刪除。", "将大请求体临时存储到磁盘": "將大請求體臨時存儲到磁碟", "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "會把目前編輯中的模型 {{name}} 的價格配置,批量套用到已勾選的 {{count}} 個模型。", + "将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。_other": "", "将清除所有保存的配置并恢复默认设置,此操作不可撤销。是否继续?": "將清除所有儲存的設定並恢復預設設定,此操作不可撤銷。是否繼續?", "将清除选定时间之前的所有日志": "將清除選定時間之前的所有日誌", + "将追加 2 条规则到现有规则列表。": "", "小时": "小時", "小时费率": "小時費率", "尚未使用": "尚未使用", @@ -1069,8 +1301,10 @@ "展示价格": "展示價格", "嵌套映射:用户分组 → 使用分组 → 倍率": "巢狀對映:使用者分組 → 使用分組 → 倍率", "左侧边栏个人设置": "左側邊欄個人設定", - "已为 ${count} 个渠道设置标签!": "已為 ${count} 個管道設定標籤!", "已为 {{count}} 个模型设置{{type}}_other": "已為 {{count}} 個模型設定{{type}}", + "已为 ${count} 个渠道设置标签!": "已為 ${count} 個管道設定標籤!", + "已从 Discovery 自动填充配置": "", + "已从 Discovery 获取配置,可继续手动修改所有字段。": "", "已作废": "已作廢", "已保存偏好为": "已儲存偏好為", "已修复 ${success} 个通道,失败 ${fails} 个通道。": "已修復 ${success} 個通道,失敗 ${fails} 個通道。", @@ -1082,14 +1316,16 @@ "已切换为System角色": "已切換為System角色", "已切换至最优倍率视图,每个模型使用其最低倍率分组": "已切換至最優倍率視圖,每個模型使用其最低倍率分組", "已初始化": "已初始化", - "已删除 ${data} 个通道!": "已刪除 ${data} 個通道!", + "已删除": "", "已删除 {{count}} 个令牌!": "已刪除 {{count}} 個令牌!", "已删除 {{count}} 个令牌!_other": "已刪除 {{count}} 個令牌!", "已删除 {{count}} 条失效兑换码_other": "已刪除 {{count}} 條失效兌換碼", + "已删除 ${data} 个通道!": "已刪除 ${data} 個通道!", "已删除所有禁用渠道,共计 ${data} 个": "已刪除所有禁用管道,共計 ${data} 個", "已删除消息及其回复": "已刪除消息及其回覆", "已勾选": "已勾選", "已勾选 {{count}} 个模型": "已勾選 {{count}} 個模型", + "已勾选 {{count}} 个模型_other": "", "已发起支付": "已發起支付", "已发送到 Fluent": "已發送到 Fluent", "已取消 Passkey 注册": "已取消 Passkey 註冊", @@ -1098,24 +1334,33 @@ "已启用 Passkey,无需密码即可登录": "已啟用 Passkey,無需密碼即可登錄", "已启用所有密钥": "已啟用所有密鑰", "已在自定义模式中忽略": "已在自訂模式中忽略", + "已填充提示模板": "", + "已填充模版": "", + "已填充策略模板": "", + "已处理上游模型更新:加入 {{added}} 个,删除 {{removed}} 个,本次忽略 {{ignored}} 个,当前已忽略模型 {{totalIgnored}} 个": "", "已备份": "已備份", "已复制": "已複製", "已复制 ${count} 个模型": "已複製 ${count} 個模型", "已复制 ID 到剪贴板": "已複製 ID 到剪貼板", + "已复制:": "已複製:", + "已复制:{{name}}": "已複製:{{name}}", "已复制全部数据": "已複製全部數據", "已复制到剪切板": "已複製到剪切板", "已复制到剪贴板": "已複製到剪貼板", "已复制到剪贴板!": "已複製到剪貼板!", + "已复制字段:{{name}}": "", "已复制模型名称": "已複製模型名稱", "已复制版本号": "已複製版本號", "已复制自动生成的 API Key": "已複製自動生成的 API Key", - "已复制:": "已複製:", - "已复制:{{name}}": "已複製:{{name}}", "已完成": "已完成", "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "已將模型 {{name}} 的價格配置批量套用到 {{count}} 個模型", + "已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型_other": "", "已开启全局请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "已開啟全域請求透傳:參數覆寫、模型重定向、管道相容等 NewAPI 內置功能將失效,非最佳實踐;如因此產生問題,請勿提交 issue 回饋。", + "已忽略模型": "", "已成功开始测试所有已启用通道,请刷新页面查看结果。": "已成功開始測試所有已啟用通道,請刷新頁面查看結果。", + "已打开授权页面": "", "已打开支付页面": "已打開支付頁面", + "已批量处理上游模型更新:渠道 {{channels}} 个,加入 {{added}} 个,删除 {{removed}} 个,失败 {{fails}} 个": "", "已提交": "已提交", "已支付金额": "已支付金額", "已新增 {{count}} 个模型:{{list}}_other": "已新增 {{count}} 個模型:{{list}}", @@ -1127,9 +1372,13 @@ "已服务": "已服務", "已注销": "已註銷", "已添加": "已添加", + "已添加 {{count}} 个模板_other": "", "已添加到白名单": "已添加到白名單", "已清理 {{count}} 个日志文件,释放 {{size}}": "已清理 {{count}} 個日誌檔案,釋放 {{size}}", + "已清理 {{count}} 个日志文件,释放 {{size}}_other": "", + "已清空": "", "已清空测试结果": "已清空測試結果", + "已生成授权凭据": "", "已用": "已用", "已用/剩余": "已用/剩餘", "已用额度": "已用額度", @@ -1140,23 +1389,28 @@ "已结束": "已結束", "已耗尽": "已耗盡", "已解锁豆包自定义 API 地址编辑": "已解鎖豆包自訂 API 位址編輯", + "已设置": "", "已达上限": "已達上限", "已达到购买上限": "已達到購買上限", "已过期": "已過期", "已运行时间": "已運行時間", - "已选择 ${count} 个渠道": "已選擇 ${count} 個管道", "已选择 {{count}} 个模型_other": "已選擇 {{count}} 個模型", "已选择 {{selected}} / {{total}}": "已選擇 {{selected}} / {{total}}", + "已选择 ${count} 个渠道": "已選擇 ${count} 個管道", "已重置为默认配置": "已重置為預設設定", "已销毁": "已銷燬", "币种": "幣種", + "帐号信息": "", + "常用上下文 Key(用于 context_*)": "", "常见问答": "常見問答", "常见问答管理,为用户提供常见问题的答案(最多50个,前端显示最新20条)": "常見問答管理,為使用者提供常見問題的答案(最多50個,前端顯示最新20條)", "平台": "平臺", "平均RPM": "平均RPM", "平均TPM": "平均TPM", "平移": "平移", + "年": "", "应付金额": "應付金額", + "应用": "", "应用同步": "應用同步", "应用更改": "應用更改", "应用覆盖": "應用覆蓋", @@ -1165,33 +1419,42 @@ "延长容器时长将会产生额外费用,请确认您有足够的账户余额。": "延長容器時長將會產生額外費用,請確認您有足夠的帳號餘額。", "延长操作一旦确认无法撤销,费用将立即扣除。": "延長操作一旦確認無法撤銷,費用將立即扣除。", "延长时长": "延長時長", + "延长时长(小时)": "延長時長(小時)", "延长时长不能超过720小时(30天)": "延長時長不能超過720小時(30天)", "延长时长失败": "延長時長失敗", "延长时长至少为1小时": "延長時長至少為1小時", - "延长时长(小时)": "延長時長(小時)", "建立连接时发生错误": "建立連接時發生錯誤", "建议在生产环境中使用 MySQL 或 PostgreSQL 数据库,或确保 SQLite 数据库文件已映射到宿主机的持久化存储。": "建議在生產環境中使用 MySQL 或 PostgreSQL 資料庫,或確保 SQLite 資料庫檔案已映射到宿主機的持久化存儲。", "开": "開", "开启「默认使用 auto 分组」后,新建令牌和初始令牌都会自动设为 auto。": "開啟「預設使用 auto 分組」後,新建令牌和初始令牌都會自動設為 auto。", "开启之后会清除用户提示词中的": "開啟之後會清除使用者提示詞中的", "开启之后将上游地址替换为服务器地址": "開啟之後將上游位址替換為伺服器位址", - "开启后不限制:必须设置模型倍率": "開啟後不限制:必須設定模型倍率", - "开启后创建令牌默认选择auto分组,初始令牌也将设为auto": "開啟後建立令牌預設選擇 auto 分組,初始令牌也將設為 auto", - "开启后未登录用户无法访问模型广场": "開啟後未登錄使用者無法訪問模型廣場", + "开启后,using_group 会参与 cache key(不同分组隔离)。": "", "开启后,仅\"消费\"和\"错误\"日志将记录您的客户端IP地址": "開啟後,僅\"消費\"和\"錯誤\"日誌將記錄您的客戶端IP位址", "开启后,对免费模型(倍率为0,或者价格为0)的模型也会预消耗额度": "開啟後,對免費模型(倍率為0,或者價格為0)的模型也會預消耗額度", "开启后,将定期发送ping数据保持连接活跃": "開啟後,將定期發送ping數據保持連接活躍", "开启后,当前分组渠道失败时会按顺序尝试下一个分组的渠道": "開啟後,當前分組管道失敗時會按順序嘗試下一個分組的管道", "开启后,所有请求将直接透传给上游,不会进行任何处理(重定向和渠道适配也将失效),请谨慎开启": "開啟後,所有請求將直接透傳給上游,不會進行任何處理(重定向和管道相容也將失效),請謹慎開啟", + "开启后,模型名称会参与 cache key(不同模型隔离)。": "", + "开启后,若该规则命中且请求失败,将不会切换渠道重试。": "", + "开启后,规则名称会参与 cache key(不同规则隔离)。": "", + "开启后,该渠道请求 Claude 时将强制追加 ?beta=true(无需客户端手动传参)": "", "开启后,违规请求将额外扣费。": "開啟後,違規請求將額外扣費。", + "开启后不限制:必须设置模型倍率": "開啟後不限制:必須設定模型倍率", + "开启后创建令牌默认选择auto分组,初始令牌也将设为auto": "開啟後建立令牌預設選擇 auto 分組,初始令牌也將設為 auto", + "开启后未登录用户无法访问模型广场": "開啟後未登錄使用者無法訪問模型廣場", + "开启后检测到新增模型会自动加入当前渠道模型列表": "", + "开启后由后端定时任务检测该渠道上游模型变化": "", "开启批量操作": "開啟批量操作", "开始": "開始", "开始同步": "開始同步", "开始批量测试 ${count} 个模型,已清空上次结果...": "開始批量測試 ${count} 個模型,已清空上次結果...", "开始时间": "開始時間", + "异常": "", "异步任务退款": "非同步任務退款", "张图片": "張圖片", "弱变换": "弱變換", + "强制使用 AUTH LOGIN": "強制使用 AUTH LOGIN", "强制将响应格式化为 OpenAI 标准格式(只适用于OpenAI渠道类型)": "強制將響應格式化為 OpenAI 標準格式(只適用於OpenAI管道類型)", "强制格式化": "強制格式化", "强制要求": "強制要求", @@ -1200,11 +1463,16 @@ "当令牌分组设为 auto 时,系统按列表顺序依次选择可用分组。排在前面的优先级更高。": "當令牌分組設為 auto 時,系統按列表順序依次選擇可用分組。排在前面的優先級更高。", "当前 API 密钥已过期,请在设置中更新。": "當前 API 密鑰已過期,請在設定中更新。", "当前 Ollama 版本为 ${version}": "當前 Ollama 版本為 ${version}", + "当前仅 OpenAI / Claude 语义支持缓存 token 统计,其他通道将隐藏 token 相关字段。": "", "当前余额": "當前餘額", "当前值": "當前值", + "当前值不是合法 JSON,无法格式化": "", "当前分组为 auto,会自动选择最优分组,当一个组不可用时自动降级到下一个组(熔断机制)": "當前分組為 auto,會自動選擇最優分組,當一個組不可用時自動降級到下一個組(熔斷機制)", "当前剩余": "當前剩餘", + "当前参数覆盖不是合法的 JSON": "", "当前提示": "目前提示", + "当前旧格式 JSON 不合法,无法追加模板": "", + "当前旧格式不是 JSON 对象,无法追加模板": "", "当前时间": "當前時間", "当前未启用,需要时再打开即可。": "目前未啟用,需要時再開啟即可。", "当前未开启Midjourney回调,部分项目可能无法获得绘图结果,可在运营设置中开启。": "當前未開啟Midjourney回調,部分項目可能無法獲得繪圖結果,可在運營設定中開啟。", @@ -1217,11 +1485,14 @@ "当前状态": "當前狀態", "当前缓存大小": "當前快取大小", "当前编辑": "目前編輯", + "当前规则不支持写入到该位置": "", + "当前规则未设置参数覆盖模板": "", "当前计费": "當前計費", "当前设备不支持 Passkey": "當前設備不支援 Passkey", "当前设置类型: ": "當前設定類型: ", "当前跟随系统": "當前跟隨系統", "当前配置无法连接到 io.net。": "當前設定無法連接到 io.net。", + "当前额度": "當前額度", "当某个分组的用户使用另一个分组的令牌时,可设置特殊倍率覆盖基础倍率。例如:vip 分组的用户使用 default 分组时倍率为 0.5": "當某個分組的使用者使用另一個分組的令牌時,可設定特殊倍率覆蓋基礎倍率。例如:vip 分組的使用者使用 default 分組時倍率為 0.5", "当模型没有设置价格时仍接受调用,仅当您信任该网站时使用,可能会产生高额费用": "當模型沒有設定價格時仍接受調用,僅當您信任該網站時使用,可能會產生高額費用", "当运行通道全部测试时,超过此时间将自动禁用通道": "當運行通道全部測試時,超過此時間將自動禁用通道", @@ -1234,6 +1505,8 @@ "微信扫码登录": "微信掃碼登錄", "微信账户绑定成功!": "微信帳號綁定成功!", "必填:请输入服务器地址以自动生成完整端点 URL": "必填:請輸入伺服器位址以自動生成完整端點 URL", + "必填。对请求的 model 名称进行匹配,任意一条匹配即命中该规则。": "", + "必须全部满足(AND)": "", "必须是有效的 JSON 字符串数组,例如:[\"g1\",\"g2\"]": "必須是有效的 JSON 字符串陣列,例如:[\"g1\",\"g2\"]", "忘记密码?": "忘記密碼?", "快速开始": "快速開始", @@ -1246,6 +1519,7 @@ "思考预算占比": "思考預算佔比", "性能指标": "性能指標", "性能监控": "性能監控", + "性能设置": "", "总 GPU 小时": "總 GPU 小時", "总价:文字价格 {{textPrice}} + 音频价格 {{audioPrice}} = {{symbol}}{{total}}": "總價:文字價格 {{textPrice}} + 音訊價格 {{audioPrice}} = {{symbol}}{{total}}", "总分配内存": "總分配記憶體", @@ -1267,6 +1541,7 @@ "想起来了?": "想起來了?", "成功": "成功", "成功兑换额度:": "成功兌換額度:", + "成功后切换亲和": "", "成功时自动启用通道": "成功時自動啟用通道", "我已了解禁用两步验证将永久删除所有相关设置和备用码,此操作不可撤销": "我已瞭解禁用兩步驗證將永久刪除所有相關設定和備用碼,此操作不可撤銷", "我已阅读并同意": "我已閱讀並同意", @@ -1279,10 +1554,13 @@ "所有密钥已复制到剪贴板": "所有密鑰已複製到剪貼板", "所有用户": "所有使用者", "所有编辑均为覆盖操作,留空则不更改": "所有編輯均為覆蓋操作,留空則不更改", + "所选模板已存在": "", "手动禁用": "手動禁用", "手动编辑": "手動編輯", "手动输入": "手動輸入", + "打开 CC Switch": "", "打开侧边栏": "打開側邊欄", + "打开授权页面": "", "扣费": "扣費", "执行 GC": "執行 GC", "执行中": "執行中", @@ -1296,10 +1574,13 @@ "批量删除失败": "批量刪除失敗", "批量删除成功": "批量刪除成功", "批量删除模型": "批量刪除模型", + "批量处理失败": "", "批量应用当前模型价格": "批量套用目前模型價格", "批量操作": "批量操作", "批量操作失败": "批量操作失敗", "批量操作完成: {{success}}个成功, {{failed}}个失败": "批量操作完成: {{success}}個成功, {{failed}}個失敗", + "批量检测失败": "", + "批量检测完成:渠道 {{channels}} 个,新增 {{add}} 个,删除 {{remove}} 个,失败 {{fails}} 个": "", "批量测试${count}个模型": "批量測試${count}個模型", "批量测试完成!成功: ${success}, 失败: ${fail}, 总计: ${total}": "批量測試完成!成功: ${success}, 失敗: ${fail}, 總計: ${total}", "批量测试已停止": "批量測試已停止", @@ -1313,36 +1594,39 @@ "拉取新模型": "拉取新模型", "拉取模型": "拉取模型", "拉取进度": "拉取進度", + "拒绝提示模板(可选)": "", + "拦截原因": "", "按K显示单位": "按K顯示單位", "按价格设置": "按價格設定", "按倍率类型筛选": "按倍率類型篩選", "按倍率设置": "按倍率設定", "按次": "按次", "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "按次 {{symbol}}{{price}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "按次计费": "按次計費", "按次:{{symbol}}{{price}}": "按次:{{symbol}}{{price}}", "按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "按次:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}", + "按次计费": "按次計費", "按照如下格式输入:AccessKey|SecretAccessKey|Region": "按照如下格式輸入:AccessKey|SecretAccessKey|Region", "按量计费": "按量計費", "按量计费下需要先填写输入价格,才能保存其它价格项。": "按量計費下需要先填寫輸入價格,才能儲存其它價格項。", "按顺序替换content中的变量占位符": "按順序替換content中的變數佔位符", "换脸": "換臉", + "授权,需在遵守": "授權,需在遵守", "授权失败": "授權失敗", "授权端点": "授權端點", "授权范围 (Scopes)": "授權範圍 (Scopes)", - "授权,需在遵守": "授權,需在遵守", "排序": "排序", "排队中": "排隊中", "接受未设置价格模型": "接受未設定價格模型", "接口凭证": "接口憑證", "接口密钥已过期": "接口密鑰已過期", + "接收上游模型更新通知": "", "控制台": "控制檯", "控制台区域": "控制檯區域", "控制输出的随机性和创造性": "控制輸出的隨機性和創造性", "控制顶栏模块显示状态,全局生效": "控制頂欄模組顯示狀態,全域生效", "推荐": "推薦", - "推荐使用(用户可选)": "推薦使用(使用者可選)", "推荐:用户可以选择是否使用指纹等验证": "推薦:使用者可以選擇是否使用指紋等驗證", + "推荐使用(用户可选)": "推薦使用(使用者可選)", "描述": "描述", "提交": "提交", "提交时间": "提交時間", @@ -1353,17 +1637,18 @@ "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}}": "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}}", "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 補全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 快取 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 快取建立 {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + 補全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", - "提示价格:{{symbol}}{{price}} / 1M tokens": "提示價格:{{symbol}}{{price}} / 1M tokens", - "提示缓存倍率": "提示快取倍率", "提示:如需备份数据,只需复制上述目录即可": "提示:如需備份數據,只需複製上述目錄即可", "提示:此处配置仅用于控制「模型广场」对用户的展示效果,不会影响模型的实际调用与路由。若需配置真实调用行为,请前往「渠道管理」进行设置。": "提示:此處設定僅用於控制「模型廣場」對使用者的展示效果,不會影響模型的實際調用與路由。若需設定真實調用行為,請前往「管道管理」進行設定。", "提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。": "提示:端點映射僅用於模型廣場展示,不會影響模型真實呼叫。如需配置真實呼叫,請前往「管道管理」。", "提示:该功能为测试版,未来配置结构与功能行为可能发生变更,请勿在生产环境使用。": "提示:該功能為測試版,未來設定結構與功能行為可能發生變更,請勿在生產環境使用。", "提示:语言偏好会同步到您登录的所有设备,并影响API返回的错误消息语言。": "提示:語言偏好會同步到您登錄的所有設備,並影響API返回的錯誤消息語言。", "提示:链接中的{key}将被替换为API密钥,{address}将被替换为服务器地址": "提示:連結中的{key}將被替換為API密鑰,{address}將被替換為伺服器位址", + "提示价格:{{symbol}}{{price}} / 1M tokens": "提示價格:{{symbol}}{{price}} / 1M tokens", + "提示缓存倍率": "提示快取倍率", "搜索供应商": "搜尋供應商", "搜索关键字": "搜尋關鍵字", "搜索失败": "搜尋失敗", + "搜索字段名 / 中文说明": "", "搜索无结果": "搜尋無結果", "搜索日志内容": "搜尋日誌內容", "搜索条件": "搜尋條件", @@ -1373,6 +1658,7 @@ "搜索模型失败": "搜尋模型失敗", "搜索渠道名称或地址": "搜尋管道名稱或位址", "搜索聊天应用名称": "搜尋聊天應用名稱", + "搜索规则(描述 / 类型 / 路径 / 来源 / 目标)": "", "搜索部署名称": "搜尋部署名稱", "操作": "操作", "操作失败": "操作失敗", @@ -1380,18 +1666,22 @@ "操作成功完成!": "操作成功完成!", "操作暂时被禁用": "操作暫時被禁用", "操作确认": "操作確認", + "操作类型": "", "操练场": "操練場", "操练场和聊天功能": "操練場和聊天功能", "支付": "支付", "支付地址": "支付位址", "支付失败": "支付失敗", + "支付完成后用户跳转的页面,留空则自动使用 服务器地址 + /console/topup": "", "支付宝": "支付寶", "支付方式": "支付方式", "支付方式名称": "付款方式名稱", + "支付方式名称不能为空": "", "支付方式类型": "付款方式類型", "支付渠道": "支付管道", "支付设置": "支付設定", "支付请求失败": "支付請求失敗", + "支付返回地址": "", "支付金额": "支付金額", "支持 Ctrl+V 粘贴图片": "支援 Ctrl+V 貼上圖片", "支持 JSONPath,如 email, data.user.email": "支援 JSONPath,如 email, data.user.email", @@ -1412,12 +1702,15 @@ "支持拉取 Ollama 官方模型库中的所有模型,拉取过程可能需要几分钟时间": "支援拉取 Ollama 官方模型庫中的所有模型,拉取過程可能需要幾分鐘時間", "支持搜索用户的 ID、用户名、显示名称和邮箱地址": "支援搜尋使用者的 ID、使用者名、顯示名稱和信箱位址", "支持的图像模型": "支援的圖像模型", + "支持精确匹配;使用 regex: 开头可按正则匹配。": "", "支持通配符格式,如:example.com, *.api.example.com": "支援通配符格式,如:example.com, *.api.example.com", + "支持逻辑 and/or 与嵌套 groups;操作符支持 eq/ne/gt/gte/lt/lte/in/not_in/contains/exists": "", "收益": "收益", "收益统计": "收益統計", "收起": "收起", "收起侧边栏": "收起側邊欄", "收起内容": "收起內容", + "收起原生额度输入": "收起原生額度輸入", "放大": "放大", "放大编辑": "放大編輯", "敏感信息不会发送到前端显示": "敏感資訊不會發送到前端顯示", @@ -1434,9 +1727,9 @@ "数据看板默认时间粒度": "數據看板預設時間粒度", "数据管理和日志查看": "數據管理和日誌查看", "文件上传": "檔案上傳", + "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "檔案搜尋:{{count}} / 1K * 單價 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "文件搜索价格:{{symbol}}{{price}} / 1K 次": "檔案搜尋價格:{{symbol}}{{price}} / 1K 次", "文件搜索调用 {{fileSearchCallCount}} 次": "檔案搜尋呼叫 {{fileSearchCallCount}} 次", - "文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "檔案搜尋:{{count}} / 1K * 單價 {{price}} * {{ratioType}} {{ratio}} = {{amount}}", "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 文字補全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字補全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音訊提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音訊補全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}", "文字提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}": "文字提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 快取 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 文字補全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} = {{symbol}}{{total}}", @@ -1448,7 +1741,15 @@ "文档地址": "文件位址", "文生视频": "文生影片", "新增": "新增", + "新增 Key 来源": "", "新增供应商": "新增供應商", + "新增失败": "", + "新增已选 {{selected}} / {{total}}": "", + "新增成功": "", + "新增支付方式": "", + "新增条件": "", + "新增模型": "", + "新增规则": "", "新增订阅": "新增訂閱", "新密码": "新密碼", "新密码需要和原密码不一致!": "新密碼需要和原密碼不一致!", @@ -1458,8 +1759,9 @@ "新建容器部署": "新建容器部署", "新建数量": "新建數量", "新建组": "新建組", - "新格式模板": "新格式模板", "新格式(支持条件判断与json自定义):": "新格式(支援條件判斷與json自訂):", + "新格式(规则 + 条件)": "", + "新格式模板": "新格式模板", "新版本": "新版本", "新用户使用邀请码奖励额度": "新使用者使用邀請碼獎勵額度", "新用户初始额度": "新使用者初始額度", @@ -1482,6 +1784,7 @@ "无邀请人": "無邀請人", "无限制": "無限制", "无限额度": "無限額度", + "日": "", "日志导出成功": "日誌導出成功", "日志已下载": "日誌已下載", "日志已加载": "日誌已載入", @@ -1495,29 +1798,35 @@ "日志类型": "日誌類型", "日志设置": "日誌設定", "日志详情": "日誌詳情", - "旧格式模板": "舊格式模板", + "旧格式(JSON 对象)": "", "旧格式(直接覆盖):": "舊格式(直接覆蓋):", + "旧格式必须是 JSON 对象": "", + "旧格式模板": "舊格式模板", "旧的备用码已失效,请保存新的备用码": "舊的備用碼已失效,請儲存新的備用碼", "早上好": "早安", "时间": "時間", "时间信息": "時間資訊", "时间粒度": "時間粒度", + "易支付": "", "易支付商户ID": "易支付商戶ID", "易支付商户密钥": "易支付商戶密鑰", "是": "是", "是否为企业账户": "是否為企業帳號", "是否同时重置对话消息?选择\"是\"将清空所有对话记录并恢复默认示例;选择\"否\"将保留当前对话记录。": "是否同時重置對話消息?選擇\"是\"將清空所有對話記錄並恢復預設示例;選擇\"否\"將保留當前對話記錄。", "是否将该订单标记为成功并为用户入账?": "是否將該訂單標記為成功併為使用者入賬?", + "是否检测上游模型更新": "", "是否确认充值?": "是否確認儲值?", + "是否自动同步上游模型更新": "", "是否自动禁用": "是否自動禁用", "是否要求指纹/面容等生物识别": "是否要求指紋/面容等生物識別", "显示倍率": "顯示倍率", + "显示最新20条": "顯示最新20條", "显示名称": "顯示名稱", "显示名称字段": "顯示名稱字段", + "显示名称字段(可选)": "", "显示完整内容": "顯示完整內容", "显示操作项": "顯示操作項", "显示更多": "顯示更多", - "显示最新20条": "顯示最新20條", "显示第": "顯示第", "显示设置": "顯示設定", "显示调试": "顯示除錯", @@ -1528,6 +1837,7 @@ "智能体ID": "智慧體ID", "智能熔断": "智慧熔斷", "智谱": "智譜", + "暂存错误": "", "暂无": "暫無", "暂无API信息": "暫無API資訊", "暂无SSE响应数据": "暫無SSE響應數據", @@ -1537,7 +1847,10 @@ "暂无公告": "暫無公告", "暂无分组,点击下方按钮添加": "暫無分組,點擊下方按鈕新增", "暂无匹配模型": "暫無匹配模型", + "暂无参数覆盖记录": "", + "暂无可复制 JSON": "", "暂无可复制的版本信息": "暫無可複製的版本資訊", + "暂无可展示数据": "", "暂无可用的支付方式,请联系管理员配置": "暫無可用的支付方式,請聯繫管理員設定", "暂无可购买套餐": "暫無可購買訂閱", "暂无响应数据": "暫無響應數據", @@ -1545,8 +1858,10 @@ "暂无容器详情": "暫無容器詳情", "暂无密钥数据": "暫無密鑰數據", "暂无差异化倍率显示": "暫無差異化倍率顯示", + "暂无已绑定项": "", "暂无常见问答": "暫無常見問答", "暂无成功模型": "暫無成功模型", + "暂无支付方式,点击上方按钮新增": "", "暂无数据": "暫無數據", "暂无数据,点击下方按钮添加键值对": "暫無數據,點擊下方按鈕添加鍵值對", "暂无日志": "暫無日誌", @@ -1575,6 +1890,7 @@ "更新": "更新", "更新 Creem 设置": "更新 Creem 設定", "更新 Stripe 设置": "更新 Stripe 設定", + "更新 Waffo 设置": "", "更新SSRF防护设置": "更新SSRF防護設定", "更新Worker设置": "更新Worker設定", "更新令牌信息": "更新令牌資訊", @@ -1597,15 +1913,8 @@ "更新配置后,容器可能需要重启以应用新的设置。请确保您了解这些更改的影响。": "更新設定後,容器可能需要重啟以應用新的設定。請確保您瞭解這些更改的影響。", "更新配置失败": "更新設定失敗", "更新预填组": "更新預填組", - "最低": "最低", - "最低充值美元数量": "最低儲值美元數量", - "最后使用时间": "最後使用時間", - "最后更新": "最後更新", - "最后请求": "最後請求", - "最大GPU数量": "最大GPU數量", - "最大可用": "最大可用", - "最近事件": "最近事件", - "最高优先级": "最高優先級", + "替换": "", + "月": "", "有 Reasoning": "有 Reasoning", "有序字符串数组": "有序字串陣列", "有效期": "有效期", @@ -1614,6 +1923,7 @@ "有效期设置": "有效期設定", "服务可用性": "服務可用性", "服务商": "服務商", + "服务器IP": "伺服器IP", "服务器地址": "伺服器位址", "服务器日志功能未启用(未配置日志目录)": "伺服器日誌功能未啟用(未配置日誌目錄)", "服务器日志管理": "伺服器日誌管理", @@ -1624,15 +1934,18 @@ "未启动": "未啟動", "未启用": "未啟用", "未命名": "未命名", + "未在 Discovery 响应中找到可用的 OAuth 端点": "", "未备份": "未備份", "未开始": "未開始", "未找到匹配的模型": "未找到匹配的模型", "未找到可用的容器访问地址": "未找到可用的容器訪問位址", "未找到差异化倍率,无需同步": "未找到差異化倍率,無需同步", + "未授权": "", "未提交": "未提交", "未检测到 Fluent 容器": "未檢測到 Fluent 容器", "未检测到 FluentRead(流畅阅读),请确认扩展已启用": "未檢測到 FluentRead(流暢閱讀),請確認擴展已啟用", "未测试": "未測試", + "未添加附加条件时,仅使用上方 type 进行清理。": "", "未登录或登录已过期,请重新登录": "未登錄或登錄已過期,請重新登錄", "未知": "未知", "未知供应商": "未知供應商", @@ -1647,16 +1960,18 @@ "未绑定": "未綁定", "未获取到授权码": "未獲取到授權碼", "未设置": "未設定", + "未设置价格": "", "未设置价格模型": "未設定價格模型", "未设置倍率模型": "未設定倍率模型", + "未设置路径": "", "未配置模型": "未設定模型", "未配置的模型列表": "未設定的模型列表", "本地": "本地", "本地数据存储": "本地數據存儲", "本地计费": "本地計費", "本月获得": "本月獲得", - "本设备内置": "本設備內置", "本设备:手机指纹/面容,外接:USB安全密钥": "本設備:手機指紋/面容,外接:USB安全密鑰", + "本设备内置": "本設備內置", "本项目根据": "本項目根據", "机密环境变量": "機密環境變數", "机密环境变量将被加密存储,适用于存储密码、API密钥等敏感信息。": "機密環境變數將被加密存儲,適用於存儲密碼、API密鑰等敏感資訊。", @@ -1665,14 +1980,21 @@ "权限设置": "權限設定", "条": "條", "条 - 第": "條 - 第", + "条,共": "條,共", + "条件取反": "", + "条件数": "", + "条件规则": "", + "条件项设置": "", "条日志已清理!": "條日誌已清理!", "条规则": "條規則", - "条,共": "條,共", "来源": "來源", "来源于 IO.NET 部署": "來源於 IO.NET 部署", + "来源端点": "", "来自模型重定向,尚未加入模型列表": "來自模型重定向,尚未加入模型列表", "某些配置更改可能需要几分钟才能生效。": "某些設定更改可能需要幾分鐘才能生效。", "查看": "查看", + "查看 Codex 帐号信息与用量": "", + "查看 JSON 示例": "", "查看关联部署": "查看關聯部署", "查看图片": "查看圖片", "查看密钥": "查看密鑰", @@ -1705,11 +2027,14 @@ "格式示例:": "格式示例:", "格式错误": "格式錯誤", "检查更新": "檢查更新", + "检测全部渠道上游更新": "", "检测到 FluentRead(流畅阅读)": "檢測到 FluentRead(流暢閱讀)", "检测到以下高危状态码重定向规则": "檢測到以下高風險狀態碼重定向規則", "检测到剪贴板中的连接信息": "偵測到剪貼簿中的連線資訊", "检测到多个密钥,您可以单独复制每个密钥,或点击复制全部获取完整内容。": "檢測到多個密鑰,您可以單獨複製每個密鑰,或點擊複製全部獲取完整內容。", "检测到该消息后有AI回复,是否删除后续回复并重新生成?": "檢測到該消息後有AI回覆,是否刪除後續回覆並重新生成?", + "检测失败": "", + "检测完成:新增 {{add}} 个,删除 {{remove}} 个": "", "检测必须等待绘图成功才能进行放大等操作": "檢測必須等待繪圖成功才能進行放大等操作", "概览": "概覽", "模型": "模型", @@ -1725,6 +2050,7 @@ "模型价格:{{symbol}}{{price}}": "模型價格:{{symbol}}{{price}}", "模型价格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}": "模型價格:{{symbol}}{{price}} * {{ratioType}}:{{ratio}} = {{symbol}}{{total}}", "模型价格:{{symbol}}{{price}} / 次": "模型價格:{{symbol}}{{price}} / 次", + "模型价格未配置": "模型價格未配置", "模型倍率": "模型倍率", "模型倍率 {{modelRatio}}": "模型倍率 {{modelRatio}}", "模型倍率 {{modelRatio}},缓存倍率 {{cacheRatio}},输出倍率 {{completionRatio}},{{ratioType}} {{ratio}}": "模型倍率 {{modelRatio}},快取倍率 {{cacheRatio}},輸出倍率 {{completionRatio}},{{ratioType}} {{ratio}}", @@ -1749,8 +2075,8 @@ "模型名称已存在": "模型名稱已存在", "模型固定价格": "模型固定價格", "模型图标": "模型圖示", - "模型定价设置": "模型定價設定", "模型定价,需要登录访问": "模型定價,需要登錄訪問", + "模型定价设置": "模型定價設定", "模型广场": "模型廣場", "模型拉取失败: {{error}}": "模型拉取失敗: {{error}}", "模型排行": "模型排行", @@ -1759,6 +2085,9 @@ "模型映射必须是合法的 JSON 格式!": "模型映射必須是合法的 JSON 格式!", "模型更新成功!": "模型更新成功!", "模型未加入列表,可能无法调用": "模型未加入列表,可能無法調用", + "模型正则": "", + "模型正则(每行一个)": "", + "模型正则不能为空": "", "模型消耗分布": "模型消耗分佈", "模型消耗趋势": "模型消耗趨勢", "模型版本": "模型版本", @@ -1781,22 +2110,32 @@ "模型重定向": "模型重定向", "模型重定向里的下列模型尚未添加到“模型”列表,调用时会因为缺少可用模型而失败:": "模型重定向裡的下列模型尚未添加到「模型」列表,調用時會因為缺少可用模型而失敗:", "模型限制列表": "模型限制列表", + "模式": "", + "模板": "", + "模板应用失败": "", "模板示例": "模板示例", "模糊搜索模型名称": "模糊搜尋模型名稱", "次": "次", "欢迎使用,请完成以下设置以开始使用系统": "歡迎使用,請完成以下設定以開始使用系統", "欧元": "歐元", + "正则替换": "", "正在加载可用部署位置...": "正在載入可用部署位置...", "正在加载签到状态...": "正在載入簽到狀態...", + "正在处理,请稍候": "", "正在处理大内容...": "正在處理大內容...", + "正在批量处理,请稍候": "", + "正在批量检测,请稍候": "", "正在提交": "正在提交", "正在构造请求体预览...": "正在構造請求體預覽...", "正在检查 io.net 连接...": "正在檢查 io.net 連接...", + "正在检测,请稍候": "", "正在测试第 ${current} - ${end} 个模型 (共 ${total} 个)": "正在測試第 ${current} - ${end} 個模型 (共 ${total} 個)", "正在跟随最新日志": "正在跟隨最新日誌", "正在跳转 GitHub...": "正在跳轉 GitHub...", "正在跳转...": "正在跳轉...", + "正常": "", "正常情况下,令牌的计费倍率由令牌所选的分组决定。特殊倍率可以根据「用户所在分组」进一步覆盖这个倍率。": "正常情況下,令牌的計費倍率由令牌所選的分組決定。特殊倍率可以根據「使用者所在分組」進一步覆蓋這個倍率。", + "正数为增加,负数为减少": "正數為增加,負數為減少", "此代理仅用于图片请求转发,Webhook通知发送等,AI API请求仍然由服务器直接发出,可在渠道设置中单独配置代理": "此代理僅用於圖片請求轉發,Webhook通知發送等,AI API請求仍然由伺服器直接發出,可在管道設定中單獨設定代理", "此修改将不可逆": "此修改將不可逆", "此操作不可恢复,请仔细确认时间后再操作!": "此操作不可恢復,請仔細確認時間後再操作!", @@ -1823,33 +2162,43 @@ "此项可选,用于覆盖请求参数。不支持覆盖 stream 参数": "此項可選,用於覆蓋請求參數。不支援覆蓋 stream 參數", "此项可选,用于覆盖请求头参数": "此項可選,用於覆蓋請求頭參數", "此项可选,用于通过自定义API地址来进行 API 调用,末尾不要带/v1和/": "此項可選,用於透過自訂API位址來進行 API 調用,末尾不要帶/v1和/", + "每个充值单位对应的 USD 金额,默认 1.0": "", "每个分组代表一个价格档位。管理员创建分组后,可以选择哪些档位对用户开放自选。": "每個分組代表一個價格檔位。管理員建立分組後,可以選擇哪些檔位對使用者開放自選。", + "每个用户最多可创建的令牌数量,默认 1000,设置过大可能会影响性能": "", + "每周": "", + "每天": "", "每容器GPU数": "每容器GPU數", "每日仅可签到一次,请勿重复签到": "每日僅可簽到一次,請勿重複簽到", "每日签到": "每日簽到", "每日签到可获得随机额度奖励": "每日簽到可獲得隨機額度獎勵", + "每月": "", "每美元对应 Token 数": "每美元對應 Token 數", "每隔多少分钟测试一次所有通道": "每隔多少分鐘測試一次所有通道", "永不过期": "永不過期", "永久删除您的两步验证设置": "永久刪除您的兩步驗證設定", "永久删除所有备用码(包括未使用的)": "永久刪除所有備用碼(包括未使用的)", "汇率": "匯率", + "沙盒模式": "", "沙盒环境 RSA 私钥 Base64 (PKCS#8 DER)": "沙盒環境 RSA 私鑰 Base64 (PKCS#8 DER)", + "沙盒环境 Waffo API 密钥": "", "沙盒环境 Waffo 公钥 Base64 (X.509 DER)": "沙盒環境 Waffo 公鑰 Base64 (X.509 DER)", + "没有匹配的字段": "", "没有匹配的日志条目": "沒有匹配的日誌條目", + "没有匹配的规则": "", "没有可用令牌用于填充": "沒有可用令牌用於填充", "没有可用模型": "沒有可用模型", "没有找到匹配的模型": "沒有找到匹配的模型", "没有未设置定价的模型": "沒有未設定定價的模型", "没有未设置的模型": "沒有未設定的模型", + "没有条件时,默认总是执行该操作。": "", "没有模型可以复制": "沒有模型可以複製", "没有账户?": "沒有帳號?", "注 册": "注 冊", "注册": "註冊", "注册 Passkey": "註冊 Passkey", "注意": "注意", - "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "注意非Chat API,請務必填寫正確的API位址,否則可能導致無法使用", "注意:JSON中重复的键只会保留最后一个同名键的值": "注意:JSON中重複的鍵只會保留最後一個同名鍵的值", + "注意非Chat API,请务必填写正确的API地址,否则可能导致无法使用": "注意非Chat API,請務必填寫正確的API位址,否則可能導致無法使用", "注销": "註銷", "注销成功!": "註銷成功!", "活跃文件": "活躍檔案", @@ -1858,7 +2207,9 @@ "流式": "流式", "流式响应完成": "流式響應完成", "流式输出": "流式輸出", + "流状态": "", "流量端口": "流量端口", + "流错误详情": "", "浅色": "淺色", "浅色模式": "淺色模式", "测活": "測活", @@ -1888,6 +2239,7 @@ "深色模式": "深色模式", "添加": "新增", "添加 (+:)": "新增 (+:)", + "添加 OAuth 提供商": "", "添加API": "添加API", "添加产品": "添加產品", "添加令牌": "添加令牌", @@ -1912,20 +2264,15 @@ "添加键值对": "添加鍵值對", "添加问答": "添加問答", "添加额度": "添加額度", - "减少": "減少", - "覆盖": "覆蓋", - "调整额度": "調整額度", - "调整额度成功": "調整額度成功", - "当前额度": "當前額度", - "变更": "變更", - "预计结果": "預計結果", - "正数为增加,负数为减少": "正數為增加,負數為減少", "清理不活跃缓存": "清理不活躍快取", "清理失败": "清理失敗", "清理方式": "清理方式", "清理日志文件": "清理日誌檔案", "清空": "清空", + "清空全部缓存": "", + "清空该规则缓存": "", "清空重定向": "清空重定向", + "清除": "", "清除历史日志": "清除歷史日誌", "清除失效兑换码": "清除失效兌換碼", "清除所有模型": "清除所有模型", @@ -1933,6 +2280,8 @@ "渠道 ID": "管道 ID", "渠道ID,名称,密钥,API地址": "管道ID,名稱,密鑰,API位址", "渠道亲和性": "渠道親和性", + "渠道亲和性:上游缓存命中": "", + "渠道亲和性会基于从请求上下文或 JSON Body 提取的 Key,优先复用上一次成功的渠道。": "", "渠道优先级": "管道優先級", "渠道信息": "管道資訊", "渠道创建成功!": "管道建立成功!", @@ -1953,12 +2302,15 @@ "渠道行为": "頻道行為", "渠道额外设置": "管道額外設定", "源地址": "源位址", + "满足任一条件(OR)": "", "演示站点": "演示站點", "演示站点模式": "演示站點模式", "点击 + 按钮添加图片URL进行多模态对话": "點擊 + 按鈕添加圖片URL進行多模態對話", "点击\"确认延长\"后将立即扣除费用并延长容器运行时间": "點擊\"確認延長\"後將立即扣除費用並延長容器運行時間", "点击上传文件或拖拽文件到这里": "點擊上傳檔案或拖拽檔案到這裡", "点击下方按钮通过 Telegram 完成绑定": "點擊下方按鈕透過 Telegram 完成綁定", + "点击处理删除模型": "", + "点击处理新增模型": "", "点击复制ID": "點擊複製ID", "点击复制模型名称": "點擊複製模型名稱", "点击查看差异": "點擊查看差異", @@ -1970,14 +2322,17 @@ "特殊倍率": "特殊倍率", "特殊可用分组规则": "特殊可用分組規則", "状态": "狀態", + "状态码": "", "状态码复写": "狀態碼複寫", "状态码复写包含无效的状态码": "狀態碼複寫包含無效的狀態碼", "状态筛选": "狀態篩選", "状态页面Slug": "狀態頁面Slug", "环境变量": "環境變數", "生产环境 RSA 私钥 Base64 (PKCS#8 DER)": "正式環境 RSA 私鑰 Base64 (PKCS#8 DER)", + "生产环境 Waffo API 密钥": "", "生产环境 Waffo 公钥 Base64 (X.509 DER)": "正式環境 Waffo 公鑰 Base64 (X.509 DER)", "生成令牌": "生成令牌", + "生成并填入": "", "生成数量": "生成數量", "生成数量必须大于0": "生成數量必須大於0", "生成新的备用码": "生成新的備用碼", @@ -1985,6 +2340,7 @@ "生成音乐": "生成音樂", "生效": "生效", "用于API调用的身份验证令牌,请妥善保管": "用於API調用的身份驗證令牌,請妥善保管", + "用于唯一标识用户的字段路径": "", "用于配置网络代理,支持 socks5 协议": "用於設定網路代理,支援 socks5 協議", "用于验证回调 new-api 的 webhook 请求的密钥,敏感信息不显示": "用於驗證回調 new-api 的 webhook 請求的密鑰,敏感資訊不顯示", "用以支持基于 WebAuthn 的无密码登录注册": "用以支援基於 WebAuthn 的無密碼登錄註冊", @@ -1999,12 +2355,15 @@ "用以防止恶意用户利用临时邮箱批量注册": "用以防止惡意使用者利用臨時信箱批量註冊", "用户": "使用者", "用户 ID 字段": "使用者 ID 字段", + "用户 ID 字段(可选)": "", "用户个人功能": "使用者個人功能", "用户主页,展示系统信息": "使用者首頁,展示系統訊息", "用户优先:如果用户在请求中指定了系统提示词,将优先使用用户的设置": "使用者優先:如果使用者在請求中指定了系統提示詞,將優先使用使用者的設定", "用户信息": "使用者資訊", "用户信息更新成功!": "使用者資訊更新成功!", "用户信息端点": "使用者資訊端點", + "用户信息缺失": "", + "用户最大令牌数量": "", "用户分组": "使用者分組", "用户分组和额度管理": "使用者分組和額度管理", "用户分组的联动作用": "使用者分組的連動作用", @@ -2019,8 +2378,10 @@ "用户可选分组的名称和描述(只包含勾选了用户可选的分组)": "使用者可選分組的名稱和描述(只包含勾選了使用者可選的分組)", "用户名": "使用者名", "用户名字段": "使用者名字段", + "用户名字段(可选)": "", "用户名或邮箱": "使用者名或信箱", "用户名称": "使用者名稱", + "用户在充值页面看到的支付方式名称,例如:Credit Card": "", "用户控制面板,管理账户": "使用者控制面板,管理帳號", "用户新建令牌时可选的分组,格式为 JSON 字符串,例如:{\"vip\": \"VIP 用户\", \"test\": \"测试\"},表示用户可以选择 vip 分组和 test 分组": "使用者新建令牌時可選的分組,格式為 JSON 字符串,例如:{\"vip\": \"VIP 使用者\", \"test\": \"測試\"},表示使用者可以選擇 vip 分組和 test 分組", "用户每周期最多请求完成次数": "使用者每週期最多請求完成次數", @@ -2042,6 +2403,8 @@ "留空使用系统临时目录": "留空使用系統臨時目錄", "留空则使用账号绑定的邮箱": "留空則使用帳號綁定的信箱", "留空则使用默认端点;支持 {path, method}": "留空則使用預設端點;支援 {path, method}", + "留空则保持原有密钥": "", + "留空则自动使用 服务器地址 + /api/waffo/webhook": "", "留空则默认使用服务器地址,注意不能携带http://或者https://": "留空則預設使用伺服器位址,注意不能攜帶http://或者https://", "登 录": "登 錄", "登录": "登錄", @@ -2054,7 +2417,10 @@ "目录文件数": "目錄檔案數", "目标分组": "目標分組", "目标用户:{{username}}": "目標使用者:{{username}}", + "目标端点": "", + "目标路径(可选)": "", "直接提交": "直接提交", + "直接编辑 JSON 文本,保存时会校验格式。": "", "直接追加(和添加类似,但无前缀)": "直接追加(和新增類似,但無前綴)", "相关项目": "相關項目", "相当于删除用户,此修改将不可逆": "相當於刪除使用者,此修改將不可逆", @@ -2065,6 +2431,7 @@ "硬件类型": "硬體類型", "硬件配置": "硬體設定", "确定": "確定", + "确定?": "確定?", "确定删除此组?": "確定刪除此組?", "确定导入": "確定導入", "确定是否要修复数据库一致性?": "確定是否要修復資料庫一致性?", @@ -2077,6 +2444,7 @@ "确定是否要复制此渠道?": "確定是否要複製此管道?", "确定是否要注销此用户?": "確定是否要註銷此使用者?", "确定清除所有失效兑换码?": "確定清除所有失效兌換碼?", + "确定要仅检测全部渠道上游模型更新吗?(不执行新增/删除)": "", "确定要修改所有子渠道优先级为 ": "確定要修改所有子管道優先級為 ", "确定要修改所有子渠道权重为 ": "確定要修改所有子管道權重為 ", "确定要充值 $": "確定要儲值 $", @@ -2084,6 +2452,7 @@ "确定要删除所有已自动禁用的密钥吗?": "確定要刪除所有已自動禁用的密鑰嗎?", "确定要删除所选的 {{count}} 个令牌吗?_other": "確定要刪除所選的 {{count}} 個令牌嗎?", "确定要删除所选的 {{count}} 个模型吗?_other": "確定要刪除所選的 {{count}} 個模型嗎?", + "确定要删除此 OAuth 提供商吗?": "", "确定要删除此API信息吗?": "確定要刪除此API資訊嗎?", "确定要删除此公告吗?": "確定要刪除此公告嗎?", "确定要删除此分类吗?": "確定要刪除此分類嗎?", @@ -2094,6 +2463,7 @@ "确定要删除选中的": "確定要刪除選中的", "确定要启用所有密钥吗?": "確定要啟用所有密鑰嗎?", "确定要启用此用户吗?": "確定要啟用此使用者嗎?", + "确定要对全部渠道执行上游模型更新吗?": "", "确定要提升此用户吗?": "確定要提升此使用者嗎?", "确定要更新所有已启用通道余额吗?": "確定要更新所有已啟用通道餘額嗎?", "确定要测试所有未手动禁用渠道吗?": "確定要測試所有未手動停用通道嗎?", @@ -2104,17 +2474,17 @@ "确定要降级此用户吗?": "確定要降級此使用者嗎?", "确定重置": "確定重置", "确定重置模型倍率吗?": "確定重置模型倍率嗎?", - "确定?": "確定?", "确认": "確認", "确认作废": "確認作廢", "确认关闭提示": "確認關閉提示", "确认冲突项修改": "確認衝突項修改", "确认删除": "確認刪除", "确认删除模型": "確認刪除模型", - "确认删除该分组的所有规则?": "確認刪除該分組的所有規則?", "确认删除该分组?": "確認刪除該分組?", + "确认删除该分组的所有规则?": "確認刪除該分組的所有規則?", "确认删除该规则?": "確認刪除該規則?", "确认取消密码登录": "確認取消密碼登錄", + "确认启用": "", "确认密码": "確認密碼", "确认导入配置": "確認導入設定", "确认延长": "確認延長", @@ -2123,6 +2493,8 @@ "确认新密码": "確認新密碼", "确认清理不活跃的磁盘缓存?": "確認清理不活躍的磁碟快取?", "确认清理日志文件?": "確認清理日誌檔案?", + "确认清空全部渠道亲和性缓存": "", + "确认清空该规则缓存": "", "确认清除历史日志": "確認清除歷史日誌", "确认禁用": "確認禁用", "确认移除?": "確認移除?", @@ -2137,13 +2509,16 @@ "磁盘使用率超过此值时拒绝请求": "磁碟使用率超過此值時拒絕請求", "磁盘可用空间小于缓存最大总量设置": "磁碟可用空間小於快取最大總量設定", "磁盘命中": "磁碟命中", + "磁盘缓存最大总量 (MB)": "磁碟快取最大總量 (MB)", "磁盘缓存占用的最大空间": "磁碟快取佔用的最大空間", "磁盘缓存已清理": "磁碟快取已清理", - "磁盘缓存最大总量 (MB)": "磁碟快取最大總量 (MB)", "磁盘缓存设置(磁盘换内存)": "磁碟快取設定(磁碟換記憶體)", "磁盘缓存阈值 (MB)": "磁碟快取閾值 (MB)", "示例": "示例", "示例:{\"default\": [200, 100], \"vip\": [0, 1000]}。": "示例:{\"default\": [200, 100], \"vip\": [0, 1000]}。", + "视频": "影片", + "视频Remix": "影片 Remix", + "视频无法在当前浏览器中播放,这可能是由于:": "影片無法在當前瀏覽器中播放,這可能是由於:", "禁用": "禁用", "禁用 store 透传": "禁用 store 透傳", "禁用2FA失败": "禁用2FA失敗", @@ -2161,10 +2536,12 @@ "私有镜像仓库的密码": "私有鏡像倉庫的密碼", "私有镜像仓库的用户名": "私有鏡像倉庫的使用者名", "秒": "秒", + "移动": "", + "移动请求头": "", "移除": "移除", "移除 (-:)": "移除 (-:)", - "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "移除 One API 的版權標識必須首先獲得授權,項目維護需要花費大量精力,如果本項目對你有意義,請主動支援本項目", "移除 functionResponse.id 字段": "移除 functionResponse.id 字段", + "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "移除 One API 的版權標識必須首先獲得授權,項目維護需要花費大量精力,如果本項目對你有意義,請主動支援本項目", "空": "空", "窗口处理": "窗口處理", "窗口等待": "窗口等待", @@ -2183,22 +2560,34 @@ "端点映射": "端點映射", "端点类型": "端點類型", "端点组": "端點組", + "第 {{line}} 条 prune_objects 缺少条件": "", + "第 {{line}} 条 prune_objects 需要至少一个匹配条件": "", + "第 {{line}} 条 return_error 需要 message 字段": "", + "第 {{line}} 条操作缺少值": "", + "第 {{line}} 条操作缺少来源字段": "", + "第 {{line}} 条操作缺少目标字段": "", + "第 {{line}} 条操作缺少目标路径": "", + "第 {{line}} 条请求头透传格式无效": "", + "第 {{line}} 条请求头透传缺少请求头名称": "", "第三方支付配置": "第三方支付設定", "第三方账户绑定状态(只读)": "第三方帳號綁定狀態(唯讀)", "等价金额:": "等價金額:", "等待中": "等待中", "等待获取邮箱信息...": "等待獲取信箱資訊...", "筛选": "篩選", + "签到最大额度": "簽到最大額度", + "签到最小额度": "簽到最小額度", "签到功能允许用户每日签到获取随机额度奖励": "簽到功能允許使用者每日簽到獲取隨機額度獎勵", "签到失败": "簽到失敗", "签到奖励将直接添加到您的账户余额": "簽到獎勵將直接添加到您的帳號餘額", "签到奖励的最大额度": "簽到獎勵的最大額度", "签到奖励的最小额度": "簽到獎勵的最小額度", "签到成功!获得": "簽到成功!獲得", - "签到最大额度": "簽到最大額度", - "签到最小额度": "簽到最小額度", "签到设置": "簽到設定", "简单来说:同一个令牌分组,不同等级的用户可以享受不同的价格。": "簡單來說:同一個令牌分組,不同等級的使用者可以享受不同的價格。", + "简洁": "", + "简洁模式:按 type 全量清理对象,例如 redacted_thinking。": "", + "简洁模式仅返回 message;状态码和错误类型将使用系统默认值。": "", "管理": "管理", "管理 Ollama 模型的拉取和删除": "管理 Ollama 模型的拉取和刪除", "管理你的 LinuxDO OAuth App": "管理你的 LinuxDO OAuth App", @@ -2209,8 +2598,8 @@ "管理员暂时未设置任何关于内容": "管理員暫時未設定任何關於內容", "管理员未开启 Creem 充值!": "管理員未開啟 Creem 儲值!", "管理员未开启Stripe充值!": "管理員未開啟Stripe儲值!", - "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "管理員未開啟在線儲值功能,請聯繫管理員開啟或使用兌換碼儲值。", "管理员未开启在线充值!": "管理員未開啟在線儲值!", + "管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。": "管理員未開啟在線儲值功能,請聯繫管理員開啟或使用兌換碼儲值。", "管理员未开启在线支付功能,请联系管理员配置。": "管理員未開啟在線支付功能,請聯繫管理員設定。", "管理员未设置用户可选分组": "管理員未設定使用者可選分組", "管理员给用户分配的分组(如 vip)不仅决定用户身份,还会影响后续两个功能:": "管理員給使用者分配的分組(如 vip)不僅決定使用者身份,還會影響後續兩個功能:", @@ -2219,7 +2608,10 @@ "管理员账号已经初始化过,请继续设置其他参数": "管理員帳號已經初始化過,請繼續設定其他參數", "管理服务器运行日志文件。日志文件会随运行时间不断累积,建议定期清理以释放磁盘空间。": "管理伺服器運行日誌檔案。日誌檔案會隨運行時間不斷累積,建議定期清理以釋放磁碟空間。", "管理模型、标签、端点等预填组": "管理模型、標籤、端點等預填組", + "管理用户已绑定的第三方账户,支持筛选与解绑": "", + "管理绑定": "", "类型": "類型", + "类型(常用)": "", "粘贴图片失败": "貼上圖片失敗", "精确": "精確", "系统": "系統", @@ -2247,14 +2639,16 @@ "系统数据统计": "系統數據統計", "系统文档和帮助信息": "系統文件和幫助資訊", "系统消息": "系統消息", + "系统版本": "系統版本", "系统管理功能": "系統管理功能", "系统设置": "系統設定", "系统访问令牌": "系統訪問令牌", + "约": "約", "索引": "索引", "紧凑列表": "緊湊列表", + "纯字符串会直接覆盖整条请求头,或者点击“查看 JSON 示例”按 token 规则处理。": "", "累计签到": "累計簽到", "累计获得": "累計獲得", - "约": "約", "线路描述": "線路描述", "组列表": "組列表", "组名": "組名", @@ -2273,6 +2667,7 @@ "结束": "結束", "结束时间": "結束時間", "结果图片": "結果圖片", + "结算差额": "", "绘图": "繪圖", "绘图任务记录": "繪圖任務記錄", "绘图日志": "繪圖日誌", @@ -2283,45 +2678,48 @@ "统计次数": "統計次數", "统计额度": "統計額度", "继续": "繼續", - "缓存 Tokens": "快取 Tokens", "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "快取 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "快取 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})", + "缓存 Tokens": "快取 Tokens", "缓存: {{cacheRatio}}": "快取: {{cacheRatio}}", "缓存价格:{{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "快取價格:{{symbol}}{{price}} * {{cacheRatio}} = {{symbol}}{{total}} / 1M tokens (快取倍率: {{cacheRatio}})", "缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})": "快取價格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (快取倍率: {{cacheRatio}})", "缓存倍率": "快取倍率", "缓存倍率 {{cacheRatio}}": "快取倍率 {{cacheRatio}}", "缓存写": "快取寫", - "缓存创建 Tokens": "快取建立 Tokens", "缓存创建 {{price}} / 1M tokens": "快取建立 {{price}} / 1M tokens", "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "快取建立 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}", "缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})": "快取建立 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})", + "缓存创建 Tokens": "快取建立 Tokens", + "缓存创建: {{cacheCreationRatio}}": "快取建立: {{cacheCreationRatio}}", "缓存创建: 1h {{cacheCreationRatio1h}}": "快取建立: 1h {{cacheCreationRatio1h}}", "缓存创建: 5m {{cacheCreationRatio5m}}": "快取建立: 5m {{cacheCreationRatio5m}}", "缓存创建: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "快取建立: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存创建: {{cacheCreationRatio}}": "快取建立: {{cacheCreationRatio}}", + "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "快取建立:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 快取建立倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存创建价格": "快取建立價格", "缓存创建价格 {{symbol}}{{price}} / 1M tokens": "快取建立價格 {{symbol}}{{price}} / 1M tokens", - "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "快取建立價格合計:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "快取建立價格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (快取建立倍率: {{cacheCreationRatio}})", "缓存创建价格:{{symbol}}{{price}} / 1M tokens": "快取建立價格:{{symbol}}{{price}} / 1M tokens", + "缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens": "快取建立價格合計:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens", "缓存创建倍率": "快取建立倍率", + "缓存创建倍率 {{cacheCreationRatio}}": "快取建立倍率 {{cacheCreationRatio}}", "缓存创建倍率 1h {{cacheCreationRatio1h}}": "快取建立倍率 1h {{cacheCreationRatio1h}}", "缓存创建倍率 5m {{cacheCreationRatio5m}}": "快取建立倍率 5m {{cacheCreationRatio5m}}", "缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}": "快取建立倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}", - "缓存创建倍率 {{cacheCreationRatio}}": "快取建立倍率 {{cacheCreationRatio}}", - "缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "快取建立:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 快取建立倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "缓存条目数": "", "缓存目录": "快取目錄", "缓存目录磁盘空间": "快取目錄磁碟空間", "缓存读": "快取讀", "缓存读 {{price}} / 1M tokens": "快取讀 {{price}} / 1M tokens", + "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "快取讀取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 快取倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "缓存读取价格": "快取讀取價格", "缓存读取价格 {{symbol}}{{price}} / 1M tokens": "快取讀取價格 {{symbol}}{{price}} / 1M tokens", "缓存读取价格:{{symbol}}{{price}} / 1M tokens": "快取讀取價格:{{symbol}}{{price}} / 1M tokens", "缓存读取价格:{{symbol}}{{total}} / 1M tokens": "快取讀取價格:{{symbol}}{{total}} / 1M tokens", - "缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "快取讀取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 快取倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "缓存读取倍率": "", "缓存输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "快取輸入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 快取倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "编辑": "編輯", + "编辑 OAuth 提供商": "", "编辑API": "編輯API", "编辑产品": "編輯產品", "编辑供应商": "編輯供應商", @@ -2330,11 +2728,14 @@ "编辑分类": "編輯分類", "编辑成功": "編輯成功", "编辑提供商": "編輯提供商", + "编辑支付方式": "", + "编辑方式": "", "编辑标签": "編輯標籤", "编辑模型": "編輯模型", "编辑模式": "編輯模式", "编辑用户": "編輯使用者", "编辑聊天配置": "編輯聊天設定", + "编辑规则": "", "编辑问答": "編輯問答", "缩词": "縮詞", "缺省 MaxTokens": "缺省 MaxTokens", @@ -2360,6 +2761,7 @@ "自动分组选择": "自動分組選擇", "自动刷新": "自動刷新", "自动刷新中": "自動刷新中", + "自动填充字段": "", "自动填入": "自動填入", "自动检测": "自動檢測", "自动模式": "自動模式", @@ -2372,6 +2774,8 @@ "自动选择": "自動選擇", "自动重试状态码": "自動重試狀態碼", "自动重试状态码格式不正确": "自動重試狀態碼格式不正確", + "自定义": "", + "自定义 JSON": "", "自定义 OAuth 提供商": "自訂 OAuth 提供商", "自定义充值数量选项": "自訂儲值數量選項", "自定义充值数量选项不是合法的 JSON 数组": "自訂儲值數量選項不是合法的 JSON 陣列", @@ -2383,6 +2787,7 @@ "自定义货币": "自訂貨幣", "自定义货币符号": "自訂貨幣符號", "自定义货币符号将显示在所有额度数值前,例如 €1.50": "自訂貨幣符號將顯示在所有額度數值前,例如 €1.50", + "自定义错误响应": "", "自定义镜像": "自訂鏡像", "自用模式": "自用模式", "自适应列表": "動態列表", @@ -2391,11 +2796,14 @@ "花费": "花費", "花费时间": "花費時間", "若你的 OIDC Provider 支持 Discovery Endpoint,你可以仅填写 OIDC Well-Known URL,系统会自动获取 OIDC 配置": "若你的 OIDC Provider 支援 Discovery Endpoint,你可以僅填寫 OIDC Well-Known URL,系統會自動獲取 OIDC 設定", + "获取 Discovery 配置": "", + "获取 Discovery 配置失败:": "", + "获取 io.net API Key": "獲取 io.net API Key", "获取 OIDC 配置失败,请检查网络状况和 Well-Known URL 是否正确": "獲取 OIDC 設定失敗,請檢查網路狀況和 Well-Known URL 是否正確", "获取 OIDC 配置成功!": "獲取 OIDC 設定成功!", "获取 Ollama 版本失败": "獲取 Ollama 版本失敗", - "获取 io.net API Key": "獲取 io.net API Key", "获取2FA状态失败": "獲取2FA狀態失敗", + "获取令牌密钥失败": "", "获取充值配置失败": "取得儲值設定失敗", "获取充值配置异常": "儲值設定異常", "获取初始化状态失败": "獲取初始化狀態失敗", @@ -2416,6 +2824,8 @@ "获取硬件类型失败: ": "獲取硬體類型失敗: ", "获取签到状态失败": "獲取簽到狀態失敗", "获取组列表失败": "獲取組列表失敗", + "获取绑定信息失败": "", + "获取自定义 OAuth 提供商列表失败": "", "获取详情失败": "獲取詳情失敗", "获取部署列表失败": "獲取部署列表失敗", "获取金额失败": "獲取金額失敗", @@ -2425,9 +2835,9 @@ "补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "補全 {{completion}} tokens * 輸出倍率 {{completionRatio}}", "补全 {{completion}} tokens / 1M tokens * {{symbol}}{{price}}": "補全 {{completion}} tokens / 1M tokens * {{symbol}}{{price}}", "补全价格": "補全價格", - "补全价格已锁定": "補全價格已鎖定", "补全价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "補全價格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (補全倍率: {{completionRatio}})", "补全价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens": "補全價格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens", + "补全价格已锁定": "補全價格已鎖定", "补全倍率": "補全倍率", "补全倍率 {{completionRatio}}": "補全倍率 {{completionRatio}}", "补全倍率值": "補全倍率值", @@ -2436,43 +2846,56 @@ "补单成功": "補單成功", "表单引用错误,请刷新页面重试": "表單引用錯誤,請刷新頁面重試", "表格视图": "表格視圖", + "覆盖": "覆蓋", "覆盖模式:将完全替换现有的所有密钥": "覆蓋模式:將完全替換現有的所有密鑰", + "覆盖模板": "", "覆盖现有密钥": "覆蓋現有密鑰", "规则": "規則", - "视频": "影片", - "视频Remix": "影片 Remix", - "视频无法在当前浏览器中播放,这可能是由于:": "影片無法在當前瀏覽器中播放,這可能是由於:", + "规则 JSON": "", + "规则 JSON 格式不正确": "", + "规则 ttl_seconds 为 0 时使用。0 表示使用后端默认 TTL:3600 秒。": "", + "规则为 JSON 数组;可视化与 JSON 模式共用同一份数据。": "", + "规则名称(可读性更好,也会出现在管理侧日志中)。": "", + "规则导航": "", + "规则描述(可选)": "", + "规则未找到,请刷新后重试": "", "角色": "角色", "解析响应数据时发生错误": "解析響應數據時發生錯誤", "解析密钥文件失败: {{msg}}": "解析密鑰檔案失敗: {{msg}}", "解析错误": "解析錯誤", + "解绑": "", "解绑 Passkey": "解綁 Passkey", "解绑后将无法使用 Passkey 登录,确定要继续吗?": "解綁後將無法使用 Passkey 登錄,確定要繼續嗎?", "解绑成功": "解綁成功", - "警告": "警告", - "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔": "警告:啟用保活後,如果已經寫入保活數據後管道出錯,系統無法重試,如果必須開啟,推薦設定儘可能大的Ping間隔", - "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "警告:禁用兩步驗證將永久刪除您的驗證設定和所有備用碼,此操作不可撤銷!", "计价币种": "計價幣種", "计算中": "計算中", "计算成本": "計算成本", "计算费用中...": "計算費用中...", "计费乘数,倍率越低费用越低。例如倍率 0.5 表示半价。": "計費乘數,倍率越低費用越低。例如倍率 0.5 表示半價。", "计费开始": "計費開始", + "计费摘要": "", + "计费方式": "", "计费显示模式": "計費顯示模式", "计费模式": "計費模式", "计费类型": "計費類型", "计费过程": "計費過程", "订单号": "訂單號", + "订单支付方式": "訂單支付方式", "订阅": "訂閱", + "订阅剩余": "", "订阅套餐": "訂閱", "订阅套餐管理": "訂閱管理", + "订阅实例": "", "订阅抵扣": "訂閱抵扣", "订阅管理": "訂閱管理", + "订阅结算": "", + "订阅说明": "", "认证方式": "認證方式", "讯飞星火": "訊飛星火", "记录请求与错误日志IP": "記錄請求與錯誤日誌IP", "设备": "設備", "设备类型偏好": "設備類型偏好", + "设置": "", "设置 Logo": "設定 Logo", "设置2FA失败": "設定2FA失敗", "设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}": "設定不同儲值金額對應的折扣,鍵為儲值金額,值為折扣率,例如:{\"100\": 0.95, \"200\": 0.9, \"500\": 0.85}", @@ -2501,6 +2924,7 @@ "设置首页内容": "設定首頁內容", "设置默认地区和特定模型的专用地区": "設定預設地區和特定模型的專用地區", "设计与开发由": "設計與開發由", + "设请求头": "", "访问 io.net 控制台的 API Keys 页面": "訪問 io.net 控制檯的 API Keys 頁面", "访问容器": "訪問容器", "访问模型部署功能需要先启用 io.net 部署服务": "訪問模型部署功能需要先啟用 io.net 部署服務", @@ -2516,6 +2940,12 @@ "该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "該模型補全倍率由後端固定為 {{ratio}}。補全價格不能在這裡修改。", "该渠道已开启请求透传,参数覆写、模型重定向等 NewAPI 内置功能将失效,非最佳实践。": "該管道已開啟請求透傳,參數覆寫、模型重定向等 NewAPI 內置功能將失效,非最佳實踐。", "该渠道已开启请求透传:参数覆写、模型重定向、渠道适配等 NewAPI 内置功能将失效,非最佳实践;如因此产生问题,请勿提交 issue 反馈。": "該管道已開啟請求透傳:參數覆寫、模型重定向、管道相容等 NewAPI 內置功能將失效,非最佳實踐;如因此產生問題,請勿提交 issue 回饋。", + "该渠道暂无可处理的上游模型更新": "", + "该渠道未开启上游模型更新检测": "", + "该规则未启用“作用域:包含规则名称”,无法按规则清空缓存。": "", + "该规则未设置参数覆盖模板": "", + "该规则的缓存保留时长;0 表示使用默认 TTL:": "", + "该记录不包含可用的 token 统计口径。": "", "详情": "詳情", "详见「特殊倍率」和「可用分组」标签页。": "詳見「特殊倍率」和「可用分組」標籤頁。", "语言偏好": "語言偏好", @@ -2523,9 +2953,10 @@ "语音输入": "語音輸入", "语音输出": "語音輸出", "说明": "說明", - "说明信息": "說明資訊", "说明:": "說明:", "说明:本页测试为非流式请求;若渠道仅支持流式返回,可能出现测试失败,请以实际使用为准。": "說明:本頁測試為非流式請求;若管道僅支援流式返回,可能出現測試失敗,請以實際使用為準。", + "说明:生成结果是可直接粘贴到渠道密钥里的 JSON(包含 access_token / refresh_token / account_id)。": "", + "说明信息": "說明資訊", "请上传密钥文件": "請上傳密鑰檔案", "请上传密钥文件!": "請上傳密鑰檔案!", "请为渠道命名": "請為管道命名", @@ -2533,12 +2964,17 @@ "请先勾选需要批量设置的模型": "請先勾選需要批量設定的模型", "请先在设置中启用图片功能": "請先在設定中啟用圖片功能", "请先填写 API Key": "請先填寫 API Key", + "请先填写 Discovery URL 或 Issuer URL": "", + "请先填写 Issuer URL,以自动生成完整的端点 URL": "", "请先填写 Ollama API 地址": "請先填寫 Ollama API 位址", "请先填写服务器地址": "請先填寫伺服器位址", "请先填写服务器地址,以自动生成完整的端点 URL": "請先填寫伺服器位址,以自動生成完整的端點 URL", "请先开启并填写音频输入价格。": "請先開啟並填寫音訊輸入價格。", + "请先新增模型或从左侧列表选择一个模型": "", + "请先粘贴回调 URL": "", "请先输入密钥": "請先輸入密鑰", "请先选择一个作为模板的模型": "請先選擇一個作為範本的模型", + "请先选择一条规则": "", "请先选择同步渠道": "請先選擇同步管道", "请先选择模型!": "請先選擇模型!", "请先选择硬件类型": "請先選擇硬體類型", @@ -2550,6 +2986,7 @@ "请再次输入新密码": "請再次輸入新密碼", "请前往个人设置 → 安全设置进行配置。": "請前往個人設定 → 安全設定進行設定。", "请勿过度信任此功能,IP可能被伪造,请配合nginx和cdn等网关使用": "請勿過度信任此功能,IP可能被偽造,請配合nginx和cdn等網關使用", + "请在 Waffo 后台获取 API 密钥、商户 ID 以及 RSA 密钥对,并配置回调地址。": "", "请在系统设置页面编辑分组倍率以添加新的分组:": "請在系統設定頁面編輯分組倍率以添加新的分組:", "请填写完整的产品信息": "請填寫完整的產品資訊", "请填写完整的管理员账号信息": "請填寫完整的管理員帳號資訊", @@ -2568,7 +3005,6 @@ "请求参数无效": "請求參數無效", "请求发生错误": "請求發生錯誤", "请求发生错误: ": "請求發生錯誤: ", - "模型价格未配置": "模型價格未配置", "请求后端接口失败:": "請求後端接口失敗:", "请求失败": "請求失敗", "请求头覆盖": "請求頭覆蓋", @@ -2586,6 +3022,7 @@ "请确认您已了解禁用两步验证的后果": "請確認您已瞭解禁用兩步驗證的後果", "请确认管理员密码": "請確認管理員密碼", "请稍后几秒重试,Turnstile 正在检查用户环境!": "請稍後幾秒重試,Turnstile 正在檢查使用者環境!", + "请粘贴完整回调 URL(包含 code 与 state)": "", "请联系管理员在系统设置中配置API信息": "請聯繫管理員在系統設定中設定API資訊", "请联系管理员在系统设置中配置Uptime": "請聯繫管理員在系統設定中設定Uptime", "请联系管理员在系统设置中配置公告信息": "請聯繫管理員在系統設定中設定公告資訊", @@ -2598,11 +3035,18 @@ "请至少选择一个渠道": "請至少選擇一個管道", "请输入 API Key,一行一个,格式:APIKey|Region": "請輸入 API Key,一行一個,格式:APIKey|Region", "请输入 API Key,格式:APIKey|Region": "請輸入 API Key,格式:APIKey|Region", + "请输入 Authorization Endpoint": "", "请输入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com": "請輸入 AZURE_OPENAI_ENDPOINT,例如:https://docs-test-001.openai.azure.com", - "请输入 JSON 格式的密钥内容,例如:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}": "請輸入 JSON 格式的密鑰內容,例如:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}", - "请输入 OIDC 的 Well-Known URL": "請輸入 OIDC 的 Well-Known URL", + "请输入 Client ID": "", + "请输入 Client Secret": "", "请输入 io.net API Key": "請輸入 io.net API Key", "请输入 io.net API Key(敏感信息不显示)": "請輸入 io.net API Key(敏感資訊不顯示)", + "请输入 JSON 格式的 OAuth 凭据,例如:\n{\n \"access_token\": \"...\",\n \"account_id\": \"...\" \n}": "", + "请输入 JSON 格式的密钥内容,例如:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}": "請輸入 JSON 格式的密鑰內容,例如:\n{\n \"type\": \"service_account\",\n \"project_id\": \"your-project-id\",\n \"private_key_id\": \"...\",\n \"private_key\": \"...\",\n \"client_email\": \"...\",\n \"client_id\": \"...\",\n \"auth_uri\": \"...\",\n \"token_uri\": \"...\",\n \"auth_provider_x509_cert_url\": \"...\",\n \"client_x509_cert_url\": \"...\"\n}", + "请输入 OIDC 的 Well-Known URL": "請輸入 OIDC 的 Well-Known URL", + "请输入 Slug": "", + "请输入 Token Endpoint": "", + "请输入 User Info Endpoint": "", "请输入6位验证码或8位备用码": "請輸入6位驗證碼或8位備用碼", "请输入API地址": "請輸入API位址", "请输入API地址!": "請輸入API位址!", @@ -2612,9 +3056,9 @@ "请输入Gotify服务器地址": "請輸入Gotify伺服器位址", "请输入Gotify服务器地址,例如: https://gotify.example.com": "請輸入Gotify伺服器位址,例如: https://gotify.example.com", "请输入JSON数组,如 [\"model-a\",\"model-b\"]": "請輸入JSON陣列,如 [\"model-a\",\"model-b\"]", - "请输入URL链接": "請輸入URL連結", "请输入Uptime Kuma地址": "請輸入Uptime Kuma位址", "请输入Uptime Kuma服务地址,如:https://status.example.com": "請輸入Uptime Kuma服務位址,如:https://status.example.com", + "请输入URL链接": "請輸入URL連結", "请输入Webhook地址": "請輸入Webhook位址", "请输入Webhook地址,例如: https://example.com/webhook": "請輸入Webhook位址,例如: https://example.com/webhook", "请输入你的账户名以确认删除!": "請輸入你的帳號名以確認刪除!", @@ -2639,14 +3083,14 @@ "请输入备注(仅管理员可见)": "請輸入備註(僅管理員可見)", "请输入套餐标题": "請輸入訂閱標題", "请输入完整的 JSON 格式密钥内容": "請輸入完整的 JSON 格式密鑰內容", - "请输入完整的URL链接": "請輸入完整的URL連結", "请输入完整的URL,例如:https://api.openai.com/v1/chat/completions": "請輸入完整的URL,例如:https://api.openai.com/v1/chat/completions", + "请输入完整的URL链接": "請輸入完整的URL連結", "请输入容器名称": "請輸入容器名稱", "请输入密码": "請輸入密碼", "请输入密钥": "請輸入密鑰", - "请输入密钥!": "請輸入密鑰!", "请输入密钥,一行一个": "請輸入密鑰,一行一個", "请输入密钥,一行一个,格式:AccessKey|SecretAccessKey|Region": "請輸入密鑰,一行一個,格式:AccessKey|SecretAccessKey|Region", + "请输入密钥!": "請輸入密鑰!", "请输入延长时长": "請輸入延長時長", "请输入总额度": "請輸入總額度", "请输入您的密码": "請輸入您的密碼", @@ -2699,9 +3143,9 @@ "请输入认证器验证码或备用码": "請輸入認證器驗證碼或備用碼", "请输入说明": "請輸入說明", "请输入运行时长": "請輸入運行時長", + "请输入邮箱!": "請輸入信箱!", "请输入邮箱地址": "請輸入信箱位址", "请输入邮箱验证码!": "請輸入信箱驗證碼!", - "请输入邮箱!": "請輸入信箱!", "请输入部署名称": "請輸入部署名稱", "请输入部署名称以完成二次确认": "請輸入部署名稱以完成二次確認", "请输入部署地区,例如:us-central1\n支持使用模型映射格式\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}": "請輸入部署地區,例如:us-central1\n支援使用模型映射格式\n{\n \"default\": \"us-central1\",\n \"claude-3-5-sonnet-20240620\": \"europe-west1\"\n}", @@ -2715,6 +3159,8 @@ "请输入验证码或备用码": "請輸入驗證碼或備用碼", "请输入默认 API 版本,例如:2025-04-01-preview": "請輸入預設 API 版本,例如:2025-04-01-preview", "请选择API地址": "請選擇API位址", + "请选择一条规则进行编辑。": "", + "请选择主模型": "", "请选择产品": "請選擇產品", "请选择你的复制方式": "請選擇你的複製方式", "请选择使用模式": "請選擇使用模式", @@ -2727,6 +3173,7 @@ "请选择多密钥使用策略": "請選擇多密鑰使用策略", "请选择密钥更新模式": "請選擇密鑰更新模式", "请选择密钥格式": "請選擇密鑰格式", + "请选择支付方式": "", "请选择日志记录时间": "請選擇日誌記錄時間", "请选择模型": "請選擇模型", "请选择模型。": "請選擇模型。", @@ -2735,17 +3182,23 @@ "请选择硬件类型": "請選擇硬體類型", "请选择组类型": "請選擇組類型", "请选择至少一个部署位置": "請選擇至少一個部署位置", + "请选择订阅套餐": "", "请选择该令牌支持的模型,留空支持所有模型": "請選擇該令牌支援的模型,留空支援所有模型", "请选择该渠道所支持的模型": "請選擇該管道所支援的模型", "请选择该渠道所支持的模型,留空则不更改": "請選擇該管道所支援的模型,留空則不更改", "请选择过期时间": "請選擇過期時間", "请选择通知方式": "請選擇通知方式", + "调整额度": "調整額度", + "调整额度成功": "調整額度成功", "调用次数": "調用次數", "调用次数分布": "調用次數分佈", "调用次数排行": "調用次數排行", "调用趋势": "調用趨勢", "调试信息": "除錯訊息", "谨慎": "謹慎", + "警告": "警告", + "警告:启用保活后,如果已经写入保活数据后渠道出错,系统无法重试,如果必须开启,推荐设置尽可能大的Ping间隔": "警告:啟用保活後,如果已經寫入保活數據後管道出錯,系統無法重試,如果必須開啟,推薦設定儘可能大的Ping間隔", + "警告:禁用两步验证将永久删除您的验证设置和所有备用码,此操作不可撤销!": "警告:禁用兩步驗證將永久刪除您的驗證設定和所有備用碼,此操作不可撤銷!", "豆包": "豆包", "账单": "帳單", "账户充值": "帳號儲值", @@ -2755,6 +3208,7 @@ "账户管理": "帳號管理", "账户绑定": "帳號綁定", "账户绑定、安全设置和身份验证": "帳號綁定、安全設定和身份驗證", + "账户绑定管理": "", "账户统计": "帳號統計", "货币": "貨幣", "货币单位": "貨幣單位", @@ -2776,17 +3230,23 @@ "跨分组": "跨分組", "跨分组特殊倍率": "跨分組特殊倍率", "跨分组重试": "跨分組重試", + "路径正则": "", + "路径正则(每行一个)": "", "跳转": "跳轉", + "转大写": "", + "转小写": "", "转换": "轉換", "轮询": "輪詢", "轮询模式": "輪詢模式", "轮询模式必须搭配Redis和内存缓存功能使用,否则性能将大幅降低,并且无法实现轮询功能": "輪詢模式必須搭配Redis和記憶體快取功能使用,否則性能將大幅降低,並且無法實現輪詢功能", + "软错误": "", "输入": "輸入", + "输入 {{price}} / 1M tokens": "輸入 {{price}} / 1M tokens", + "输入 $/1M tokens": "", "输入 OIDC 的 Authorization Endpoint": "輸入 OIDC 的 Authorization Endpoint", "输入 OIDC 的 Client ID": "輸入 OIDC 的 Client ID", "输入 OIDC 的 Token Endpoint": "輸入 OIDC 的 Token Endpoint", "输入 OIDC 的 Userinfo Endpoint": "輸入 OIDC 的 Userinfo Endpoint", - "输入 {{price}} / 1M tokens": "輸入 {{price}} / 1M tokens", "输入IP地址后回车,如:8.8.8.8": "輸入IP位址後回車,如:8.8.8.8", "输入JSON对象": "輸入JSON對象", "输入价格": "輸入價格", @@ -2795,6 +3255,7 @@ "输入价格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}": "輸入價格:{{symbol}}{{price}} / 1M tokens{{audioPrice}}", "输入你注册的 LinuxDO OAuth APP 的 ID": "輸入你註冊的 LinuxDO OAuth APP 的 ID", "输入你的账户名{{username}}以确认删除": "輸入你的帳號名{{username}}以確認刪除", + "输入倍率": "", "输入域名后回车": "輸入域名後回車", "输入域名后回车,如:example.com": "輸入域名後回車,如:example.com", "输入基础 URL": "輸入基礎 URL", @@ -2804,6 +3265,7 @@ "输入模型倍率": "輸入模型倍率", "输入模型名称,例如 gpt-4.1": "輸入模型名稱,例如 gpt-4.1", "输入每次价格": "輸入每次價格", + "输入每次调用价格": "", "输入端口后回车,如:80 或 8000-8999": "輸入端口後回車,如:80 或 8000-8999", "输入系统提示词,用户的系统提示词将优先于此设置": "輸入系統提示詞,使用者的系統提示詞將優先於此設定", "输入自定义模型名称": "輸入自訂模型名稱", @@ -2812,19 +3274,21 @@ "输入要添加的邮箱域名": "輸入要添加的信箱域名", "输入认证器应用显示的6位数字验证码": "輸入認證器應用顯示的6位數位驗證碼", "输入邮箱地址": "輸入信箱位址", + "输入金额": "", "输入项目名称,按回车添加": "輸入項目名稱,按回車添加", + "输入额度": "", "输入验证码": "輸入驗證碼", "输入验证码完成设置": "輸入驗證碼完成設定", "输出": "輸出", "输出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}": "輸出 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}}) * {{ratioType}} {{ratio}}", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "輸出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 補全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "輸出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 輸出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "输出价格": "輸出價格", "输出价格 {{symbol}}{{price}} / 1M tokens": "輸出價格 {{symbol}}{{price}} / 1M tokens", "输出价格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (补全倍率: {{completionRatio}})": "輸出價格:{{symbol}}{{price}} * {{completionRatio}} = {{symbol}}{{total}} / 1M tokens (補全倍率: {{completionRatio}})", "输出价格:{{symbol}}{{price}} / 1M tokens": "輸出價格:{{symbol}}{{price}} / 1M tokens", "输出价格:{{symbol}}{{total}} / 1M tokens": "輸出價格:{{symbol}}{{total}} / 1M tokens", "输出倍率 {{completionRatio}}": "輸出倍率 {{completionRatio}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "輸出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 補全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "輸出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 輸出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "边栏设置": "邊欄設定", "过期于": "過期於", "过期时间": "過期時間", @@ -2838,11 +3302,13 @@ "运行时长(小时)": "運行時長(小時)", "返回修改": "返回修改", "返回登录": "返回登錄", + "返回错误": "", "这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "這個介面預設按價格填寫,儲存時會自動換算回後端需要的倍率 JSON。", "这些价格都是可选项,不填也可以。": "這些價格都是可選項,不填也可以。", "这将删除超过 10 分钟未使用的临时缓存文件": "這將刪除超過 10 分鐘未使用的臨時快取檔案", "这是基础金额,实际扣费 = 基础金额 x 系统分组倍率。": "這是基礎金額,實際扣費 = 基礎金額 x 系統分組倍率。", "这是重复键中的最后一个,其值将被使用": "這是重複鍵中的最後一個,其值將被使用", + "这里直接编辑 JSON 对象。适合简单覆盖参数的场景。": "", "进度": "進度", "进行中": "進行中", "进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用": "進行該操作時,可能導致管道訪問錯誤,請僅在資料庫出現問題時使用", @@ -2856,6 +3322,7 @@ "追加到现有密钥": "追加到現有密鑰", "追加模式:将新密钥添加到现有密钥列表末尾": "追加模式:將新密鑰添加到現有密鑰列表末尾", "追加模式:新密钥将添加到现有密钥列表的末尾": "追加模式:新密鑰將添加到現有密鑰列表的末尾", + "追加模板": "", "退出": "退出", "退款": "退款", "适合 MJ / 任务类等按次收费模型。": "適合 MJ / 任務類等按次收費模型。", @@ -2894,7 +3361,11 @@ "选择过期时间(可选,留空为永久)": "選擇過期時間(可選,留空為永久)", "选择部署位置(可多选)": "選擇部署位置(可多選)", "选择预设...": "選擇設定檔...", + "选择预设模板(可选)": "", "透传请求体": "透傳請求體", + "透传请求头": "", + "递归": "", + "递归策略": "", "通义千问": "通義千問", "通用设置": "通用設定", "通知": "通知", @@ -2914,6 +3385,7 @@ "通道 ${name} 测试成功,模型 ${model} 耗时 ${time.toFixed(2)} 秒。": "通道 ${name} 測試成功,模型 ${model} 耗時 ${time.toFixed(2)} 秒。", "通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。": "通道 ${name} 測試成功,耗時 ${time.toFixed(2)} 秒。", "速率限制设置": "速率限制設定", + "逻辑": "", "邀请": "邀請", "邀请人": "邀請人", "邀请人数": "邀請人數", @@ -2933,6 +3405,7 @@ "邮箱域名格式不正确,请输入有效的域名,如 gmail.com": "信箱域名格式不正確,請輸入有效的域名,如 gmail.com", "邮箱域名白名单格式不正确": "信箱域名白名單格式不正確", "邮箱字段": "信箱字段", + "邮箱字段(可选)": "", "邮箱账户绑定成功!": "信箱帳號綁定成功!", "部分保存失败": "部分儲存失敗", "部分保存失败,请重试": "部分儲存失敗,請重試", @@ -2961,10 +3434,13 @@ "配置 SMTP": "設定 SMTP", "配置 Telegram 登录": "設定 Telegram 登錄", "配置 Turnstile": "設定 Turnstile", + "配置 Waffo 充值时可用的支付方式,保存后在充值页面展示给用户。": "", "配置 WeChat Server": "設定 WeChat Server", + "配置:": "配置:", "配置后的效果:": "配置後的效果:", "配置和消息已全部重置": "設定和消息已全部重置", "配置套餐的有效时长": "設定訂閱的有效時長", + "配置如何从用户信息 API 响应中提取用户数据,支持 JSONPath 语法": "", "配置完成后刷新页面即可使用模型部署功能": "設定完成後刷新頁面即可使用模型部署功能", "配置导入成功": "設定導入成功", "配置已导出到下载文件夹": "設定已導出到下載資料夾", @@ -2979,13 +3455,13 @@ "配置自定义 OAuth 提供商,支持 GitHub Enterprise、GitLab、Gitea、Nextcloud、Keycloak、ORY 等兼容 OAuth 2.0 协议的身份提供商": "設定自訂 OAuth 提供商,支援 GitHub Enterprise、GitLab、Gitea、Nextcloud、Keycloak、ORY 等兼容 OAuth 2.0 協議的身份提供商", "配置说明": "設定說明", "配置邮箱域名白名单": "設定信箱域名白名單", - "配置:": "配置:", "重启部署失败": "重啟部署失敗", "重命名部署": "重命名部署", "重复提交": "重複提交", "重复的键名": "重複的鍵名", "重复的键名,此值将被后面的同名键覆盖": "重複的鍵名,此值將被後面的同名鍵覆蓋", "重定向 URL 填": "重定向 URL 填", + "重新上传": "", "重新发送": "重新發送", "重新生成": "重新生成", "重新生成备用码": "重新生成備用碼", @@ -3005,12 +3481,17 @@ "重置配置": "重置設定", "重要提醒": "重要提醒", "重试": "重試", + "重试建议": "", "重试连接": "重試連接", + "金额": "", "钱包管理": "錢包管理", "链接中的{key}将自动替换为sk-xxxx,{address}将自动替换为系统设置的服务器地址,末尾不带/和/v1": "連結中的{key}將自動替換為sk-xxxx,{address}將自動替換為系統設定的伺服器位址,末尾不帶/和/v1", "销毁容器": "銷燬容器", "销毁容器失败": "銷燬容器失敗", "错误": "錯誤", + "错误代码(可选)": "", + "错误消息(必填)": "", + "错误类型(可选)": "", "键为分组名称,值为另一个 JSON 对象,键为分组名称,值为该分组的用户的特殊分组倍率,例如:{\"vip\": {\"default\": 0.5, \"test\": 1}},表示 vip 分组的用户在使用default分组的令牌时倍率为0.5,使用test分组时倍率为1": "鍵為分組名稱,值為另一個 JSON 對象,鍵為分組名稱,值為該分組的使用者的特殊分組倍率,例如:{\"vip\": {\"default\": 0.5, \"test\": 1}},表示 vip 分組的使用者在使用default分組的令牌時倍率為0.5,使用test分組時倍率為1", "键为原状态码,值为要复写的状态码,仅影响本地判断": "鍵為原狀態碼,值為要複寫的狀態碼,僅影響本地判斷", "键为用户分组名称,值为操作映射对象。内层键以\"+:\"开头表示添加指定分组(键值为分组名称,值为描述),以\"-:\"开头表示移除指定分组(键值为分组名称),不带前缀的键直接添加该分组。例如:{\"vip\": {\"+:premium\": \"高级分组\", \"special\": \"特殊分组\", \"-:default\": \"默认分组\"}},表示 vip 分组的用户可以使用 premium 和 special 分组,同时移除 default 分组的访问权限": "鍵為使用者分組名稱,值為操作映射對象。內層鍵以\"+:\"開頭表示添加指定分組(鍵值為分組名稱,值為描述),以\"-:\"開頭表示移除指定分組(鍵值為分組名稱),不帶前綴的鍵直接添加該分組。例如:{\"vip\": {\"+:premium\": \"高級分組\", \"special\": \"特殊分組\", \"-:default\": \"預設分組\"}},表示 vip 分組的使用者可以使用 premium 和 special 分組,同時移除 default 分組的存取權限", @@ -3026,20 +3507,22 @@ "镜像配置": "鏡像設定", "问题标题": "問題標題", "队列中": "隊列中", + "附加条件": "", "降低您账户的安全性": "降低您帳號的安全性", "降级": "降級", "限制周期": "限制週期", "限制周期统一使用上方配置的“限制周期”值。": "限制週期統一使用上方設定的「限制週期」值。", + "限流": "", "限购": "限購", - "随机": "隨機", - "随机模式": "隨機模式", - "随机种子 (留空为随机)": "隨機種子 (留空為隨機)", "隐私政策": "隱私政策", "隐私政策已更新": "隱私政策已更新", "隐私政策更新失败": "隱私政策更新失敗", "隐私设置": "隱私設定", "隐藏操作项": "隱藏操作項", "隐藏调试": "隱藏除錯", + "随机": "隨機", + "随机模式": "隨機模式", + "随机种子 (留空为随机)": "隨機種子 (留空為隨機)", "零一万物": "零一萬物", "需要安全验证": "需要安全驗證", "需要添加的额度(支持负数)": "需要添加的額度(支援負數)", @@ -3057,29 +3540,36 @@ "音频补全价格": "音訊補全價格", "音频补全价格:{{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (音频补全倍率: {{audioCompRatio}})": "音訊補全價格:{{symbol}}{{price}} * {{audioRatio}} * {{audioCompRatio}} = {{symbol}}{{total}} / 1M tokens (音訊補全倍率: {{audioCompRatio}})", "音频补全价格:{{symbol}}{{price}} / 1M tokens": "音訊補全價格:{{symbol}}{{price}} / 1M tokens", + "音频补全倍率": "", "音频补全倍率(仅部分模型支持该计费)": "音訊補全倍率(僅部分模型支援該計費)", + "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "音訊輸入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音訊倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", "音频输入价格": "音訊輸入價格", "音频输入价格:{{symbol}}{{price}} / 1M tokens": "音訊輸入價格:{{symbol}}{{price}} / 1M tokens", + "音频输入倍率": "", "音频输入相关的倍率设置,键为模型名称,值为倍率": "音訊輸入相關的倍率設定,鍵為模型名稱,值為倍率", - "音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "音訊輸入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音訊倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}", - "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "音訊輸出補全相關的倍率設定,鍵為模型名稱,值為倍率", "音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "音訊輸出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音訊倍率 {{audioRatio}} * 音訊補全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}", + "音频输出补全相关的倍率设置,键为模型名称,值为倍率": "音訊輸出補全相關的倍率設定,鍵為模型名稱,值為倍率", "页脚": "頁腳", "页面未找到,请检查您的浏览器地址是否正确": "頁面未找到,請檢查您的瀏覽器位址是否正確", "页面渲染出错,请刷新页面重试": "頁面渲染出錯,請重新整理頁面重試", "顶栏管理": "頂欄管理", + "项": "", "项目": "項目", "项目内容": "項目內容", "项目操作按钮组": "項目操作按鈕組", "预估总费用": "預估總費用", "预估费用仅供参考,实际费用可能略有差异": "預估費用僅供參考,實際費用可能略有差異", "预填组管理": "預填組管理", + "预扣": "", "预览失败": "預覽失敗", "预览效果": "預覽效果", "预览更新": "預覽更新", + "预览模板": "", "预览请求体": "預覽請求體", - "预警阈值必须为正数": "預警閾值必須為正數", "预计结束": "預計結束", + "预计结果": "預計結果", + "预设模板": "", + "预警阈值必须为正数": "預警閾值必須為正數", "频率惩罚,减少重复词汇的出现": "頻率懲罰,減少重複詞彙的出現", "频率限制的周期(分钟)": "頻率限制的週期(分鐘)", "颜色": "顏色", @@ -3116,13 +3606,17 @@ "高危状态码重试风险确认项4": "我自願承擔系統穩定性風險:本人知悉該操作可能導致用戶端嚴重逾時及服務崩潰。若因本人開啟此功能導致請求積壓或服務不可用,後果由本人自行承擔。", "高危状态码重试风险输入不匹配提示": "輸入內容與要求不一致", "高危状态码重试风险输入框占位文案": "請完整輸入上方文字", + "高级": "", "高级套餐,半价优惠": "高級套餐,半價優惠", + "高级文本编辑": "", "高级设置": "進階設定", + "高级选项": "", "高级配置": "進階設定", "黑名单": "黑名單", "默认": "預設", "默认 API 版本": "預設 API 版本", "默认 Responses API 版本,为空则使用上方版本": "預設 Responses API 版本,為空則使用上方版本", + "默认 TTL(秒)": "", "默认为 5m 缓存创建倍率;1h 缓存创建倍率按固定乘法自动计算(当前为 1.6x)": "預設為 5m 快取建立倍率;1h 快取建立倍率按固定乘法自動計算(當前為 1.6x)", "默认使用auto分组": "預設使用 auto 分組", "默认使用系统名称": "預設使用系統名稱", @@ -3133,10 +3627,6 @@ "默认折叠侧边栏": "預設摺疊側邊欄", "默认测试模型": "預設測試模型", "默认用户消息": "你好", - "默认补全倍率": "預設補全倍率", - "(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(當前僅支援易支付接口,預設使用上方伺服器位址作為回調位址!)", - ",当前无生效订阅,将自动使用钱包": ",當前無生效訂閱,將自動使用錢包", - ",时间:": ",時間:", - ",点击更新": ",點擊更新" + "默认补全倍率": "預設補全倍率" } } From 925342622316b7c0113315dd1693bb455d35ab46 Mon Sep 17 00:00:00 2001 From: CaIon Date: Fri, 17 Apr 2026 23:58:45 +0800 Subject: [PATCH 27/46] fix(user): invalidate user and token caches when disabling user When an admin disables/deletes/promotes/demotes a user via ManageUser, explicitly evict the user cache and all of the user's token caches from Redis. This prevents a disabled user from continuing to make successful API requests until the user cache TTL expires, and ensures subsequent requests reload fresh status from the database. --- controller/user.go | 17 +++++++++++++++++ model/token.go | 29 +++++++++++++++++++++++++++++ model/user_cache.go | 6 ++++++ 3 files changed, 52 insertions(+) diff --git a/controller/user.go b/controller/user.go index a12d5c66..0e4786fb 100644 --- a/controller/user.go +++ b/controller/user.go @@ -891,6 +891,11 @@ func ManageUser(c *gin.Context) { }) return } + // 删除用户后,强制清理 Redis 中所有该用户令牌的缓存, + // 避免已缓存的令牌在 TTL 过期前仍能通过 TokenAuth 校验。 + if err := model.InvalidateUserTokensCache(user.Id); err != nil { + common.SysLog(fmt.Sprintf("failed to invalidate tokens cache for user %d: %s", user.Id, err.Error())) + } case "promote": if myRole != common.RoleRootUser { common.ApiErrorI18n(c, i18n.MsgUserAdminCannotPromote) @@ -959,6 +964,18 @@ func ManageUser(c *gin.Context) { common.ApiError(c, err) return } + // 禁用 / 角色调整后,强制失效用户缓存与其全部令牌缓存, + // 避免在 Redis TTL 过期前仍使用旧状态(尤其是禁用后仍可发起请求的问题)。 + // InvalidateUserCache 会让下一次 GetUserCache 从数据库重新加载, + // InvalidateUserTokensCache 则确保令牌侧的缓存也同步刷新。 + if req.Action == "disable" || req.Action == "promote" || req.Action == "demote" { + if err := model.InvalidateUserCache(user.Id); err != nil { + common.SysLog(fmt.Sprintf("failed to invalidate user cache for user %d: %s", user.Id, err.Error())) + } + if err := model.InvalidateUserTokensCache(user.Id); err != nil { + common.SysLog(fmt.Sprintf("failed to invalidate tokens cache for user %d: %s", user.Id, err.Error())) + } + } clearUser := model.User{ Role: user.Role, Status: user.Status, diff --git a/model/token.go b/model/token.go index 8cfcd618..0529e2c7 100644 --- a/model/token.go +++ b/model/token.go @@ -480,3 +480,32 @@ func GetTokenKeysByIds(ids []int, userId int) ([]Token, error) { Find(&tokens).Error return tokens, err } + +// InvalidateUserTokensCache 清理指定用户所有令牌在 Redis 中的缓存, +// 配合 InvalidateUserCache 使用,可在用户被禁用/删除时立即阻断其令牌的请求。 +// 下一次请求将从数据库重新加载令牌及用户状态,从而立即识别出被禁用的用户。 +func InvalidateUserTokensCache(userId int) error { + if !common.RedisEnabled { + return nil + } + if userId <= 0 { + return errors.New("userId 无效") + } + var tokens []Token + if err := DB.Unscoped(). + Select("id", commonKeyCol). + Where("user_id = ?", userId). + Find(&tokens).Error; err != nil { + return err + } + var firstErr error + for _, t := range tokens { + if t.Key == "" { + continue + } + if err := cacheDeleteToken(t.Key); err != nil && firstErr == nil { + firstErr = err + } + } + return firstErr +} diff --git a/model/user_cache.go b/model/user_cache.go index 2ba1f18e..80d0264f 100644 --- a/model/user_cache.go +++ b/model/user_cache.go @@ -57,6 +57,12 @@ func invalidateUserCache(userId int) error { return common.RedisDelKey(getUserCacheKey(userId)) } +// InvalidateUserCache is the exported version of invalidateUserCache. +// 供 controller 等上层包在用户状态变更(如禁用、删除、角色变更)后主动清理缓存。 +func InvalidateUserCache(userId int) error { + return invalidateUserCache(userId) +} + // updateUserCache updates all user cache fields using hash func updateUserCache(user User) error { if !common.RedisEnabled { From b2e62a44eee467a24e4069db3181d8f03fc4c7db Mon Sep 17 00:00:00 2001 From: CaIon Date: Sat, 18 Apr 2026 00:01:03 +0800 Subject: [PATCH 28/46] fix(topup): harden top-up search against DoS and cap user queries to 30 days Apply the same LIKE sanitization used for token search to SearchUserTopUps and SearchAllTopUps (reject %%, cap % count, require >=2 stripped chars, use ESCAPE '!') and bound COUNT with a 10000-row hard limit to avoid unbounded full-table scans. Also restrict user-facing list and search (GetUserTopUps, SearchUserTopUps) to records within the last 30 days via create_time. Admin endpoints (GetAllTopUps, SearchAllTopUps) remain unrestricted. --- model/topup.go | 56 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/model/topup.go b/model/topup.go index 00ad5c49..d1b0c5cb 100644 --- a/model/topup.go +++ b/model/topup.go @@ -110,6 +110,14 @@ func Recharge(referenceId string, customerId string, callerIp string) (err error return nil } +// topUpQueryWindowSeconds 限制充值记录查询的时间窗口(秒)。 +const topUpQueryWindowSeconds int64 = 30 * 24 * 60 * 60 + +// topUpQueryCutoff 返回允许查询的最早 create_time(秒级 Unix 时间戳)。 +func topUpQueryCutoff() int64 { + return common.GetTimestamp() - topUpQueryWindowSeconds +} + func GetUserTopUps(userId int, pageInfo *common.PageInfo) (topups []*TopUp, total int64, err error) { // Start transaction tx := DB.Begin() @@ -122,15 +130,17 @@ func GetUserTopUps(userId int, pageInfo *common.PageInfo) (topups []*TopUp, tota } }() + cutoff := topUpQueryCutoff() + // Get total count within transaction - err = tx.Model(&TopUp{}).Where("user_id = ?", userId).Count(&total).Error + err = tx.Model(&TopUp{}).Where("user_id = ? AND create_time >= ?", userId, cutoff).Count(&total).Error if err != nil { tx.Rollback() return nil, 0, err } // Get paginated topups within same transaction - err = tx.Where("user_id = ?", userId).Order("id desc").Limit(pageInfo.GetPageSize()).Offset(pageInfo.GetStartIdx()).Find(&topups).Error + err = tx.Where("user_id = ? AND create_time >= ?", userId, cutoff).Order("id desc").Limit(pageInfo.GetPageSize()).Offset(pageInfo.GetStartIdx()).Find(&topups).Error if err != nil { tx.Rollback() return nil, 0, err @@ -144,7 +154,7 @@ func GetUserTopUps(userId int, pageInfo *common.PageInfo) (topups []*TopUp, tota return topups, total, nil } -// GetAllTopUps 获取全平台的充值记录(管理员使用) +// GetAllTopUps 获取全平台的充值记录(管理员使用,不限制时间窗口) func GetAllTopUps(pageInfo *common.PageInfo) (topups []*TopUp, total int64, err error) { tx := DB.Begin() if tx.Error != nil { @@ -173,6 +183,10 @@ func GetAllTopUps(pageInfo *common.PageInfo) (topups []*TopUp, total int64, err return topups, total, nil } +// searchTopUpCountHardLimit 搜索充值记录时 COUNT 的安全上限, +// 防止对超大表执行无界 COUNT 触发 DoS。 +const searchTopUpCountHardLimit = 10000 + // SearchUserTopUps 按订单号搜索某用户的充值记录 func SearchUserTopUps(userId int, keyword string, pageInfo *common.PageInfo) (topups []*TopUp, total int64, err error) { tx := DB.Begin() @@ -185,20 +199,26 @@ func SearchUserTopUps(userId int, keyword string, pageInfo *common.PageInfo) (to } }() - query := tx.Model(&TopUp{}).Where("user_id = ?", userId) + query := tx.Model(&TopUp{}).Where("user_id = ? AND create_time >= ?", userId, topUpQueryCutoff()) if keyword != "" { - like := "%%" + keyword + "%%" - query = query.Where("trade_no LIKE ?", like) + pattern, perr := sanitizeLikePattern(keyword) + if perr != nil { + tx.Rollback() + return nil, 0, perr + } + query = query.Where("trade_no LIKE ? ESCAPE '!'", pattern) } - if err = query.Count(&total).Error; err != nil { + if err = query.Limit(searchTopUpCountHardLimit).Count(&total).Error; err != nil { tx.Rollback() - return nil, 0, err + common.SysError("failed to count search topups: " + err.Error()) + return nil, 0, errors.New("搜索充值记录失败") } if err = query.Order("id desc").Limit(pageInfo.GetPageSize()).Offset(pageInfo.GetStartIdx()).Find(&topups).Error; err != nil { tx.Rollback() - return nil, 0, err + common.SysError("failed to search topups: " + err.Error()) + return nil, 0, errors.New("搜索充值记录失败") } if err = tx.Commit().Error; err != nil { @@ -207,7 +227,7 @@ func SearchUserTopUps(userId int, keyword string, pageInfo *common.PageInfo) (to return topups, total, nil } -// SearchAllTopUps 按订单号搜索全平台充值记录(管理员使用) +// SearchAllTopUps 按订单号搜索全平台充值记录(管理员使用,不限制时间窗口) func SearchAllTopUps(keyword string, pageInfo *common.PageInfo) (topups []*TopUp, total int64, err error) { tx := DB.Begin() if tx.Error != nil { @@ -221,18 +241,24 @@ func SearchAllTopUps(keyword string, pageInfo *common.PageInfo) (topups []*TopUp query := tx.Model(&TopUp{}) if keyword != "" { - like := "%%" + keyword + "%%" - query = query.Where("trade_no LIKE ?", like) + pattern, perr := sanitizeLikePattern(keyword) + if perr != nil { + tx.Rollback() + return nil, 0, perr + } + query = query.Where("trade_no LIKE ? ESCAPE '!'", pattern) } - if err = query.Count(&total).Error; err != nil { + if err = query.Limit(searchTopUpCountHardLimit).Count(&total).Error; err != nil { tx.Rollback() - return nil, 0, err + common.SysError("failed to count search topups: " + err.Error()) + return nil, 0, errors.New("搜索充值记录失败") } if err = query.Order("id desc").Limit(pageInfo.GetPageSize()).Offset(pageInfo.GetStartIdx()).Find(&topups).Error; err != nil { tx.Rollback() - return nil, 0, err + common.SysError("failed to search topups: " + err.Error()) + return nil, 0, errors.New("搜索充值记录失败") } if err = tx.Commit().Error; err != nil { From c31343ac76ec5afe337b50f5582a1fbc0735d097 Mon Sep 17 00:00:00 2001 From: CaIon Date: Sat, 18 Apr 2026 00:16:52 +0800 Subject: [PATCH 29/46] fix(log): hide admin identity in user-visible management logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Admin username/ID was embedded directly into the log Content for quota changes and forced 2FA disable, leaking the operator's identity to the target user via their own usage log page. Move operator info into Other.admin_info so formatUserLogs strips it for non-admin viewers, and render it in the expand panel only for admins as "操作管理员". Closes #4301 --- controller/twofa.go | 12 ++++++--- controller/user.go | 17 ++++++++----- model/log.go | 24 ++++++++++++++++++ web/src/hooks/usage-logs/useUsageLogsData.jsx | 25 +++++++++++++++++++ web/src/i18n/locales/en.json | 1 + web/src/i18n/locales/fr.json | 1 + web/src/i18n/locales/ja.json | 1 + web/src/i18n/locales/ru.json | 1 + web/src/i18n/locales/vi.json | 1 + web/src/i18n/locales/zh-CN.json | 1 + web/src/i18n/locales/zh-TW.json | 1 + 11 files changed, 75 insertions(+), 10 deletions(-) diff --git a/controller/twofa.go b/controller/twofa.go index 556c07e9..123c74e2 100644 --- a/controller/twofa.go +++ b/controller/twofa.go @@ -2,7 +2,6 @@ package controller import ( "errors" - "fmt" "net/http" "strconv" @@ -542,10 +541,15 @@ func AdminDisable2FA(c *gin.Context) { return } - // 记录操作日志 + // 记录操作日志:管理员身份通过 admin_info 传递,避免在非管理员可见的日志内容中泄露。 adminId := c.GetInt("id") - model.RecordLog(userId, model.LogTypeManage, - fmt.Sprintf("管理员(ID:%d)强制禁用了用户的两步验证", adminId)) + adminName := c.GetString("username") + adminInfo := map[string]interface{}{ + "admin_id": adminId, + "admin_username": adminName, + } + model.RecordLogWithAdminInfo(userId, model.LogTypeManage, + "管理员强制禁用了用户的两步验证", adminInfo) c.JSON(http.StatusOK, gin.H{ "success": true, diff --git a/controller/user.go b/controller/user.go index 0e4786fb..d6becdd8 100644 --- a/controller/user.go +++ b/controller/user.go @@ -918,6 +918,11 @@ func ManageUser(c *gin.Context) { user.Role = common.RoleCommonUser case "add_quota": adminName := c.GetString("username") + adminId := c.GetInt("id") + adminInfo := map[string]interface{}{ + "admin_id": adminId, + "admin_username": adminName, + } switch req.Mode { case "add": if req.Value <= 0 { @@ -928,8 +933,8 @@ func ManageUser(c *gin.Context) { common.ApiError(c, err) return } - model.RecordLog(user.Id, model.LogTypeManage, - fmt.Sprintf("管理员(%s)增加用户额度 %s", adminName, logger.LogQuota(req.Value))) + model.RecordLogWithAdminInfo(user.Id, model.LogTypeManage, + fmt.Sprintf("管理员增加用户额度 %s", logger.LogQuota(req.Value)), adminInfo) case "subtract": if req.Value <= 0 { common.ApiErrorI18n(c, i18n.MsgUserQuotaChangeZero) @@ -939,16 +944,16 @@ func ManageUser(c *gin.Context) { common.ApiError(c, err) return } - model.RecordLog(user.Id, model.LogTypeManage, - fmt.Sprintf("管理员(%s)减少用户额度 %s", adminName, logger.LogQuota(req.Value))) + model.RecordLogWithAdminInfo(user.Id, model.LogTypeManage, + fmt.Sprintf("管理员减少用户额度 %s", logger.LogQuota(req.Value)), adminInfo) case "override": oldQuota := user.Quota if err := model.DB.Model(&model.User{}).Where("id = ?", user.Id).Update("quota", req.Value).Error; err != nil { common.ApiError(c, err) return } - model.RecordLog(user.Id, model.LogTypeManage, - fmt.Sprintf("管理员(%s)覆盖用户额度从 %s 为 %s", adminName, logger.LogQuota(oldQuota), logger.LogQuota(req.Value))) + model.RecordLogWithAdminInfo(user.Id, model.LogTypeManage, + fmt.Sprintf("管理员覆盖用户额度从 %s 为 %s", logger.LogQuota(oldQuota), logger.LogQuota(req.Value)), adminInfo) default: common.ApiErrorI18n(c, i18n.MsgInvalidParams) return diff --git a/model/log.go b/model/log.go index f9d15985..c7242688 100644 --- a/model/log.go +++ b/model/log.go @@ -90,6 +90,30 @@ func RecordLog(userId int, logType int, content string) { } } +// RecordLogWithAdminInfo 记录操作日志,并将管理员相关信息存入 Other.admin_info, +func RecordLogWithAdminInfo(userId int, logType int, content string, adminInfo map[string]interface{}) { + if logType == LogTypeConsume && !common.LogConsumeEnabled { + return + } + username, _ := GetUsernameById(userId, false) + log := &Log{ + UserId: userId, + Username: username, + CreatedAt: common.GetTimestamp(), + Type: logType, + Content: content, + } + if len(adminInfo) > 0 { + other := map[string]interface{}{ + "admin_info": adminInfo, + } + log.Other = common.MapToJsonStr(other) + } + if err := LOG_DB.Create(log).Error; err != nil { + common.SysLog("failed to record log: " + err.Error()) + } +} + func RecordTopupLog(userId int, content string, callerIp string, paymentMethod string, callbackPaymentMethod string) { username, _ := GetUsernameById(userId, false) adminInfo := map[string]interface{}{ diff --git a/web/src/hooks/usage-logs/useUsageLogsData.jsx b/web/src/hooks/usage-logs/useUsageLogsData.jsx index 2c3c131a..47a3f72f 100644 --- a/web/src/hooks/usage-logs/useUsageLogsData.jsx +++ b/web/src/hooks/usage-logs/useUsageLogsData.jsx @@ -746,6 +746,31 @@ export const useLogsData = () => { }); } } + if (isAdminUser && logs[i].type === 3 && other?.admin_info) { + const adminInfo = other.admin_info; + const hasUsername = + adminInfo.admin_username !== undefined && + adminInfo.admin_username !== null && + adminInfo.admin_username !== ''; + const hasId = + adminInfo.admin_id !== undefined && + adminInfo.admin_id !== null && + adminInfo.admin_id !== ''; + if (hasUsername || hasId) { + let operatorValue = ''; + if (hasUsername && hasId) { + operatorValue = `${adminInfo.admin_username} (ID: ${adminInfo.admin_id})`; + } else if (hasUsername) { + operatorValue = String(adminInfo.admin_username); + } else { + operatorValue = `ID: ${adminInfo.admin_id}`; + } + expandDataLocal.push({ + key: t('操作管理员'), + value: operatorValue, + }); + } + } expandDatesLocal[logs[i].key] = expandDataLocal; } diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index cd7a1bbd..58812988 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1695,6 +1695,7 @@ "操作成功完成!": "Operation completed successfully!", "操作暂时被禁用": "Operation temporarily disabled", "操作确认": "Operation confirmation", + "操作管理员": "Operator Admin", "操作类型": "Operation Type", "操练场": "Playground", "操练场和聊天功能": "Playground and chat functions", diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json index ef92f79d..ff9840ef 100644 --- a/web/src/i18n/locales/fr.json +++ b/web/src/i18n/locales/fr.json @@ -1695,6 +1695,7 @@ "操作失败,请重试": "L'opération a échoué, veuillez réessayer", "操作成功完成!": "Opération terminée avec succès !", "操作暂时被禁用": "Opération temporairement désactivée", + "操作管理员": "Administrateur opérateur", "操作类型": "Type d'opération", "操练场": "Terrain de jeu", "操练场和聊天功能": "Terrain de jeu et fonctions de discussion", diff --git a/web/src/i18n/locales/ja.json b/web/src/i18n/locales/ja.json index 178b38a7..16edae7c 100644 --- a/web/src/i18n/locales/ja.json +++ b/web/src/i18n/locales/ja.json @@ -1666,6 +1666,7 @@ "操作失败,请重试": "操作に失敗しました。再試行してください。", "操作成功完成!": "操作が正常に完了しました", "操作暂时被禁用": "この操作は一時的に無効にされています", + "操作管理员": "操作管理者", "操作类型": "操作タイプ", "操练场": "Playground", "操练场和聊天功能": "プレイグラウンドとチャット機能", diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index 6ec34b87..80f657c5 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -1713,6 +1713,7 @@ "操作失败,请重试": "Операция не удалась, попробуйте еще раз", "操作成功完成!": "Операция успешно завершена!", "操作暂时被禁用": "Операция временно отключена", + "操作管理员": "Администратор операции", "操作类型": "Тип операции", "操练场": "Тренировочная площадка", "操练场和聊天功能": "Тренировочная площадка и чат-функции", diff --git a/web/src/i18n/locales/vi.json b/web/src/i18n/locales/vi.json index 42d33c18..c2d07978 100644 --- a/web/src/i18n/locales/vi.json +++ b/web/src/i18n/locales/vi.json @@ -1667,6 +1667,7 @@ "操作失败,请重试": "Thao tác thất bại, vui lòng thử lại", "操作成功完成!": "Thao tác hoàn tất thành công!", "操作暂时被禁用": "Thao tác tạm thời bị vô hiệu hóa", + "操作管理员": "Quản trị viên thao tác", "操作类型": "Loại thao tác", "操练场": "Sân chơi", "操练场和聊天功能": "Chức năng sân chơi và trò chuyện", diff --git a/web/src/i18n/locales/zh-CN.json b/web/src/i18n/locales/zh-CN.json index 155e877f..19b32e04 100644 --- a/web/src/i18n/locales/zh-CN.json +++ b/web/src/i18n/locales/zh-CN.json @@ -1655,6 +1655,7 @@ "操作成功完成!": "操作成功完成!", "操作暂时被禁用": "操作暂时被禁用", "操作确认": "操作确认", + "操作管理员": "操作管理员", "操作类型": "操作类型", "操练场": "操练场", "操练场和聊天功能": "操练场和聊天功能", diff --git a/web/src/i18n/locales/zh-TW.json b/web/src/i18n/locales/zh-TW.json index 6ff17075..c25eb6ff 100644 --- a/web/src/i18n/locales/zh-TW.json +++ b/web/src/i18n/locales/zh-TW.json @@ -1666,6 +1666,7 @@ "操作成功完成!": "操作成功完成!", "操作暂时被禁用": "操作暫時被禁用", "操作确认": "操作確認", + "操作管理员": "操作管理員", "操作类型": "", "操练场": "操練場", "操练场和聊天功能": "操練場和聊天功能", From 6ff8c7ab033dbe2c9a685d423a49e56f4f1eec43 Mon Sep 17 00:00:00 2001 From: CaIon Date: Sat, 18 Apr 2026 00:36:05 +0800 Subject: [PATCH 30/46] fix(topup-log): keep row expandable and warn admins on legacy logs Top-up logs written by pre-upgrade instances have no admin_info, which made the expanded row empty and the row un-expandable. For admins, always emit an entry: either the audit fields from admin_info when present, or a warning prompting the operator to upgrade the instance so audit fields (server IP, callback IP, payment method, system version) are recorded. --- web/src/hooks/usage-logs/useUsageLogsData.jsx | 71 +++++++++++-------- web/src/i18n/locales/en.json | 2 + web/src/i18n/locales/fr.json | 2 + web/src/i18n/locales/ja.json | 2 + web/src/i18n/locales/ru.json | 2 + web/src/i18n/locales/vi.json | 2 + web/src/i18n/locales/zh-CN.json | 2 + web/src/i18n/locales/zh-TW.json | 2 + 8 files changed, 56 insertions(+), 29 deletions(-) diff --git a/web/src/hooks/usage-logs/useUsageLogsData.jsx b/web/src/hooks/usage-logs/useUsageLogsData.jsx index 47a3f72f..d33b633f 100644 --- a/web/src/hooks/usage-logs/useUsageLogsData.jsx +++ b/web/src/hooks/usage-logs/useUsageLogsData.jsx @@ -713,36 +713,49 @@ export const useLogsData = () => { value: localCountMode, }); } - if (isAdminUser && logs[i].type === 1 && other?.admin_info) { - const adminInfo = other.admin_info; - if (adminInfo.payment_method) { + if (isAdminUser && logs[i].type === 1) { + const adminInfo = other?.admin_info; + if (adminInfo) { + if (adminInfo.payment_method) { + expandDataLocal.push({ + key: t('订单支付方式'), + value: adminInfo.payment_method, + }); + } + if (adminInfo.callback_payment_method) { + expandDataLocal.push({ + key: t('回调支付方式'), + value: adminInfo.callback_payment_method, + }); + } + if (adminInfo.caller_ip) { + expandDataLocal.push({ + key: t('回调调用者IP'), + value: adminInfo.caller_ip, + }); + } + if (adminInfo.server_ip) { + expandDataLocal.push({ + key: t('服务器IP'), + value: adminInfo.server_ip, + }); + } + if (adminInfo.version) { + expandDataLocal.push({ + key: t('系统版本'), + value: adminInfo.version, + }); + } + } else { expandDataLocal.push({ - key: t('订单支付方式'), - value: adminInfo.payment_method, - }); - } - if (adminInfo.callback_payment_method) { - expandDataLocal.push({ - key: t('回调支付方式'), - value: adminInfo.callback_payment_method, - }); - } - if (adminInfo.caller_ip) { - expandDataLocal.push({ - key: t('回调调用者IP'), - value: adminInfo.caller_ip, - }); - } - if (adminInfo.server_ip) { - expandDataLocal.push({ - key: t('服务器IP'), - value: adminInfo.server_ip, - }); - } - if (adminInfo.version) { - expandDataLocal.push({ - key: t('系统版本'), - value: adminInfo.version, + key: t('审计信息'), + value: ( + + {t( + '该记录由旧版本实例写入,缺少审计信息,建议将实例升级至最新版本以便记录服务器IP、回调IP、支付方式与系统版本等审计字段。', + )} + + ), }); } } diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 58812988..151f3ce8 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1230,6 +1230,7 @@ "实际模型": "Actual model", "实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "Actual charge: {{symbol}}{{total}} (group pricing adjustment included)", "实际请求体": "Actual request body", + "审计信息": "Audit Info", "容器": "Container", "容器ID": "Container ID", "容器创建失败: ": "Container creation failed: ", @@ -2981,6 +2982,7 @@ "该规则未设置参数覆盖模板": "This rule has no parameter override template set", "该规则的缓存保留时长;0 表示使用默认 TTL:": "Cache retention duration for this rule; 0 means using default TTL: ", "该记录不包含可用的 token 统计口径。": "This record does not contain available token statistics.", + "该记录由旧版本实例写入,缺少审计信息,建议将实例升级至最新版本以便记录服务器IP、回调IP、支付方式与系统版本等审计字段。": "This record was written by an older instance and lacks audit info. Please upgrade the instance to the latest version so that server IP, callback IP, payment method, and system version audit fields are recorded.", "详情": "Details", "详见「特殊倍率」和「可用分组」标签页。": "See \"Special Ratios\" and \"Usable Groups\" tabs for details.", "语言偏好": "Language Preference", diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json index ff9840ef..6ffb8563 100644 --- a/web/src/i18n/locales/fr.json +++ b/web/src/i18n/locales/fr.json @@ -1226,6 +1226,7 @@ "实际模型": "Modèle réel", "实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "Montant facturé réel : {{symbol}}{{total}} (ajustement tarifaire de groupe inclus)", "实际请求体": "Corps de requête réel", + "审计信息": "Informations d'audit", "容器": "Container", "容器ID": "Container ID", "容器创建失败: ": "Container creation failed: ", @@ -2967,6 +2968,7 @@ "该规则未设置参数覆盖模板": "Cette règle n'a pas de modèle de remplacement de paramètres défini", "该规则的缓存保留时长;0 表示使用默认 TTL:": "Durée de rétention du cache pour cette règle ; 0 signifie utiliser le TTL par défaut : ", "该记录不包含可用的 token 统计口径。": "Cet enregistrement ne contient pas de statistiques de tokens disponibles.", + "该记录由旧版本实例写入,缺少审计信息,建议将实例升级至最新版本以便记录服务器IP、回调IP、支付方式与系统版本等审计字段。": "Cet enregistrement a été écrit par une ancienne version de l'instance et ne contient pas d'informations d'audit. Veuillez mettre à jour l'instance vers la dernière version afin d'enregistrer les champs d'audit tels que l'IP du serveur, l'IP de rappel, le mode de paiement et la version du système.", "详情": "Détails", "详见「特殊倍率」和「可用分组」标签页。": "See \"Special Ratios\" and \"Usable Groups\" tabs for details.", "语言偏好": "Préférence linguistique", diff --git a/web/src/i18n/locales/ja.json b/web/src/i18n/locales/ja.json index 16edae7c..40929a57 100644 --- a/web/src/i18n/locales/ja.json +++ b/web/src/i18n/locales/ja.json @@ -1213,6 +1213,7 @@ "实际模型": "アップストリームモデル", "实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "実際の請求額:{{symbol}}{{total}}(グループ価格調整込み)", "实际请求体": "実際のリクエストボディ", + "审计信息": "監査情報", "容器": "Container", "容器ID": "Container ID", "容器创建失败: ": "Container creation failed: ", @@ -2936,6 +2937,7 @@ "该规则未设置参数覆盖模板": "このルールにはパラメータオーバーライドテンプレートが設定されていません", "该规则的缓存保留时长;0 表示使用默认 TTL:": "このルールのキャッシュ保持期間。0はデフォルトTTLを使用:", "该记录不包含可用的 token 统计口径。": "このレコードには利用可能なトークン統計がありません。", + "该记录由旧版本实例写入,缺少审计信息,建议将实例升级至最新版本以便记录服务器IP、回调IP、支付方式与系统版本等审计字段。": "このレコードは古いバージョンのインスタンスによって書き込まれており、監査情報が欠落しています。サーバーIP、コールバックIP、支払い方法、システムバージョンなどの監査フィールドを記録するために、インスタンスを最新バージョンにアップグレードすることをお勧めします。", "详情": "詳細", "详见「特殊倍率」和「可用分组」标签页。": "詳しくは「特殊レート」と「利用可能グループ」タブをご覧ください。", "语言偏好": "言語設定", diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index 80f657c5..7e2d693c 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -1234,6 +1234,7 @@ "实际模型": "Фактическая модель", "实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "Фактическое списание: {{symbol}}{{total}} (включая групповую ценовую корректировку)", "实际请求体": "Фактическое тело запроса", + "审计信息": "Информация об аудите", "容器": "Container", "容器ID": "Container ID", "容器创建失败: ": "Container creation failed: ", @@ -2987,6 +2988,7 @@ "该规则未设置参数覆盖模板": "У этого правила не задан шаблон переопределения параметров", "该规则的缓存保留时长;0 表示使用默认 TTL:": "Время хранения кэша для этого правила; 0 — использовать TTL по умолчанию: ", "该记录不包含可用的 token 统计口径。": "Эта запись не содержит доступной статистики токенов.", + "该记录由旧版本实例写入,缺少审计信息,建议将实例升级至最新版本以便记录服务器IP、回调IP、支付方式与系统版本等审计字段。": "Эта запись была создана более старой версией экземпляра и не содержит данных аудита. Рекомендуется обновить экземпляр до последней версии, чтобы фиксировать поля аудита: IP сервера, IP callback, способ оплаты и версию системы.", "详情": "Подробности", "详见「特殊倍率」和「可用分组」标签页。": "See \"Special Ratios\" and \"Usable Groups\" tabs for details.", "语言偏好": "Языковые настройки", diff --git a/web/src/i18n/locales/vi.json b/web/src/i18n/locales/vi.json index c2d07978..357c39c9 100644 --- a/web/src/i18n/locales/vi.json +++ b/web/src/i18n/locales/vi.json @@ -1214,6 +1214,7 @@ "实际模型": "Mô hình thực tế", "实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "Khoản phí thực tế: {{symbol}}{{total}} (đã bao gồm điều chỉnh giá theo nhóm)", "实际请求体": "Thân yêu cầu thực tế", + "审计信息": "Thông tin kiểm toán", "容器": "Container", "容器ID": "Container ID", "容器创建失败: ": "Container creation failed: ", @@ -3297,6 +3298,7 @@ "该规则未设置参数覆盖模板": "Quy tắc này chưa thiết lập mẫu ghi đè tham số", "该规则的缓存保留时长;0 表示使用默认 TTL:": "Thời gian lưu bộ nhớ đệm cho quy tắc này; 0 nghĩa là sử dụng TTL mặc định: ", "该记录不包含可用的 token 统计口径。": "Bản ghi này không chứa thống kê token khả dụng.", + "该记录由旧版本实例写入,缺少审计信息,建议将实例升级至最新版本以便记录服务器IP、回调IP、支付方式与系统版本等审计字段。": "Bản ghi này được ghi bởi phiên bản cũ của instance và thiếu thông tin kiểm toán. Khuyến nghị nâng cấp instance lên phiên bản mới nhất để ghi lại các trường kiểm toán như IP máy chủ, IP callback, phương thức thanh toán và phiên bản hệ thống.", "详情": "Chi tiết", "详细信息": "Thông tin chi tiết", "详见「特殊倍率」和「可用分组」标签页。": "See \"Special Ratios\" and \"Usable Groups\" tabs for details.", diff --git a/web/src/i18n/locales/zh-CN.json b/web/src/i18n/locales/zh-CN.json index 19b32e04..50f45b82 100644 --- a/web/src/i18n/locales/zh-CN.json +++ b/web/src/i18n/locales/zh-CN.json @@ -1202,6 +1202,7 @@ "实际模型": "实际模型", "实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)", "实际请求体": "实际请求体", + "审计信息": "审计信息", "容器": "容器", "容器ID": "容器ID", "容器创建失败: ": "容器创建失败: ", @@ -2935,6 +2936,7 @@ "该规则未设置参数覆盖模板": "该规则未设置参数覆盖模板", "该规则的缓存保留时长;0 表示使用默认 TTL:": "该规则的缓存保留时长;0 表示使用默认 TTL:", "该记录不包含可用的 token 统计口径。": "该记录不包含可用的 token 统计口径。", + "该记录由旧版本实例写入,缺少审计信息,建议将实例升级至最新版本以便记录服务器IP、回调IP、支付方式与系统版本等审计字段。": "该记录由旧版本实例写入,缺少审计信息,建议将实例升级至最新版本以便记录服务器IP、回调IP、支付方式与系统版本等审计字段。", "详情": "详情", "详见「特殊倍率」和「可用分组」标签页。": "详见「特殊倍率」和「可用分组」标签页。", "语言偏好": "语言偏好", diff --git a/web/src/i18n/locales/zh-TW.json b/web/src/i18n/locales/zh-TW.json index c25eb6ff..db2a57b6 100644 --- a/web/src/i18n/locales/zh-TW.json +++ b/web/src/i18n/locales/zh-TW.json @@ -1212,6 +1212,7 @@ "实际模型": "實際模型", "实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "實際結算金額:{{symbol}}{{total}}(已包含分組價格調整)", "实际请求体": "實際請求體", + "审计信息": "審計資訊", "容器": "容器", "容器ID": "容器ID", "容器创建失败: ": "容器建立失敗: ", @@ -2947,6 +2948,7 @@ "该规则未设置参数覆盖模板": "", "该规则的缓存保留时长;0 表示使用默认 TTL:": "", "该记录不包含可用的 token 统计口径。": "", + "该记录由旧版本实例写入,缺少审计信息,建议将实例升级至最新版本以便记录服务器IP、回调IP、支付方式与系统版本等审计字段。": "此記錄由舊版本執行個體寫入,缺少審計資訊,建議將執行個體升級至最新版本,以便記錄伺服器IP、回調IP、支付方式與系統版本等審計欄位。", "详情": "詳情", "详见「特殊倍率」和「可用分组」标签页。": "詳見「特殊倍率」和「可用分組」標籤頁。", "语言偏好": "語言偏好", From 209645e26b4809314efacc039aea55db7f449a00 Mon Sep 17 00:00:00 2001 From: CaIon Date: Sat, 18 Apr 2026 00:51:04 +0800 Subject: [PATCH 31/46] feat(topup-log): add NODE_NAME env var for audit logs Introduce NODE_NAME environment variable to identify node identity in top-up audit logs, improving readability over auto-detected container internal IPs in Docker/K8s deployments. Surface node_name in admin expanded log rows and add it as a commented example to docker-compose.yml. --- common/constants.go | 4 ++++ common/init.go | 1 + docker-compose.yml | 1 + model/log.go | 1 + web/src/hooks/usage-logs/useUsageLogsData.jsx | 6 ++++++ web/src/i18n/locales/en.json | 1 + web/src/i18n/locales/fr.json | 1 + web/src/i18n/locales/ja.json | 1 + web/src/i18n/locales/ru.json | 1 + web/src/i18n/locales/vi.json | 1 + web/src/i18n/locales/zh-CN.json | 1 + web/src/i18n/locales/zh-TW.json | 1 + 12 files changed, 20 insertions(+) diff --git a/common/constants.go b/common/constants.go index 6caa7f5c..274c514f 100644 --- a/common/constants.go +++ b/common/constants.go @@ -116,6 +116,10 @@ var RetryTimes = 0 var IsMasterNode bool +// NodeName 节点名称,从 NODE_NAME 环境变量读取; +// 用于审计日志中标识节点身份,在容器/K8s 部署时比自动探测到的容器内网 IP 更具可读性。 +var NodeName = "" + var requestInterval int var RequestInterval time.Duration diff --git a/common/init.go b/common/init.go index e9cfc98b..35b4c6be 100644 --- a/common/init.go +++ b/common/init.go @@ -82,6 +82,7 @@ func InitEnv() { DebugEnabled = os.Getenv("DEBUG") == "true" MemoryCacheEnabled = os.Getenv("MEMORY_CACHE_ENABLED") == "true" IsMasterNode = os.Getenv("NODE_TYPE") != "slave" + NodeName = os.Getenv("NODE_NAME") TLSInsecureSkipVerify = GetEnvOrDefaultBool("TLS_INSECURE_SKIP_VERIFY", false) if TLSInsecureSkipVerify { if tr, ok := http.DefaultTransport.(*http.Transport); ok && tr != nil { diff --git a/docker-compose.yml b/docker-compose.yml index 3c56faf3..7a887b60 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,6 +32,7 @@ services: - TZ=Asia/Shanghai - ERROR_LOG_ENABLED=true # 是否启用错误日志记录 (Whether to enable error log recording) - BATCH_UPDATE_ENABLED=true # 是否启用批量更新 (Whether to enable batch update) + - NODE_NAME=new-api-node-1 # 节点名称,用于审计日志中标识节点身份;多节点/容器部署时建议设置 (Node name used in audit logs; recommended when running multiple instances or in containers) # - STREAMING_TIMEOUT=300 # 流模式无响应超时时间,单位秒,默认120秒,如果出现空补全可以尝试改为更大值 (Streaming timeout in seconds, default is 120s. Increase if experiencing empty completions) # - SESSION_SECRET=random_string # 多机部署时设置,必须修改这个随机字符串!! (multi-node deployment, set this to a random string!!!!!!!) # - SYNC_FREQUENCY=60 # Uncomment if regular database syncing is needed diff --git a/model/log.go b/model/log.go index c7242688..9203ff28 100644 --- a/model/log.go +++ b/model/log.go @@ -118,6 +118,7 @@ func RecordTopupLog(userId int, content string, callerIp string, paymentMethod s username, _ := GetUsernameById(userId, false) adminInfo := map[string]interface{}{ "server_ip": common.GetIp(), + "node_name": common.NodeName, "caller_ip": callerIp, "payment_method": paymentMethod, "callback_payment_method": callbackPaymentMethod, diff --git a/web/src/hooks/usage-logs/useUsageLogsData.jsx b/web/src/hooks/usage-logs/useUsageLogsData.jsx index d33b633f..fcb7e39f 100644 --- a/web/src/hooks/usage-logs/useUsageLogsData.jsx +++ b/web/src/hooks/usage-logs/useUsageLogsData.jsx @@ -740,6 +740,12 @@ export const useLogsData = () => { value: adminInfo.server_ip, }); } + if (adminInfo.node_name) { + expandDataLocal.push({ + key: t('节点名称'), + value: adminInfo.node_name, + }); + } if (adminInfo.version) { expandDataLocal.push({ key: t('系统版本'), diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 151f3ce8..495fba96 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1957,6 +1957,7 @@ "服务可用性": "Service Status", "服务商": "Service Provider", "服务器IP": "Server IP", + "节点名称": "Node Name", "服务器地址": "Server Address", "服务器日志功能未启用(未配置日志目录)": "Server logging is not enabled (log directory not configured)", "服务器日志管理": "Server Log Management", diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json index 6ffb8563..bdd77c26 100644 --- a/web/src/i18n/locales/fr.json +++ b/web/src/i18n/locales/fr.json @@ -1950,6 +1950,7 @@ "服务可用性": "État du service", "服务商": "Service Provider", "服务器IP": "IP du serveur", + "节点名称": "Nom du nœud", "服务器地址": "Adresse du serveur", "服务器日志功能未启用(未配置日志目录)": "La journalisation du serveur n'est pas activée (répertoire de journaux non configuré)", "服务器日志管理": "Gestion des journaux du serveur", diff --git a/web/src/i18n/locales/ja.json b/web/src/i18n/locales/ja.json index 40929a57..e6721e36 100644 --- a/web/src/i18n/locales/ja.json +++ b/web/src/i18n/locales/ja.json @@ -1921,6 +1921,7 @@ "服务可用性": "サービスの可用性", "服务商": "Service Provider", "服务器IP": "サーバーIP", + "节点名称": "ノード名", "服务器地址": "サーバーURL", "服务器日志功能未启用(未配置日志目录)": "サーバーログ機能が有効になっていません(ログディレクトリが未設定)", "服务器日志管理": "サーバーログ管理", diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index 7e2d693c..333057e1 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -1968,6 +1968,7 @@ "服务可用性": "Доступность сервиса", "服务商": "Service Provider", "服务器IP": "IP сервера", + "节点名称": "Имя узла", "服务器地址": "Адрес сервера", "服务器日志功能未启用(未配置日志目录)": "Ведение журнала сервера не включено (каталог журналов не настроен)", "服务器日志管理": "Управление журналами сервера", diff --git a/web/src/i18n/locales/vi.json b/web/src/i18n/locales/vi.json index 357c39c9..a35ec454 100644 --- a/web/src/i18n/locales/vi.json +++ b/web/src/i18n/locales/vi.json @@ -1922,6 +1922,7 @@ "服务可用性": "Trạng thái dịch vụ", "服务商": "Service Provider", "服务器IP": "IP máy chủ", + "节点名称": "Tên nút", "服务器地址": "Địa chỉ máy chủ", "服务器日志功能未启用(未配置日志目录)": "Ghi nhật ký máy chủ chưa được bật (chưa cấu hình thư mục nhật ký)", "服务器日志管理": "Quản lý nhật ký máy chủ", diff --git a/web/src/i18n/locales/zh-CN.json b/web/src/i18n/locales/zh-CN.json index 50f45b82..5a7a7185 100644 --- a/web/src/i18n/locales/zh-CN.json +++ b/web/src/i18n/locales/zh-CN.json @@ -1915,6 +1915,7 @@ "服务可用性": "服务可用性", "服务商": "服务商", "服务器IP": "服务器IP", + "节点名称": "节点名称", "服务器地址": "服务器地址", "服务器日志功能未启用(未配置日志目录)": "服务器日志功能未启用(未配置日志目录)", "服务器日志管理": "服务器日志管理", diff --git a/web/src/i18n/locales/zh-TW.json b/web/src/i18n/locales/zh-TW.json index db2a57b6..a7727f89 100644 --- a/web/src/i18n/locales/zh-TW.json +++ b/web/src/i18n/locales/zh-TW.json @@ -1926,6 +1926,7 @@ "服务可用性": "服務可用性", "服务商": "服務商", "服务器IP": "伺服器IP", + "节点名称": "節點名稱", "服务器地址": "伺服器位址", "服务器日志功能未启用(未配置日志目录)": "伺服器日誌功能未啟用(未配置日誌目錄)", "服务器日志管理": "伺服器日誌管理", From d75a0467919e6d9625ecfc25b0b8aa1728d72083 Mon Sep 17 00:00:00 2001 From: CaIon Date: Sat, 18 Apr 2026 00:56:07 +0800 Subject: [PATCH 32/46] chore(docker-compose): set default redis password Enable Redis requirepass in the compose template and embed the matching credential in REDIS_CONN_STRING, aligning with the existing PostgreSQL default password pattern so out-of-the-box deployments are not left with an unauthenticated Redis instance. --- docker-compose.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7a887b60..be8c885b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,7 +28,7 @@ services: environment: - SQL_DSN=postgresql://root:123456@postgres:5432/new-api # ⚠️ IMPORTANT: Change the password in production! # - SQL_DSN=root:123456@tcp(mysql:3306)/new-api # Point to the mysql service, uncomment if using MySQL - - REDIS_CONN_STRING=redis://redis + - REDIS_CONN_STRING=redis://:123456@redis:6379 # ⚠️ IMPORTANT: Change the password in production! - TZ=Asia/Shanghai - ERROR_LOG_ENABLED=true # 是否启用错误日志记录 (Whether to enable error log recording) - BATCH_UPDATE_ENABLED=true # 是否启用批量更新 (Whether to enable batch update) @@ -56,6 +56,7 @@ services: image: redis:latest container_name: redis restart: always + command: ["redis-server", "--requirepass", "123456"] # ⚠️ IMPORTANT: Change this password in production! networks: - new-api-network From f995a868e4551e3180c7d836561a5a257dae93dc Mon Sep 17 00:00:00 2001 From: Seefs <40468931+seefs001@users.noreply.github.com> Date: Sat, 18 Apr 2026 14:22:54 +0800 Subject: [PATCH 33/46] Merge pull request #4089 from seefs001/feature/waffo-pay rafactor: payment --- controller/option.go | 14 +- controller/payment_webhook_availability.go | 100 +++++ .../payment_webhook_availability_test.go | 166 +++++++ controller/subscription_payment_creem.go | 22 +- controller/subscription_payment_epay.go | 6 +- controller/subscription_payment_stripe.go | 6 +- controller/topup.go | 156 ++++--- controller/topup_creem.go | 121 +++--- controller/topup_epay_guard_test.go | 31 ++ controller/topup_stripe.go | 133 +++--- controller/topup_waffo.go | 107 +++-- controller/topup_waffo_pancake.go | 259 +++++++++++ controller/topup_waffo_pancake_test.go | 91 ++++ dto/values.go | 22 + model/option.go | 36 ++ model/payment_method_guard_test.go | 172 ++++++++ model/subscription.go | 12 +- model/task_cas_test.go | 16 +- model/topup.go | 115 ++++- router/api-router.go | 4 + service/task_billing_test.go | 2 + service/waffo_pancake.go | 398 +++++++++++++++++ service/waffo_pancake_test.go | 157 +++++++ setting/payment_waffo_pancake.go | 16 + .../components/settings/PaymentSetting.jsx | 90 +++- web/src/components/topup/RechargeCard.jsx | 116 ++--- web/src/components/topup/index.jsx | 230 ++++++++-- .../topup/modals/PaymentConfirmModal.jsx | 11 + web/src/i18n/locales/en.json | 12 +- web/src/i18n/locales/fr.json | 12 +- web/src/i18n/locales/ja.json | 12 +- web/src/i18n/locales/ru.json | 12 +- web/src/i18n/locales/vi.json | 12 +- web/src/i18n/locales/zh-CN.json | 46 +- web/src/i18n/locales/zh-TW.json | 12 +- .../Payment/SettingsGeneralPayment.jsx | 176 +++++++- .../Payment/SettingsPaymentGateway.jsx | 183 +------- .../Payment/SettingsPaymentGatewayCreem.jsx | 32 +- .../Payment/SettingsPaymentGatewayStripe.jsx | 82 ++-- .../Payment/SettingsPaymentGatewayWaffo.jsx | 351 +++++++++------ .../SettingsPaymentGatewayWaffoPancake.jsx | 411 ++++++++++++++++++ 41 files changed, 3222 insertions(+), 740 deletions(-) create mode 100644 controller/payment_webhook_availability.go create mode 100644 controller/payment_webhook_availability_test.go create mode 100644 controller/topup_epay_guard_test.go create mode 100644 controller/topup_waffo_pancake.go create mode 100644 controller/topup_waffo_pancake_test.go create mode 100644 model/payment_method_guard_test.go create mode 100644 service/waffo_pancake.go create mode 100644 service/waffo_pancake_test.go create mode 100644 setting/payment_waffo_pancake.go create mode 100644 web/src/pages/Setting/Payment/SettingsPaymentGatewayWaffoPancake.jsx diff --git a/controller/option.go b/controller/option.go index ecb1e25e..5a7c9f41 100644 --- a/controller/option.go +++ b/controller/option.go @@ -27,6 +27,15 @@ var completionRatioMetaOptionKeys = []string{ "AudioCompletionRatio", } +func isVisiblePublicKeyOption(key string) bool { + switch key { + case "WaffoPancakeWebhookPublicKey", "WaffoPancakeWebhookTestKey": + return true + default: + return false + } +} + func collectModelNamesFromOptionValue(raw string, modelNames map[string]struct{}) { if strings.TrimSpace(raw) == "" { return @@ -66,11 +75,12 @@ func GetOptions(c *gin.Context) { common.OptionMapRWMutex.Lock() for k, v := range common.OptionMap { value := common.Interface2String(v) - if strings.HasSuffix(k, "Token") || + isSensitiveKey := strings.HasSuffix(k, "Token") || strings.HasSuffix(k, "Secret") || strings.HasSuffix(k, "Key") || strings.HasSuffix(k, "secret") || - strings.HasSuffix(k, "api_key") { + strings.HasSuffix(k, "api_key") + if isSensitiveKey && !isVisiblePublicKeyOption(k) { continue } options = append(options, &model.Option{ diff --git a/controller/payment_webhook_availability.go b/controller/payment_webhook_availability.go new file mode 100644 index 00000000..9e7a08ae --- /dev/null +++ b/controller/payment_webhook_availability.go @@ -0,0 +1,100 @@ +package controller + +import ( + "strings" + + "github.com/QuantumNous/new-api/setting" + "github.com/QuantumNous/new-api/setting/operation_setting" +) + +func isStripeTopUpEnabled() bool { + return strings.TrimSpace(setting.StripeApiSecret) != "" && + strings.TrimSpace(setting.StripeWebhookSecret) != "" && + strings.TrimSpace(setting.StripePriceId) != "" +} + +func isStripeWebhookConfigured() bool { + return strings.TrimSpace(setting.StripeWebhookSecret) != "" +} + +func isStripeWebhookEnabled() bool { + return isStripeTopUpEnabled() +} + +func isCreemTopUpEnabled() bool { + products := strings.TrimSpace(setting.CreemProducts) + return strings.TrimSpace(setting.CreemApiKey) != "" && + products != "" && + products != "[]" +} + +func isCreemWebhookConfigured() bool { + return strings.TrimSpace(setting.CreemWebhookSecret) != "" +} + +func isCreemWebhookEnabled() bool { + return isCreemTopUpEnabled() && isCreemWebhookConfigured() +} + +func isWaffoTopUpEnabled() bool { + if !setting.WaffoEnabled { + return false + } + + return isWaffoWebhookConfigured() +} + +func isWaffoWebhookConfigured() bool { + if setting.WaffoSandbox { + return strings.TrimSpace(setting.WaffoSandboxApiKey) != "" && + strings.TrimSpace(setting.WaffoSandboxPrivateKey) != "" && + strings.TrimSpace(setting.WaffoSandboxPublicCert) != "" + } + + return strings.TrimSpace(setting.WaffoApiKey) != "" && + strings.TrimSpace(setting.WaffoPrivateKey) != "" && + strings.TrimSpace(setting.WaffoPublicCert) != "" +} + +func isWaffoWebhookEnabled() bool { + return isWaffoTopUpEnabled() +} + +func isWaffoPancakeTopUpEnabled() bool { + if !setting.WaffoPancakeEnabled { + return false + } + + return isWaffoPancakeWebhookConfigured() && + strings.TrimSpace(setting.WaffoPancakeMerchantID) != "" && + strings.TrimSpace(setting.WaffoPancakePrivateKey) != "" && + strings.TrimSpace(setting.WaffoPancakeStoreID) != "" && + strings.TrimSpace(setting.WaffoPancakeProductID) != "" +} + +func isWaffoPancakeWebhookConfigured() bool { + currentWebhookKey := strings.TrimSpace(setting.WaffoPancakeWebhookPublicKey) + if setting.WaffoPancakeSandbox { + currentWebhookKey = strings.TrimSpace(setting.WaffoPancakeWebhookTestKey) + } + + return currentWebhookKey != "" +} + +func isWaffoPancakeWebhookEnabled() bool { + return isWaffoPancakeTopUpEnabled() +} + +func isEpayTopUpEnabled() bool { + return isEpayWebhookConfigured() && len(operation_setting.PayMethods) > 0 +} + +func isEpayWebhookConfigured() bool { + return strings.TrimSpace(operation_setting.PayAddress) != "" && + strings.TrimSpace(operation_setting.EpayId) != "" && + strings.TrimSpace(operation_setting.EpayKey) != "" +} + +func isEpayWebhookEnabled() bool { + return isEpayTopUpEnabled() +} diff --git a/controller/payment_webhook_availability_test.go b/controller/payment_webhook_availability_test.go new file mode 100644 index 00000000..0534acc4 --- /dev/null +++ b/controller/payment_webhook_availability_test.go @@ -0,0 +1,166 @@ +package controller + +import ( + "testing" + + "github.com/QuantumNous/new-api/setting" + "github.com/QuantumNous/new-api/setting/operation_setting" + "github.com/stretchr/testify/require" +) + +func TestStripeWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { + originalAPISecret := setting.StripeApiSecret + originalWebhookSecret := setting.StripeWebhookSecret + originalPriceID := setting.StripePriceId + t.Cleanup(func() { + setting.StripeApiSecret = originalAPISecret + setting.StripeWebhookSecret = originalWebhookSecret + setting.StripePriceId = originalPriceID + }) + + setting.StripeWebhookSecret = "" + setting.StripeApiSecret = "sk_test_123" + setting.StripePriceId = "price_123" + require.False(t, isStripeWebhookEnabled()) + + setting.StripeWebhookSecret = "whsec_test" + require.True(t, isStripeWebhookEnabled()) + + setting.StripePriceId = "" + require.False(t, isStripeWebhookEnabled()) +} + +func TestCreemWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { + originalAPIKey := setting.CreemApiKey + originalProducts := setting.CreemProducts + originalWebhookSecret := setting.CreemWebhookSecret + t.Cleanup(func() { + setting.CreemApiKey = originalAPIKey + setting.CreemProducts = originalProducts + setting.CreemWebhookSecret = originalWebhookSecret + }) + + setting.CreemWebhookSecret = "" + setting.CreemApiKey = "creem_api_key" + setting.CreemProducts = `[{"productId":"prod_123"}]` + require.False(t, isCreemWebhookEnabled()) + + setting.CreemWebhookSecret = "creem_secret" + require.True(t, isCreemWebhookEnabled()) + + setting.CreemProducts = "[]" + require.False(t, isCreemWebhookEnabled()) +} + +func TestWaffoWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { + originalEnabled := setting.WaffoEnabled + originalSandbox := setting.WaffoSandbox + originalAPIKey := setting.WaffoApiKey + originalPrivateKey := setting.WaffoPrivateKey + originalPublicCert := setting.WaffoPublicCert + originalSandboxAPIKey := setting.WaffoSandboxApiKey + originalSandboxPrivateKey := setting.WaffoSandboxPrivateKey + originalSandboxPublicCert := setting.WaffoSandboxPublicCert + t.Cleanup(func() { + setting.WaffoEnabled = originalEnabled + setting.WaffoSandbox = originalSandbox + setting.WaffoApiKey = originalAPIKey + setting.WaffoPrivateKey = originalPrivateKey + setting.WaffoPublicCert = originalPublicCert + setting.WaffoSandboxApiKey = originalSandboxAPIKey + setting.WaffoSandboxPrivateKey = originalSandboxPrivateKey + setting.WaffoSandboxPublicCert = originalSandboxPublicCert + }) + + setting.WaffoEnabled = true + setting.WaffoSandbox = false + setting.WaffoApiKey = "" + setting.WaffoPrivateKey = "private" + setting.WaffoPublicCert = "public" + require.False(t, isWaffoWebhookEnabled()) + + setting.WaffoApiKey = "api" + require.True(t, isWaffoWebhookEnabled()) + + setting.WaffoEnabled = false + require.False(t, isWaffoWebhookEnabled()) + + setting.WaffoEnabled = true + setting.WaffoSandbox = true + setting.WaffoSandboxApiKey = "" + setting.WaffoSandboxPrivateKey = "sandbox_private" + setting.WaffoSandboxPublicCert = "sandbox_public" + require.False(t, isWaffoWebhookEnabled()) + + setting.WaffoSandboxApiKey = "sandbox_api" + require.True(t, isWaffoWebhookEnabled()) +} + +func TestWaffoPancakeWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { + originalEnabled := setting.WaffoPancakeEnabled + originalSandbox := setting.WaffoPancakeSandbox + originalMerchantID := setting.WaffoPancakeMerchantID + originalPrivateKey := setting.WaffoPancakePrivateKey + originalWebhookPublicKey := setting.WaffoPancakeWebhookPublicKey + originalWebhookTestKey := setting.WaffoPancakeWebhookTestKey + originalStoreID := setting.WaffoPancakeStoreID + originalProductID := setting.WaffoPancakeProductID + t.Cleanup(func() { + setting.WaffoPancakeEnabled = originalEnabled + setting.WaffoPancakeSandbox = originalSandbox + setting.WaffoPancakeMerchantID = originalMerchantID + setting.WaffoPancakePrivateKey = originalPrivateKey + setting.WaffoPancakeWebhookPublicKey = originalWebhookPublicKey + setting.WaffoPancakeWebhookTestKey = originalWebhookTestKey + setting.WaffoPancakeStoreID = originalStoreID + setting.WaffoPancakeProductID = originalProductID + }) + + setting.WaffoPancakeEnabled = true + setting.WaffoPancakeSandbox = false + setting.WaffoPancakeMerchantID = "merchant" + setting.WaffoPancakePrivateKey = "private" + setting.WaffoPancakeStoreID = "store" + setting.WaffoPancakeProductID = "product" + setting.WaffoPancakeWebhookPublicKey = "" + require.False(t, isWaffoPancakeWebhookEnabled()) + + setting.WaffoPancakeWebhookPublicKey = "public" + require.True(t, isWaffoPancakeWebhookEnabled()) + + setting.WaffoPancakeEnabled = false + require.False(t, isWaffoPancakeWebhookEnabled()) + + setting.WaffoPancakeEnabled = true + setting.WaffoPancakeSandbox = true + setting.WaffoPancakeWebhookTestKey = "" + require.False(t, isWaffoPancakeWebhookEnabled()) + + setting.WaffoPancakeWebhookTestKey = "test_public" + require.True(t, isWaffoPancakeWebhookEnabled()) +} + +func TestEpayWebhookEnabledRequiresTopUpAndWebhookConfig(t *testing.T) { + originalPayAddress := operation_setting.PayAddress + originalEpayID := operation_setting.EpayId + originalEpayKey := operation_setting.EpayKey + originalPayMethods := operation_setting.PayMethods + t.Cleanup(func() { + operation_setting.PayAddress = originalPayAddress + operation_setting.EpayId = originalEpayID + operation_setting.EpayKey = originalEpayKey + operation_setting.PayMethods = originalPayMethods + }) + + operation_setting.PayAddress = "https://pay.example.com" + operation_setting.EpayId = "epay_id" + operation_setting.EpayKey = "" + operation_setting.PayMethods = []map[string]string{{"type": "alipay"}} + require.False(t, isEpayWebhookEnabled()) + + operation_setting.EpayKey = "epay_key" + require.True(t, isEpayWebhookEnabled()) + + operation_setting.PayMethods = nil + require.False(t, isEpayWebhookEnabled()) +} diff --git a/controller/subscription_payment_creem.go b/controller/subscription_payment_creem.go index 258d4fb3..935429ac 100644 --- a/controller/subscription_payment_creem.go +++ b/controller/subscription_payment_creem.go @@ -2,11 +2,13 @@ package controller import ( "bytes" + "fmt" "io" - "log" + "net/http" "time" "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/setting" "github.com/QuantumNous/new-api/setting/operation_setting" @@ -24,14 +26,14 @@ func SubscriptionRequestCreemPay(c *gin.Context) { // Keep body for debugging consistency (like RequestCreemPay) bodyBytes, err := io.ReadAll(c.Request.Body) if err != nil { - log.Printf("read subscription creem pay req body err: %v", err) - c.JSON(200, gin.H{"message": "error", "data": "read query error"}) + logger.LogError(c.Request.Context(), fmt.Sprintf("Creem 订阅支付请求读取失败 error=%q", err.Error())) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "read query error"}) return } c.Request.Body = io.NopCloser(bytes.NewReader(bodyBytes)) if err := c.ShouldBindJSON(&req); err != nil || req.PlanId <= 0 { - c.JSON(200, gin.H{"message": "error", "data": "参数错误"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "参数错误"}) return } @@ -85,12 +87,12 @@ func SubscriptionRequestCreemPay(c *gin.Context) { PlanId: plan.Id, Money: plan.PriceAmount, TradeNo: referenceId, - PaymentMethod: PaymentMethodCreem, + PaymentMethod: model.PaymentMethodCreem, CreateTime: time.Now().Unix(), Status: common.TopUpStatusPending, } if err := order.Insert(); err != nil { - c.JSON(200, gin.H{"message": "error", "data": "创建订单失败"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "创建订单失败"}) return } @@ -112,14 +114,14 @@ func SubscriptionRequestCreemPay(c *gin.Context) { Quota: 0, } - checkoutUrl, err := genCreemLink(referenceId, product, user.Email, user.Username) + checkoutUrl, err := genCreemLink(c.Request.Context(), referenceId, product, user.Email, user.Username) if err != nil { - log.Printf("获取Creem支付链接失败: %v", err) - c.JSON(200, gin.H{"message": "error", "data": "拉起支付失败"}) + logger.LogError(c.Request.Context(), fmt.Sprintf("Creem 订阅支付链接创建失败 trade_no=%s product_id=%s error=%q", referenceId, product.ProductId, err.Error())) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "拉起支付失败"}) return } - c.JSON(200, gin.H{ + c.JSON(http.StatusOK, gin.H{ "message": "success", "data": gin.H{ "checkout_url": checkoutUrl, diff --git a/controller/subscription_payment_epay.go b/controller/subscription_payment_epay.go index c45b3914..8f7848d5 100644 --- a/controller/subscription_payment_epay.go +++ b/controller/subscription_payment_epay.go @@ -104,7 +104,7 @@ func SubscriptionRequestEpay(c *gin.Context) { ReturnUrl: returnUrl, }) if err != nil { - _ = model.ExpireSubscriptionOrder(tradeNo) + _ = model.ExpireSubscriptionOrder(tradeNo, req.PaymentMethod) common.ApiErrorMsg(c, "拉起支付失败") return } @@ -156,7 +156,7 @@ func SubscriptionEpayNotify(c *gin.Context) { LockOrder(verifyInfo.ServiceTradeNo) defer UnlockOrder(verifyInfo.ServiceTradeNo) - if err := model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, common.GetJsonString(verifyInfo)); err != nil { + if err := model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, common.GetJsonString(verifyInfo), verifyInfo.Type); err != nil { _, _ = c.Writer.Write([]byte("fail")) return } @@ -205,7 +205,7 @@ func SubscriptionEpayReturn(c *gin.Context) { if verifyInfo.TradeStatus == epay.StatusTradeSuccess { LockOrder(verifyInfo.ServiceTradeNo) defer UnlockOrder(verifyInfo.ServiceTradeNo) - if err := model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, common.GetJsonString(verifyInfo)); err != nil { + if err := model.CompleteSubscriptionOrder(verifyInfo.ServiceTradeNo, common.GetJsonString(verifyInfo), verifyInfo.Type); err != nil { c.Redirect(http.StatusFound, system_setting.ServerAddress+"/console/topup?pay=fail") return } diff --git a/controller/subscription_payment_stripe.go b/controller/subscription_payment_stripe.go index 2603a828..9824c90d 100644 --- a/controller/subscription_payment_stripe.go +++ b/controller/subscription_payment_stripe.go @@ -2,12 +2,12 @@ package controller import ( "fmt" - "log" "net/http" "strings" "time" "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/setting" "github.com/QuantumNous/new-api/setting/system_setting" @@ -78,7 +78,7 @@ func SubscriptionRequestStripePay(c *gin.Context) { payLink, err := genStripeSubscriptionLink(referenceId, user.StripeCustomer, user.Email, plan.StripePriceId) if err != nil { - log.Println("获取Stripe Checkout支付链接失败", err) + logger.LogError(c.Request.Context(), fmt.Sprintf("Stripe 订阅支付链接创建失败 trade_no=%s plan_id=%d error=%q", referenceId, plan.Id, err.Error())) c.JSON(http.StatusOK, gin.H{"message": "error", "data": "拉起支付失败"}) return } @@ -88,7 +88,7 @@ func SubscriptionRequestStripePay(c *gin.Context) { PlanId: plan.Id, Money: plan.PriceAmount, TradeNo: referenceId, - PaymentMethod: PaymentMethodStripe, + PaymentMethod: model.PaymentMethodStripe, CreateTime: time.Now().Unix(), Status: common.TopUpStatusPending, } diff --git a/controller/topup.go b/controller/topup.go index 395156e4..86d361a3 100644 --- a/controller/topup.go +++ b/controller/topup.go @@ -2,7 +2,7 @@ package controller import ( "fmt" - "log" + "net/http" "net/url" "strconv" "sync" @@ -27,7 +27,7 @@ func GetTopUpInfo(c *gin.Context) { payMethods := operation_setting.PayMethods // 如果启用了 Stripe 支付,添加到支付方法列表 - if setting.StripeApiSecret != "" && setting.StripeWebhookSecret != "" && setting.StripePriceId != "" { + if isStripeTopUpEnabled() { // 检查是否已经包含 Stripe hasStripe := false for _, method := range payMethods { @@ -49,19 +49,11 @@ func GetTopUpInfo(c *gin.Context) { } // 如果启用了 Waffo 支付,添加到支付方法列表 - enableWaffo := setting.WaffoEnabled && - ((!setting.WaffoSandbox && - setting.WaffoApiKey != "" && - setting.WaffoPrivateKey != "" && - setting.WaffoPublicCert != "") || - (setting.WaffoSandbox && - setting.WaffoSandboxApiKey != "" && - setting.WaffoSandboxPrivateKey != "" && - setting.WaffoSandboxPublicCert != "")) + enableWaffo := isWaffoTopUpEnabled() if enableWaffo { hasWaffo := false for _, method := range payMethods { - if method["type"] == "waffo" { + if method["type"] == model.PaymentMethodWaffo { hasWaffo = true break } @@ -70,7 +62,7 @@ func GetTopUpInfo(c *gin.Context) { if !hasWaffo { waffoMethod := map[string]string{ "name": "Waffo (Global Payment)", - "type": "waffo", + "type": model.PaymentMethodWaffo, "color": "rgba(var(--semi-blue-5), 1)", "min_topup": strconv.Itoa(setting.WaffoMinTopUp), } @@ -78,24 +70,46 @@ func GetTopUpInfo(c *gin.Context) { } } + enableWaffoPancake := isWaffoPancakeTopUpEnabled() + if enableWaffoPancake { + hasWaffoPancake := false + for _, method := range payMethods { + if method["type"] == model.PaymentMethodWaffoPancake { + hasWaffoPancake = true + break + } + } + + if !hasWaffoPancake { + payMethods = append(payMethods, map[string]string{ + "name": "Waffo Pancake", + "type": model.PaymentMethodWaffoPancake, + "color": "rgba(var(--semi-orange-5), 1)", + "min_topup": strconv.Itoa(setting.WaffoPancakeMinTopUp), + }) + } + } + data := gin.H{ - "enable_online_topup": operation_setting.PayAddress != "" && operation_setting.EpayId != "" && operation_setting.EpayKey != "", - "enable_stripe_topup": setting.StripeApiSecret != "" && setting.StripeWebhookSecret != "" && setting.StripePriceId != "", - "enable_creem_topup": setting.CreemApiKey != "" && setting.CreemProducts != "[]", - "enable_waffo_topup": enableWaffo, + "enable_online_topup": isEpayTopUpEnabled(), + "enable_stripe_topup": isStripeTopUpEnabled(), + "enable_creem_topup": isCreemTopUpEnabled(), + "enable_waffo_topup": enableWaffo, + "enable_waffo_pancake_topup": enableWaffoPancake, "waffo_pay_methods": func() interface{} { if enableWaffo { return setting.GetWaffoPayMethods() } return nil }(), - "creem_products": setting.CreemProducts, - "pay_methods": payMethods, - "min_topup": operation_setting.MinTopUp, - "stripe_min_topup": setting.StripeMinTopUp, - "waffo_min_topup": setting.WaffoMinTopUp, - "amount_options": operation_setting.GetPaymentSetting().AmountOptions, - "discount": operation_setting.GetPaymentSetting().AmountDiscount, + "creem_products": setting.CreemProducts, + "pay_methods": payMethods, + "min_topup": operation_setting.MinTopUp, + "stripe_min_topup": setting.StripeMinTopUp, + "waffo_min_topup": setting.WaffoMinTopUp, + "waffo_pancake_min_topup": setting.WaffoPancakeMinTopUp, + "amount_options": operation_setting.GetPaymentSetting().AmountOptions, + "discount": operation_setting.GetPaymentSetting().AmountDiscount, } common.ApiSuccess(c, data) } @@ -109,6 +123,17 @@ type AmountRequest struct { Amount int64 `json:"amount"` } +var nonEpayPaymentMethodsForCallback = []string{ + model.PaymentMethodStripe, + model.PaymentMethodCreem, + model.PaymentMethodWaffo, + model.PaymentMethodWaffoPancake, +} + +func isNonEpayPaymentMethodForEpayCallback(paymentMethod string) bool { + return lo.Contains(nonEpayPaymentMethodsForCallback, paymentMethod) +} + func GetEpayClient() *epay.Client { if operation_setting.PayAddress == "" || operation_setting.EpayId == "" || operation_setting.EpayKey == "" { return nil @@ -167,28 +192,28 @@ func RequestEpay(c *gin.Context) { var req EpayRequest err := c.ShouldBindJSON(&req) if err != nil { - c.JSON(200, gin.H{"message": "error", "data": "参数错误"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "参数错误"}) return } if req.Amount < getMinTopup() { - c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", getMinTopup())}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", getMinTopup())}) return } id := c.GetInt("id") group, err := model.GetUserGroup(id, true) if err != nil { - c.JSON(200, gin.H{"message": "error", "data": "获取用户分组失败"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "获取用户分组失败"}) return } payMoney := getPayMoney(req.Amount, group) if payMoney < 0.01 { - c.JSON(200, gin.H{"message": "error", "data": "充值金额过低"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "充值金额过低"}) return } if !operation_setting.ContainsPayMethod(req.PaymentMethod) { - c.JSON(200, gin.H{"message": "error", "data": "支付方式不存在"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "支付方式不存在"}) return } @@ -199,7 +224,7 @@ func RequestEpay(c *gin.Context) { tradeNo = fmt.Sprintf("USR%dNO%s", id, tradeNo) client := GetEpayClient() if client == nil { - c.JSON(200, gin.H{"message": "error", "data": "当前管理员未配置支付信息"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "当前管理员未配置支付信息"}) return } uri, params, err := client.Purchase(&epay.PurchaseArgs{ @@ -212,7 +237,8 @@ func RequestEpay(c *gin.Context) { ReturnUrl: returnUrl, }) if err != nil { - c.JSON(200, gin.H{"message": "error", "data": "拉起支付失败"}) + logger.LogError(c.Request.Context(), fmt.Sprintf("易支付 拉起支付失败 user_id=%d trade_no=%s payment_method=%s amount=%d error=%q", id, tradeNo, req.PaymentMethod, req.Amount, err.Error())) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "拉起支付失败"}) return } amount := req.Amount @@ -228,14 +254,16 @@ func RequestEpay(c *gin.Context) { TradeNo: tradeNo, PaymentMethod: req.PaymentMethod, CreateTime: time.Now().Unix(), - Status: "pending", + Status: common.TopUpStatusPending, } err = topUp.Insert() if err != nil { - c.JSON(200, gin.H{"message": "error", "data": "创建订单失败"}) + logger.LogError(c.Request.Context(), fmt.Sprintf("易支付 创建充值订单失败 user_id=%d trade_no=%s payment_method=%s amount=%d error=%q", id, tradeNo, req.PaymentMethod, req.Amount, err.Error())) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "创建订单失败"}) return } - c.JSON(200, gin.H{"message": "success", "data": params, "url": uri}) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("易支付 充值订单创建成功 user_id=%d trade_no=%s payment_method=%s amount=%d money=%.2f uri=%q params=%q", id, tradeNo, req.PaymentMethod, req.Amount, payMoney, uri, common.GetJsonString(params))) + c.JSON(http.StatusOK, gin.H{"message": "success", "data": params, "url": uri}) } // tradeNo lock @@ -281,12 +309,18 @@ func UnlockOrder(tradeNo string) { } func EpayNotify(c *gin.Context) { + if !isEpayWebhookEnabled() { + logger.LogWarn(c.Request.Context(), fmt.Sprintf("易支付 webhook 被拒绝 reason=webhook_disabled path=%q client_ip=%s", c.Request.RequestURI, c.ClientIP())) + _, _ = c.Writer.Write([]byte("fail")) + return + } + var params map[string]string if c.Request.Method == "POST" { // POST 请求:从 POST body 解析参数 if err := c.Request.ParseForm(); err != nil { - log.Println("易支付回调POST解析失败:", err) + logger.LogError(c.Request.Context(), fmt.Sprintf("易支付 webhook POST 表单解析失败 path=%q client_ip=%s error=%q", c.Request.RequestURI, c.ClientIP(), err.Error())) _, _ = c.Writer.Write([]byte("fail")) return } @@ -301,54 +335,63 @@ func EpayNotify(c *gin.Context) { return r }, map[string]string{}) } + logger.LogInfo(c.Request.Context(), fmt.Sprintf("易支付 webhook 收到请求 path=%q client_ip=%s method=%s params=%q", c.Request.RequestURI, c.ClientIP(), c.Request.Method, common.GetJsonString(params))) if len(params) == 0 { - log.Println("易支付回调参数为空") + logger.LogWarn(c.Request.Context(), fmt.Sprintf("易支付 webhook 参数为空 path=%q client_ip=%s", c.Request.RequestURI, c.ClientIP())) _, _ = c.Writer.Write([]byte("fail")) return } client := GetEpayClient() if client == nil { - log.Println("易支付回调失败 未找到配置信息") + logger.LogError(c.Request.Context(), fmt.Sprintf("易支付 client 未初始化 path=%q client_ip=%s", c.Request.RequestURI, c.ClientIP())) _, err := c.Writer.Write([]byte("fail")) if err != nil { - log.Println("易支付回调写入失败") + logger.LogError(c.Request.Context(), fmt.Sprintf("易支付 webhook 响应写入失败 path=%q client_ip=%s error=%q", c.Request.RequestURI, c.ClientIP(), err.Error())) } return } verifyInfo, err := client.Verify(params) if err == nil && verifyInfo.VerifyStatus { + logger.LogInfo(c.Request.Context(), fmt.Sprintf("易支付 webhook 验签成功 trade_no=%s callback_type=%s trade_status=%s client_ip=%s verify_info=%q", verifyInfo.ServiceTradeNo, verifyInfo.Type, verifyInfo.TradeStatus, c.ClientIP(), common.GetJsonString(verifyInfo))) _, err := c.Writer.Write([]byte("success")) if err != nil { - log.Println("易支付回调写入失败") + logger.LogError(c.Request.Context(), fmt.Sprintf("易支付 webhook 响应写入失败 trade_no=%s client_ip=%s error=%q", verifyInfo.ServiceTradeNo, c.ClientIP(), err.Error())) } } else { _, err := c.Writer.Write([]byte("fail")) if err != nil { - log.Println("易支付回调写入失败") + logger.LogError(c.Request.Context(), fmt.Sprintf("易支付 webhook 响应写入失败 path=%q client_ip=%s error=%q", c.Request.RequestURI, c.ClientIP(), err.Error())) + } + if err != nil { + logger.LogWarn(c.Request.Context(), fmt.Sprintf("易支付 webhook 验签失败 path=%q client_ip=%s verify_error=%q", c.Request.RequestURI, c.ClientIP(), err.Error())) + } else { + logger.LogWarn(c.Request.Context(), fmt.Sprintf("易支付 webhook 验签失败 path=%q client_ip=%s verify_status=false", c.Request.RequestURI, c.ClientIP())) } - log.Println("易支付回调签名验证失败") return } if verifyInfo.TradeStatus == epay.StatusTradeSuccess { - log.Println(verifyInfo) LockOrder(verifyInfo.ServiceTradeNo) defer UnlockOrder(verifyInfo.ServiceTradeNo) topUp := model.GetTopUpByTradeNo(verifyInfo.ServiceTradeNo) if topUp == nil { - log.Printf("易支付回调未找到订单: %v", verifyInfo) + logger.LogWarn(c.Request.Context(), fmt.Sprintf("易支付 回调订单不存在 trade_no=%s callback_type=%s client_ip=%s verify_info=%q", verifyInfo.ServiceTradeNo, verifyInfo.Type, c.ClientIP(), common.GetJsonString(verifyInfo))) return } - if topUp.PaymentMethod == "stripe" || topUp.PaymentMethod == "creem" || topUp.PaymentMethod == "waffo" { - log.Printf("易支付回调订单支付方式不匹配: %s, 订单号: %s", topUp.PaymentMethod, verifyInfo.ServiceTradeNo) + if isNonEpayPaymentMethodForEpayCallback(topUp.PaymentMethod) { + logger.LogWarn(c.Request.Context(), fmt.Sprintf("易支付 订单支付方式不匹配 trade_no=%s order_payment_method=%s callback_type=%s client_ip=%s", verifyInfo.ServiceTradeNo, topUp.PaymentMethod, verifyInfo.Type, c.ClientIP())) return } - if topUp.Status == "pending" { - topUp.Status = "success" + if topUp.PaymentMethod != verifyInfo.Type { + logger.LogWarn(c.Request.Context(), fmt.Sprintf("易支付 订单支付方式不匹配 trade_no=%s order_payment_method=%s callback_type=%s client_ip=%s", verifyInfo.ServiceTradeNo, topUp.PaymentMethod, verifyInfo.Type, c.ClientIP())) + return + } + if topUp.Status == common.TopUpStatusPending { + topUp.Status = common.TopUpStatusSuccess err := topUp.Update() if err != nil { - log.Printf("易支付回调更新订单失败: %v", topUp) + logger.LogError(c.Request.Context(), fmt.Sprintf("易支付 更新充值订单失败 trade_no=%s user_id=%d client_ip=%s error=%q topup=%q", topUp.TradeNo, topUp.UserId, c.ClientIP(), err.Error(), common.GetJsonString(topUp))) return } //user, _ := model.GetUserById(topUp.UserId, false) @@ -358,14 +401,14 @@ func EpayNotify(c *gin.Context) { quotaToAdd := int(dAmount.Mul(dQuotaPerUnit).IntPart()) err = model.IncreaseUserQuota(topUp.UserId, quotaToAdd, true) if err != nil { - log.Printf("易支付回调更新用户失败: %v", topUp) + logger.LogError(c.Request.Context(), fmt.Sprintf("易支付 更新用户额度失败 trade_no=%s user_id=%d client_ip=%s quota_to_add=%d error=%q topup=%q", topUp.TradeNo, topUp.UserId, c.ClientIP(), quotaToAdd, err.Error(), common.GetJsonString(topUp))) return } - log.Printf("易支付回调更新用户成功 %v", topUp) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("易支付 充值成功 trade_no=%s user_id=%d client_ip=%s quota_to_add=%d money=%.2f topup=%q", topUp.TradeNo, topUp.UserId, c.ClientIP(), quotaToAdd, topUp.Money, common.GetJsonString(topUp))) model.RecordTopupLog(topUp.UserId, fmt.Sprintf("使用在线充值成功,充值金额: %v,支付金额:%f", logger.LogQuota(quotaToAdd), topUp.Money), c.ClientIP(), topUp.PaymentMethod, "epay") } } else { - log.Printf("易支付异常回调: %v", verifyInfo) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("易支付 webhook 忽略事件 trade_no=%s callback_type=%s trade_status=%s client_ip=%s verify_info=%q", verifyInfo.ServiceTradeNo, verifyInfo.Type, verifyInfo.TradeStatus, c.ClientIP(), common.GetJsonString(verifyInfo))) } } @@ -373,26 +416,26 @@ func RequestAmount(c *gin.Context) { var req AmountRequest err := c.ShouldBindJSON(&req) if err != nil { - c.JSON(200, gin.H{"message": "error", "data": "参数错误"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "参数错误"}) return } if req.Amount < getMinTopup() { - c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", getMinTopup())}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", getMinTopup())}) return } id := c.GetInt("id") group, err := model.GetUserGroup(id, true) if err != nil { - c.JSON(200, gin.H{"message": "error", "data": "获取用户分组失败"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "获取用户分组失败"}) return } payMoney := getPayMoney(req.Amount, group) if payMoney <= 0.01 { - c.JSON(200, gin.H{"message": "error", "data": "充值金额过低"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "充值金额过低"}) return } - c.JSON(200, gin.H{"message": "success", "data": strconv.FormatFloat(payMoney, 'f', 2, 64)}) + c.JSON(http.StatusOK, gin.H{"message": "success", "data": strconv.FormatFloat(payMoney, 'f', 2, 64)}) } func GetUserTopUps(c *gin.Context) { @@ -467,4 +510,3 @@ func AdminCompleteTopUp(c *gin.Context) { } common.ApiSuccess(c, nil) } - diff --git a/controller/topup_creem.go b/controller/topup_creem.go index 0b6b1627..139dd43f 100644 --- a/controller/topup_creem.go +++ b/controller/topup_creem.go @@ -2,6 +2,7 @@ package controller import ( "bytes" + "context" "crypto/hmac" "crypto/sha256" "encoding/hex" @@ -9,10 +10,10 @@ import ( "errors" "fmt" "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/setting" "io" - "log" "net/http" "time" @@ -20,10 +21,7 @@ import ( "github.com/thanhpk/randstr" ) -const ( - PaymentMethodCreem = "creem" - CreemSignatureHeader = "creem-signature" -) +const CreemSignatureHeader = "creem-signature" var creemAdaptor = &CreemAdaptor{} @@ -37,9 +35,9 @@ func generateCreemSignature(payload string, secret string) string { // 验证Creem webhook签名 func verifyCreemSignature(payload string, signature string, secret string) bool { if secret == "" { - log.Printf("Creem webhook secret not set") + logger.LogWarn(context.Background(), fmt.Sprintf("Creem webhook secret 未配置 test_mode=%t signature=%q body=%q", setting.CreemTestMode, signature, payload)) if setting.CreemTestMode { - log.Printf("Skip Creem webhook sign verify in test mode") + logger.LogInfo(context.Background(), fmt.Sprintf("Creem webhook 验签已跳过 reason=test_mode signature=%q body=%q", signature, payload)) return true } return false @@ -66,13 +64,13 @@ type CreemAdaptor struct { } func (*CreemAdaptor) RequestPay(c *gin.Context, req *CreemPayRequest) { - if req.PaymentMethod != PaymentMethodCreem { - c.JSON(200, gin.H{"message": "error", "data": "不支持的支付渠道"}) + if req.PaymentMethod != model.PaymentMethodCreem { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "不支持的支付渠道"}) return } if req.ProductId == "" { - c.JSON(200, gin.H{"message": "error", "data": "请选择产品"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "请选择产品"}) return } @@ -80,8 +78,8 @@ func (*CreemAdaptor) RequestPay(c *gin.Context, req *CreemPayRequest) { var products []CreemProduct err := json.Unmarshal([]byte(setting.CreemProducts), &products) if err != nil { - log.Println("解析Creem产品列表失败", err) - c.JSON(200, gin.H{"message": "error", "data": "产品配置错误"}) + logger.LogError(c.Request.Context(), fmt.Sprintf("Creem 产品配置解析失败 user_id=%d error=%q", c.GetInt("id"), err.Error())) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "产品配置错误"}) return } @@ -95,7 +93,7 @@ func (*CreemAdaptor) RequestPay(c *gin.Context, req *CreemPayRequest) { } if selectedProduct == nil { - c.JSON(200, gin.H{"message": "error", "data": "产品不存在"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "产品不存在"}) return } @@ -112,29 +110,28 @@ func (*CreemAdaptor) RequestPay(c *gin.Context, req *CreemPayRequest) { Amount: selectedProduct.Quota, // 充值额度 Money: selectedProduct.Price, // 支付金额 TradeNo: referenceId, - PaymentMethod: PaymentMethodCreem, + PaymentMethod: model.PaymentMethodCreem, CreateTime: time.Now().Unix(), Status: common.TopUpStatusPending, } err = topUp.Insert() if err != nil { - log.Printf("创建Creem订单失败: %v", err) - c.JSON(200, gin.H{"message": "error", "data": "创建订单失败"}) + logger.LogError(c.Request.Context(), fmt.Sprintf("Creem 创建充值订单失败 user_id=%d trade_no=%s product_id=%s error=%q", id, referenceId, selectedProduct.ProductId, err.Error())) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "创建订单失败"}) return } // 创建支付链接,传入用户邮箱 - checkoutUrl, err := genCreemLink(referenceId, selectedProduct, user.Email, user.Username) + checkoutUrl, err := genCreemLink(c.Request.Context(), referenceId, selectedProduct, user.Email, user.Username) if err != nil { - log.Printf("获取Creem支付链接失败: %v", err) - c.JSON(200, gin.H{"message": "error", "data": "拉起支付失败"}) + logger.LogError(c.Request.Context(), fmt.Sprintf("Creem 创建支付链接失败 user_id=%d trade_no=%s product_id=%s error=%q", id, referenceId, selectedProduct.ProductId, err.Error())) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "拉起支付失败"}) return } - log.Printf("Creem订单创建成功 - 用户ID: %d, 订单号: %s, 产品: %s, 充值额度: %d, 支付金额: %.2f", - id, referenceId, selectedProduct.Name, selectedProduct.Quota, selectedProduct.Price) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Creem 充值订单创建成功 user_id=%d trade_no=%s product_id=%s product_name=%q quota=%d money=%.2f", id, referenceId, selectedProduct.ProductId, selectedProduct.Name, selectedProduct.Quota, selectedProduct.Price)) - c.JSON(200, gin.H{ + c.JSON(http.StatusOK, gin.H{ "message": "success", "data": gin.H{ "checkout_url": checkoutUrl, @@ -149,20 +146,19 @@ func RequestCreemPay(c *gin.Context) { // 读取body内容用于打印,同时保留原始数据供后续使用 bodyBytes, err := io.ReadAll(c.Request.Body) if err != nil { - log.Printf("read creem pay req body err: %v", err) - c.JSON(200, gin.H{"message": "error", "data": "read query error"}) + logger.LogError(c.Request.Context(), fmt.Sprintf("Creem 支付请求读取失败 error=%q", err.Error())) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "read query error"}) return } - // 打印body内容 - log.Printf("creem pay request body: %s", string(bodyBytes)) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Creem 支付请求已收到 user_id=%d body=%q", c.GetInt("id"), string(bodyBytes))) // 重新设置body供后续的ShouldBindJSON使用 c.Request.Body = io.NopCloser(bytes.NewReader(bodyBytes)) err = c.ShouldBindJSON(&req) if err != nil { - c.JSON(200, gin.H{"message": "error", "data": "参数错误"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "参数错误"}) return } creemAdaptor.RequestPay(c, &req) @@ -230,35 +226,37 @@ type CreemWebhookEvent struct { } func CreemWebhook(c *gin.Context) { + if !isCreemWebhookEnabled() { + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Creem webhook 被拒绝 reason=webhook_disabled path=%q client_ip=%s", c.Request.RequestURI, c.ClientIP())) + c.AbortWithStatus(http.StatusForbidden) + return + } + // 读取body内容用于打印,同时保留原始数据供后续使用 bodyBytes, err := io.ReadAll(c.Request.Body) if err != nil { - log.Printf("读取Creem Webhook请求body失败: %v", err) + logger.LogError(c.Request.Context(), fmt.Sprintf("Creem webhook 读取请求体失败 path=%q client_ip=%s error=%q", c.Request.RequestURI, c.ClientIP(), err.Error())) c.AbortWithStatus(http.StatusBadRequest) return } // 获取签名头 signature := c.GetHeader(CreemSignatureHeader) - - // 打印关键信息(避免输出完整敏感payload) - log.Printf("Creem Webhook - URI: %s", c.Request.RequestURI) - if setting.CreemTestMode { - log.Printf("Creem Webhook - Signature: %s , Body: %s", signature, bodyBytes) - } else if signature == "" { - log.Printf("Creem Webhook缺少签名头") + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Creem webhook 收到请求 path=%q client_ip=%s signature=%q body=%q", c.Request.RequestURI, c.ClientIP(), signature, string(bodyBytes))) + if signature == "" { + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Creem webhook 缺少签名 path=%q client_ip=%s body=%q", c.Request.RequestURI, c.ClientIP(), string(bodyBytes))) c.AbortWithStatus(http.StatusUnauthorized) return } // 验证签名 if !verifyCreemSignature(string(bodyBytes), signature, setting.CreemWebhookSecret) { - log.Printf("Creem Webhook签名验证失败") + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Creem webhook 验签失败 path=%q client_ip=%s signature=%q body=%q", c.Request.RequestURI, c.ClientIP(), signature, string(bodyBytes))) c.AbortWithStatus(http.StatusUnauthorized) return } - log.Printf("Creem Webhook签名验证成功") + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Creem webhook 验签成功 path=%q client_ip=%s", c.Request.RequestURI, c.ClientIP())) // 重新设置body供后续的ShouldBindJSON使用 c.Request.Body = io.NopCloser(bytes.NewReader(bodyBytes)) @@ -266,19 +264,19 @@ func CreemWebhook(c *gin.Context) { // 解析新格式的webhook数据 var webhookEvent CreemWebhookEvent if err := c.ShouldBindJSON(&webhookEvent); err != nil { - log.Printf("解析Creem Webhook参数失败: %v", err) + logger.LogError(c.Request.Context(), fmt.Sprintf("Creem webhook 解析失败 path=%q client_ip=%s error=%q body=%q", c.Request.RequestURI, c.ClientIP(), err.Error(), string(bodyBytes))) c.AbortWithStatus(http.StatusBadRequest) return } - log.Printf("Creem Webhook解析成功 - EventType: %s, EventId: %s", webhookEvent.EventType, webhookEvent.Id) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Creem webhook 解析成功 event_type=%s event_id=%s request_id=%s order_id=%s order_status=%s", webhookEvent.EventType, webhookEvent.Id, webhookEvent.Object.RequestId, webhookEvent.Object.Order.Id, webhookEvent.Object.Order.Status)) // 根据事件类型处理不同的webhook switch webhookEvent.EventType { case "checkout.completed": handleCheckoutCompleted(c, &webhookEvent) default: - log.Printf("忽略Creem Webhook事件类型: %s", webhookEvent.EventType) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Creem webhook 忽略事件 event_type=%s event_id=%s", webhookEvent.EventType, webhookEvent.Id)) c.Status(http.StatusOK) } } @@ -287,7 +285,7 @@ func CreemWebhook(c *gin.Context) { func handleCheckoutCompleted(c *gin.Context, event *CreemWebhookEvent) { // 验证订单状态 if event.Object.Order.Status != "paid" { - log.Printf("订单状态不是已支付: %s, 跳过处理", event.Object.Order.Status) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Creem 订单状态未支付,忽略处理 request_id=%s order_id=%s order_status=%s", event.Object.RequestId, event.Object.Order.Id, event.Object.Order.Status)) c.Status(http.StatusOK) return } @@ -295,7 +293,7 @@ func handleCheckoutCompleted(c *gin.Context, event *CreemWebhookEvent) { // 获取引用ID(这是我们创建订单时传递的request_id) referenceId := event.Object.RequestId if referenceId == "" { - log.Println("Creem Webhook缺少request_id字段") + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Creem webhook 缺少 request_id event_id=%s order_id=%s", event.Id, event.Object.Order.Id)) c.AbortWithStatus(http.StatusBadRequest) return } @@ -303,40 +301,35 @@ func handleCheckoutCompleted(c *gin.Context, event *CreemWebhookEvent) { // Try complete subscription order first LockOrder(referenceId) defer UnlockOrder(referenceId) - if err := model.CompleteSubscriptionOrder(referenceId, common.GetJsonString(event)); err == nil { + if err := model.CompleteSubscriptionOrder(referenceId, common.GetJsonString(event), model.PaymentMethodCreem); err == nil { + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Creem 订阅订单处理成功 trade_no=%s creem_order_id=%s", referenceId, event.Object.Order.Id)) c.Status(http.StatusOK) return } else if err != nil && !errors.Is(err, model.ErrSubscriptionOrderNotFound) { - log.Printf("Creem订阅订单处理失败: %s, 订单号: %s", err.Error(), referenceId) + logger.LogError(c.Request.Context(), fmt.Sprintf("Creem 订阅订单处理失败 trade_no=%s creem_order_id=%s error=%q", referenceId, event.Object.Order.Id, err.Error())) c.AbortWithStatus(http.StatusInternalServerError) return } // 验证订单类型,目前只处理一次性付款(充值) if event.Object.Order.Type != "onetime" { - log.Printf("暂不支持的订单类型: %s, 跳过处理", event.Object.Order.Type) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Creem 暂不支持该订单类型,忽略处理 request_id=%s creem_order_id=%s order_type=%s", referenceId, event.Object.Order.Id, event.Object.Order.Type)) c.Status(http.StatusOK) return } - // 记录详细的支付信息 - log.Printf("处理Creem支付完成 - 订单号: %s, Creem订单ID: %s, 支付金额: %d %s, 客户邮箱: , 产品: %s", - referenceId, - event.Object.Order.Id, - event.Object.Order.AmountPaid, - event.Object.Order.Currency, - event.Object.Product.Name) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Creem 支付完成回调 trade_no=%s creem_order_id=%s amount_paid=%d currency=%s product_name=%q customer_email=%q customer_name=%q", referenceId, event.Object.Order.Id, event.Object.Order.AmountPaid, event.Object.Order.Currency, event.Object.Product.Name, event.Object.Customer.Email, event.Object.Customer.Name)) // 查询本地订单确认存在 topUp := model.GetTopUpByTradeNo(referenceId) if topUp == nil { - log.Printf("Creem充值订单不存在: %s", referenceId) + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Creem 充值订单不存在 trade_no=%s creem_order_id=%s", referenceId, event.Object.Order.Id)) c.AbortWithStatus(http.StatusBadRequest) return } if topUp.Status != common.TopUpStatusPending { - log.Printf("Creem充值订单状态错误: %s, 当前状态: %s", referenceId, topUp.Status) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Creem 充值订单状态非 pending,忽略处理 trade_no=%s status=%s creem_order_id=%s", referenceId, topUp.Status, event.Object.Order.Id)) c.Status(http.StatusOK) // 已处理过的订单,返回成功避免重复处理 return } @@ -347,21 +340,20 @@ func handleCheckoutCompleted(c *gin.Context, event *CreemWebhookEvent) { // 防护性检查,确保邮箱和姓名不为空字符串 if customerEmail == "" { - log.Printf("警告:Creem回调中客户邮箱为空 - 订单号: %s", referenceId) + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Creem 回调客户邮箱为空 trade_no=%s creem_order_id=%s", referenceId, event.Object.Order.Id)) } if customerName == "" { - log.Printf("警告:Creem回调中客户姓名为空 - 订单号: %s", referenceId) + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Creem 回调客户姓名为空 trade_no=%s creem_order_id=%s", referenceId, event.Object.Order.Id)) } err := model.RechargeCreem(referenceId, customerEmail, customerName, c.ClientIP()) if err != nil { - log.Printf("Creem充值处理失败: %s, 订单号: %s", err.Error(), referenceId) + logger.LogError(c.Request.Context(), fmt.Sprintf("Creem 充值处理失败 trade_no=%s creem_order_id=%s client_ip=%s error=%q", referenceId, event.Object.Order.Id, c.ClientIP(), err.Error())) c.AbortWithStatus(http.StatusInternalServerError) return } - log.Printf("Creem充值成功 - 订单号: %s, 充值额度: %d, 支付金额: %.2f", - referenceId, topUp.Amount, topUp.Money) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Creem 充值成功 trade_no=%s creem_order_id=%s quota=%d money=%.2f client_ip=%s", referenceId, event.Object.Order.Id, topUp.Amount, topUp.Money, c.ClientIP())) c.Status(http.StatusOK) } @@ -379,7 +371,7 @@ type CreemCheckoutResponse struct { Id string `json:"id"` } -func genCreemLink(referenceId string, product *CreemProduct, email string, username string) (string, error) { +func genCreemLink(ctx context.Context, referenceId string, product *CreemProduct, email string, username string) (string, error) { if setting.CreemApiKey == "" { return "", fmt.Errorf("未配置Creem API密钥") } @@ -388,7 +380,7 @@ func genCreemLink(referenceId string, product *CreemProduct, email string, usern apiUrl := "https://api.creem.io/v1/checkouts" if setting.CreemTestMode { apiUrl = "https://test-api.creem.io/v1/checkouts" - log.Printf("使用Creem测试环境: %s", apiUrl) + logger.LogInfo(ctx, fmt.Sprintf("Creem 使用测试环境 api_url=%s", apiUrl)) } // 构建请求数据,确保包含用户邮箱 @@ -424,8 +416,7 @@ func genCreemLink(referenceId string, product *CreemProduct, email string, usern req.Header.Set("Content-Type", "application/json") req.Header.Set("x-api-key", setting.CreemApiKey) - log.Printf("发送Creem支付请求 - URL: %s, 产品ID: %s, 用户邮箱: %s, 订单号: %s", - apiUrl, product.ProductId, email, referenceId) + logger.LogInfo(ctx, fmt.Sprintf("Creem 支付请求已发送 api_url=%s product_id=%s email=%q trade_no=%s", apiUrl, product.ProductId, email, referenceId)) // 发送请求 client := &http.Client{ @@ -443,7 +434,7 @@ func genCreemLink(referenceId string, product *CreemProduct, email string, usern return "", fmt.Errorf("读取响应失败: %v", err) } - log.Printf("Creem API resp - status code: %d, resp: %s", resp.StatusCode, string(body)) + logger.LogInfo(ctx, fmt.Sprintf("Creem API 响应已收到 trade_no=%s status_code=%d body=%q", referenceId, resp.StatusCode, string(body))) // 检查响应状态 if resp.StatusCode/100 != 2 { @@ -460,6 +451,6 @@ func genCreemLink(referenceId string, product *CreemProduct, email string, usern return "", fmt.Errorf("Creem API resp no checkout url ") } - log.Printf("Creem 支付链接创建成功 - 订单号: %s, 支付链接: %s", referenceId, checkoutResp.CheckoutUrl) + logger.LogInfo(ctx, fmt.Sprintf("Creem 支付链接创建成功 trade_no=%s response_id=%s checkout_url=%q", referenceId, checkoutResp.Id, checkoutResp.CheckoutUrl)) return checkoutResp.CheckoutUrl, nil } diff --git a/controller/topup_epay_guard_test.go b/controller/topup_epay_guard_test.go new file mode 100644 index 00000000..34512665 --- /dev/null +++ b/controller/topup_epay_guard_test.go @@ -0,0 +1,31 @@ +package controller + +import ( + "testing" + + "github.com/QuantumNous/new-api/model" +) + +func TestIsNonEpayPaymentMethodForEpayCallback(t *testing.T) { + testCases := []struct { + name string + paymentMethod string + expectedBlocked bool + }{ + {name: "stripe", paymentMethod: model.PaymentMethodStripe, expectedBlocked: true}, + {name: "creem", paymentMethod: model.PaymentMethodCreem, expectedBlocked: true}, + {name: "waffo", paymentMethod: model.PaymentMethodWaffo, expectedBlocked: true}, + {name: "waffo pancake", paymentMethod: model.PaymentMethodWaffoPancake, expectedBlocked: true}, + {name: "alipay", paymentMethod: "alipay", expectedBlocked: false}, + {name: "wxpay", paymentMethod: "wxpay", expectedBlocked: false}, + {name: "custom epay type", paymentMethod: "custom1", expectedBlocked: false}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if actual := isNonEpayPaymentMethodForEpayCallback(tc.paymentMethod); actual != tc.expectedBlocked { + t.Fatalf("expected blocked=%v, got %v for payment method %q", tc.expectedBlocked, actual, tc.paymentMethod) + } + }) + } +} diff --git a/controller/topup_stripe.go b/controller/topup_stripe.go index bb8e24fa..23ddb3b9 100644 --- a/controller/topup_stripe.go +++ b/controller/topup_stripe.go @@ -1,16 +1,17 @@ package controller import ( + "context" "errors" "fmt" "io" - "log" "net/http" "strconv" "strings" "time" "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/setting" "github.com/QuantumNous/new-api/setting/operation_setting" @@ -23,10 +24,6 @@ import ( "github.com/thanhpk/randstr" ) -const ( - PaymentMethodStripe = "stripe" -) - var stripeAdaptor = &StripeAdaptor{} // StripePayRequest represents a payment request for Stripe checkout. @@ -48,34 +45,34 @@ type StripeAdaptor struct { func (*StripeAdaptor) RequestAmount(c *gin.Context, req *StripePayRequest) { if req.Amount < getStripeMinTopup() { - c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", getStripeMinTopup())}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", getStripeMinTopup())}) return } id := c.GetInt("id") group, err := model.GetUserGroup(id, true) if err != nil { - c.JSON(200, gin.H{"message": "error", "data": "获取用户分组失败"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "获取用户分组失败"}) return } payMoney := getStripePayMoney(float64(req.Amount), group) if payMoney <= 0.01 { - c.JSON(200, gin.H{"message": "error", "data": "充值金额过低"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "充值金额过低"}) return } - c.JSON(200, gin.H{"message": "success", "data": strconv.FormatFloat(payMoney, 'f', 2, 64)}) + c.JSON(http.StatusOK, gin.H{"message": "success", "data": strconv.FormatFloat(payMoney, 'f', 2, 64)}) } func (*StripeAdaptor) RequestPay(c *gin.Context, req *StripePayRequest) { - if req.PaymentMethod != PaymentMethodStripe { - c.JSON(200, gin.H{"message": "error", "data": "不支持的支付渠道"}) + if req.PaymentMethod != model.PaymentMethodStripe { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "不支持的支付渠道"}) return } if req.Amount < getStripeMinTopup() { - c.JSON(200, gin.H{"message": fmt.Sprintf("充值数量不能小于 %d", getStripeMinTopup()), "data": 10}) + c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("充值数量不能小于 %d", getStripeMinTopup()), "data": 10}) return } if req.Amount > 10000 { - c.JSON(200, gin.H{"message": "充值数量不能大于 10000", "data": 10}) + c.JSON(http.StatusOK, gin.H{"message": "充值数量不能大于 10000", "data": 10}) return } @@ -98,8 +95,8 @@ func (*StripeAdaptor) RequestPay(c *gin.Context, req *StripePayRequest) { payLink, err := genStripeLink(referenceId, user.StripeCustomer, user.Email, req.Amount, req.SuccessURL, req.CancelURL) if err != nil { - log.Println("获取Stripe Checkout支付链接失败", err) - c.JSON(200, gin.H{"message": "error", "data": "拉起支付失败"}) + logger.LogError(c.Request.Context(), fmt.Sprintf("Stripe 创建 Checkout Session 失败 user_id=%d trade_no=%s amount=%d error=%q", id, referenceId, req.Amount, err.Error())) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "拉起支付失败"}) return } @@ -108,16 +105,18 @@ func (*StripeAdaptor) RequestPay(c *gin.Context, req *StripePayRequest) { Amount: req.Amount, Money: chargedMoney, TradeNo: referenceId, - PaymentMethod: PaymentMethodStripe, + PaymentMethod: model.PaymentMethodStripe, CreateTime: time.Now().Unix(), Status: common.TopUpStatusPending, } err = topUp.Insert() if err != nil { - c.JSON(200, gin.H{"message": "error", "data": "创建订单失败"}) + logger.LogError(c.Request.Context(), fmt.Sprintf("Stripe 创建充值订单失败 user_id=%d trade_no=%s amount=%d error=%q", id, referenceId, req.Amount, err.Error())) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "创建订单失败"}) return } - c.JSON(200, gin.H{ + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Stripe 充值订单创建成功 user_id=%d trade_no=%s amount=%d money=%.2f", id, referenceId, req.Amount, chargedMoney)) + c.JSON(http.StatusOK, gin.H{ "message": "success", "data": gin.H{ "pay_link": payLink, @@ -129,7 +128,7 @@ func RequestStripeAmount(c *gin.Context) { var req StripePayRequest err := c.ShouldBindJSON(&req) if err != nil { - c.JSON(200, gin.H{"message": "error", "data": "参数错误"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "参数错误"}) return } stripeAdaptor.RequestAmount(c, &req) @@ -139,90 +138,93 @@ func RequestStripePay(c *gin.Context) { var req StripePayRequest err := c.ShouldBindJSON(&req) if err != nil { - c.JSON(200, gin.H{"message": "error", "data": "参数错误"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "参数错误"}) return } stripeAdaptor.RequestPay(c, &req) } func StripeWebhook(c *gin.Context) { - if setting.StripeWebhookSecret == "" { - log.Println("Stripe Webhook Secret 未配置,拒绝处理") + ctx := c.Request.Context() + if !isStripeWebhookEnabled() { + logger.LogWarn(ctx, fmt.Sprintf("Stripe webhook 被拒绝 reason=webhook_disabled path=%q client_ip=%s", c.Request.RequestURI, c.ClientIP())) c.AbortWithStatus(http.StatusForbidden) return } payload, err := io.ReadAll(c.Request.Body) if err != nil { - log.Printf("解析Stripe Webhook参数失败: %v\n", err) + logger.LogError(ctx, fmt.Sprintf("Stripe webhook 读取请求体失败 path=%q client_ip=%s error=%q", c.Request.RequestURI, c.ClientIP(), err.Error())) c.AbortWithStatus(http.StatusServiceUnavailable) return } signature := c.GetHeader("Stripe-Signature") + logger.LogInfo(ctx, fmt.Sprintf("Stripe webhook 收到请求 path=%q client_ip=%s signature=%q body=%q", c.Request.RequestURI, c.ClientIP(), signature, string(payload))) event, err := webhook.ConstructEventWithOptions(payload, signature, setting.StripeWebhookSecret, webhook.ConstructEventOptions{ IgnoreAPIVersionMismatch: true, }) if err != nil { - log.Printf("Stripe Webhook验签失败: %v\n", err) + logger.LogWarn(ctx, fmt.Sprintf("Stripe webhook 验签失败 path=%q client_ip=%s error=%q", c.Request.RequestURI, c.ClientIP(), err.Error())) c.AbortWithStatus(http.StatusBadRequest) return } callerIp := c.ClientIP() + logger.LogInfo(ctx, fmt.Sprintf("Stripe webhook 验签成功 event_type=%s client_ip=%s path=%q", string(event.Type), callerIp, c.Request.RequestURI)) switch event.Type { case stripe.EventTypeCheckoutSessionCompleted: - sessionCompleted(event, callerIp) + sessionCompleted(ctx, event, callerIp) case stripe.EventTypeCheckoutSessionExpired: - sessionExpired(event) + sessionExpired(ctx, event) case stripe.EventTypeCheckoutSessionAsyncPaymentSucceeded: - sessionAsyncPaymentSucceeded(event, callerIp) + sessionAsyncPaymentSucceeded(ctx, event, callerIp) case stripe.EventTypeCheckoutSessionAsyncPaymentFailed: - sessionAsyncPaymentFailed(event, callerIp) + sessionAsyncPaymentFailed(ctx, event, callerIp) default: - log.Printf("不支持的Stripe Webhook事件类型: %s\n", event.Type) + logger.LogInfo(ctx, fmt.Sprintf("Stripe webhook 忽略事件 event_type=%s client_ip=%s", string(event.Type), callerIp)) } c.Status(http.StatusOK) } -func sessionCompleted(event stripe.Event, callerIp string) { +func sessionCompleted(ctx context.Context, event stripe.Event, callerIp string) { customerId := event.GetObjectValue("customer") referenceId := event.GetObjectValue("client_reference_id") status := event.GetObjectValue("status") if "complete" != status { - log.Println("错误的Stripe Checkout完成状态:", status, ",", referenceId) + logger.LogWarn(ctx, fmt.Sprintf("Stripe checkout.completed 状态异常,忽略处理 trade_no=%s status=%s client_ip=%s", referenceId, status, callerIp)) return } paymentStatus := event.GetObjectValue("payment_status") if paymentStatus != "paid" { - log.Printf("Stripe Checkout 支付尚未完成,payment_status: %s, ref: %s(等待异步支付结果)", paymentStatus, referenceId) + logger.LogInfo(ctx, fmt.Sprintf("Stripe Checkout 支付未完成,等待异步结果 trade_no=%s payment_status=%s client_ip=%s", referenceId, paymentStatus, callerIp)) return } - fulfillOrder(event, referenceId, customerId, callerIp) + fulfillOrder(ctx, event, referenceId, customerId, callerIp) } // sessionAsyncPaymentSucceeded handles delayed payment methods (bank transfer, SEPA, etc.) // that confirm payment after the checkout session completes. -func sessionAsyncPaymentSucceeded(event stripe.Event, callerIp string) { +func sessionAsyncPaymentSucceeded(ctx context.Context, event stripe.Event, callerIp string) { customerId := event.GetObjectValue("customer") referenceId := event.GetObjectValue("client_reference_id") - log.Printf("Stripe 异步支付成功: %s", referenceId) + logger.LogInfo(ctx, fmt.Sprintf("Stripe 异步支付成功 trade_no=%s client_ip=%s", referenceId, callerIp)) - fulfillOrder(event, referenceId, customerId, callerIp) + fulfillOrder(ctx, event, referenceId, customerId, callerIp) } // sessionAsyncPaymentFailed marks orders as failed when delayed payment methods // ultimately fail (e.g. bank transfer not received, SEPA rejected). -func sessionAsyncPaymentFailed(event stripe.Event, callerIp string) { +func sessionAsyncPaymentFailed(ctx context.Context, event stripe.Event, callerIp string) { referenceId := event.GetObjectValue("client_reference_id") - log.Printf("Stripe 异步支付失败: %s", referenceId) + logger.LogWarn(ctx, fmt.Sprintf("Stripe 异步支付失败 trade_no=%s client_ip=%s", referenceId, callerIp)) if len(referenceId) == 0 { - log.Println("异步支付失败事件未提供支付单号") + logger.LogWarn(ctx, fmt.Sprintf("Stripe 异步支付失败事件缺少订单号 client_ip=%s", callerIp)) return } @@ -231,32 +233,32 @@ func sessionAsyncPaymentFailed(event stripe.Event, callerIp string) { topUp := model.GetTopUpByTradeNo(referenceId) if topUp == nil { - log.Println("异步支付失败,充值订单不存在:", referenceId) + logger.LogWarn(ctx, fmt.Sprintf("Stripe 异步支付失败但本地订单不存在 trade_no=%s client_ip=%s", referenceId, callerIp)) return } - if topUp.PaymentMethod != PaymentMethodStripe { - log.Printf("异步支付失败,订单支付方式不匹配: %s, ref: %s", topUp.PaymentMethod, referenceId) + if topUp.PaymentMethod != model.PaymentMethodStripe { + logger.LogWarn(ctx, fmt.Sprintf("Stripe 异步支付失败但订单支付方式不匹配 trade_no=%s payment_method=%s client_ip=%s", referenceId, topUp.PaymentMethod, callerIp)) return } if topUp.Status != common.TopUpStatusPending { - log.Printf("异步支付失败,订单状态非pending: %s, ref: %s", topUp.Status, referenceId) + logger.LogInfo(ctx, fmt.Sprintf("Stripe 异步支付失败但订单状态非 pending,忽略处理 trade_no=%s status=%s client_ip=%s", referenceId, topUp.Status, callerIp)) return } topUp.Status = common.TopUpStatusFailed if err := topUp.Update(); err != nil { - log.Printf("标记充值订单失败出错: %v, ref: %s", err, referenceId) + logger.LogError(ctx, fmt.Sprintf("Stripe 标记充值订单失败状态失败 trade_no=%s client_ip=%s error=%q", referenceId, callerIp, err.Error())) return } - log.Printf("充值订单已标记为失败: %s", referenceId) + logger.LogInfo(ctx, fmt.Sprintf("Stripe 充值订单已标记为失败 trade_no=%s client_ip=%s", referenceId, callerIp)) } // fulfillOrder is the shared logic for crediting quota after payment is confirmed. -func fulfillOrder(event stripe.Event, referenceId string, customerId string, callerIp string) { +func fulfillOrder(ctx context.Context, event stripe.Event, referenceId string, customerId string, callerIp string) { if len(referenceId) == 0 { - log.Println("未提供支付单号") + logger.LogWarn(ctx, fmt.Sprintf("Stripe 完成订单时缺少订单号 client_ip=%s", callerIp)) return } @@ -268,65 +270,60 @@ func fulfillOrder(event stripe.Event, referenceId string, customerId string, cal "currency": strings.ToUpper(event.GetObjectValue("currency")), "event_type": string(event.Type), } - if err := model.CompleteSubscriptionOrder(referenceId, common.GetJsonString(payload)); err == nil { + if err := model.CompleteSubscriptionOrder(referenceId, common.GetJsonString(payload), model.PaymentMethodStripe); err == nil { + logger.LogInfo(ctx, fmt.Sprintf("Stripe 订阅订单处理成功 trade_no=%s event_type=%s client_ip=%s", referenceId, string(event.Type), callerIp)) return } else if err != nil && !errors.Is(err, model.ErrSubscriptionOrderNotFound) { - log.Println("complete subscription order failed:", err.Error(), referenceId) + logger.LogError(ctx, fmt.Sprintf("Stripe 订阅订单处理失败 trade_no=%s event_type=%s client_ip=%s error=%q", referenceId, string(event.Type), callerIp, err.Error())) return } err := model.Recharge(referenceId, customerId, callerIp) if err != nil { - log.Println(err.Error(), referenceId) + logger.LogError(ctx, fmt.Sprintf("Stripe 充值处理失败 trade_no=%s event_type=%s client_ip=%s error=%q", referenceId, string(event.Type), callerIp, err.Error())) return } total, _ := strconv.ParseFloat(event.GetObjectValue("amount_total"), 64) currency := strings.ToUpper(event.GetObjectValue("currency")) - log.Printf("收到款项:%s, %.2f(%s)", referenceId, total/100, currency) + logger.LogInfo(ctx, fmt.Sprintf("Stripe 充值成功 trade_no=%s amount_total=%.2f currency=%s event_type=%s client_ip=%s", referenceId, total/100, currency, string(event.Type), callerIp)) } -func sessionExpired(event stripe.Event) { +func sessionExpired(ctx context.Context, event stripe.Event) { referenceId := event.GetObjectValue("client_reference_id") status := event.GetObjectValue("status") if "expired" != status { - log.Println("错误的Stripe Checkout过期状态:", status, ",", referenceId) + logger.LogWarn(ctx, fmt.Sprintf("Stripe checkout.expired 状态异常,忽略处理 trade_no=%s status=%s", referenceId, status)) return } if len(referenceId) == 0 { - log.Println("未提供支付单号") + logger.LogWarn(ctx, "Stripe checkout.expired 缺少订单号") return } // Subscription order expiration LockOrder(referenceId) defer UnlockOrder(referenceId) - if err := model.ExpireSubscriptionOrder(referenceId); err == nil { + if err := model.ExpireSubscriptionOrder(referenceId, model.PaymentMethodStripe); err == nil { + logger.LogInfo(ctx, fmt.Sprintf("Stripe 订阅订单已过期 trade_no=%s", referenceId)) return } else if err != nil && !errors.Is(err, model.ErrSubscriptionOrderNotFound) { - log.Println("过期订阅订单失败", referenceId, ", err:", err.Error()) + logger.LogError(ctx, fmt.Sprintf("Stripe 订阅订单过期处理失败 trade_no=%s error=%q", referenceId, err.Error())) return } - topUp := model.GetTopUpByTradeNo(referenceId) - if topUp == nil { - log.Println("充值订单不存在", referenceId) + err := model.UpdatePendingTopUpStatus(referenceId, model.PaymentMethodStripe, common.TopUpStatusExpired) + if errors.Is(err, model.ErrTopUpNotFound) { + logger.LogWarn(ctx, fmt.Sprintf("Stripe 充值订单不存在,无法标记过期 trade_no=%s", referenceId)) return } - - if topUp.Status != common.TopUpStatusPending { - log.Println("充值订单状态错误", referenceId) - } - - topUp.Status = common.TopUpStatusExpired - err := topUp.Update() if err != nil { - log.Println("过期充值订单失败", referenceId, ", err:", err.Error()) + logger.LogError(ctx, fmt.Sprintf("Stripe 充值订单过期处理失败 trade_no=%s error=%q", referenceId, err.Error())) return } - log.Println("充值订单已过期", referenceId) + logger.LogInfo(ctx, fmt.Sprintf("Stripe 充值订单已过期 trade_no=%s", referenceId)) } // genStripeLink generates a Stripe Checkout session URL for payment. diff --git a/controller/topup_waffo.go b/controller/topup_waffo.go index f55baefb..c0068062 100644 --- a/controller/topup_waffo.go +++ b/controller/topup_waffo.go @@ -1,14 +1,15 @@ package controller import ( + "errors" "fmt" "io" - "log" "net/http" "strconv" "time" "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting" @@ -99,28 +100,57 @@ type WaffoPayRequest struct { PayMethodName string `json:"pay_method_name"` // Deprecated: 兼容旧前端,优先使用 pay_method_index } +func RequestWaffoAmount(c *gin.Context) { + var req WaffoPayRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "参数错误"}) + return + } + + waffoMinTopup := int64(setting.WaffoMinTopUp) + if req.Amount < waffoMinTopup { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", waffoMinTopup)}) + return + } + + id := c.GetInt("id") + group, err := model.GetUserGroup(id, true) + if err != nil { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "获取用户分组失败"}) + return + } + + payMoney := getWaffoPayMoney(float64(req.Amount), group) + if payMoney <= 0.01 { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "充值金额过低"}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "success", "data": strconv.FormatFloat(payMoney, 'f', 2, 64)}) +} + // RequestWaffoPay 创建 Waffo 支付订单 func RequestWaffoPay(c *gin.Context) { if !setting.WaffoEnabled { - c.JSON(200, gin.H{"message": "error", "data": "Waffo 支付未启用"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "Waffo 支付未启用"}) return } var req WaffoPayRequest if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(200, gin.H{"message": "error", "data": "参数错误"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "参数错误"}) return } waffoMinTopup := int64(setting.WaffoMinTopUp) if req.Amount < waffoMinTopup { - c.JSON(200, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", waffoMinTopup)}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", waffoMinTopup)}) return } id := c.GetInt("id") user, err := model.GetUserById(id, false) if err != nil || user == nil { - c.JSON(200, gin.H{"message": "error", "data": "用户不存在"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "用户不存在"}) return } @@ -131,8 +161,8 @@ func RequestWaffoPay(c *gin.Context) { // 新协议:按索引查找 idx := *req.PayMethodIndex if idx < 0 || idx >= len(methods) { - log.Printf("Waffo 无效的支付方式索引: %d, UserId=%d, 可用范围: [0, %d)", idx, id, len(methods)) - c.JSON(200, gin.H{"message": "error", "data": "不支持的支付方式"}) + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Waffo 支付方式索引无效 user_id=%d pay_method_index=%d method_count=%d", id, idx, len(methods))) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "不支持的支付方式"}) return } resolvedPayMethodType = methods[idx].PayMethodType @@ -149,8 +179,8 @@ func RequestWaffoPay(c *gin.Context) { } } if !valid { - log.Printf("Waffo 无效的支付方式: PayMethodType=%s, PayMethodName=%s, UserId=%d", req.PayMethodType, req.PayMethodName, id) - c.JSON(200, gin.H{"message": "error", "data": "不支持的支付方式"}) + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Waffo 支付方式无效 user_id=%d pay_method_type=%s pay_method_name=%q", id, req.PayMethodType, req.PayMethodName)) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "不支持的支付方式"}) return } } @@ -159,7 +189,7 @@ func RequestWaffoPay(c *gin.Context) { group, _ := model.GetUserGroup(id, true) payMoney := getWaffoPayMoney(float64(req.Amount), group) if payMoney < 0.01 { - c.JSON(200, gin.H{"message": "error", "data": "充值金额过低"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "充值金额过低"}) return } @@ -182,22 +212,22 @@ func RequestWaffoPay(c *gin.Context) { Amount: amount, Money: payMoney, TradeNo: merchantOrderId, - PaymentMethod: "waffo", + PaymentMethod: model.PaymentMethodWaffo, CreateTime: time.Now().Unix(), Status: common.TopUpStatusPending, } if err := topUp.Insert(); err != nil { - log.Printf("Waffo 创建本地订单失败: %v", err) - c.JSON(200, gin.H{"message": "error", "data": "创建订单失败"}) + logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo 创建充值订单失败 user_id=%d trade_no=%s amount=%d error=%q", id, merchantOrderId, req.Amount, err.Error())) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "创建订单失败"}) return } sdk, err := getWaffoSDK() if err != nil { - log.Printf("Waffo SDK 初始化失败: %v", err) + logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo SDK 初始化失败 user_id=%d trade_no=%s error=%q", id, merchantOrderId, err.Error())) topUp.Status = common.TopUpStatusFailed _ = topUp.Update() - c.JSON(200, gin.H{"message": "error", "data": "支付配置错误"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "支付配置错误"}) return } @@ -238,29 +268,29 @@ func RequestWaffoPay(c *gin.Context) { } resp, err := sdk.Order().Create(c.Request.Context(), createParams, nil) if err != nil { - log.Printf("Waffo 创建订单失败: %v", err) + logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo 创建订单失败 user_id=%d trade_no=%s error=%q", id, merchantOrderId, err.Error())) topUp.Status = common.TopUpStatusFailed _ = topUp.Update() - c.JSON(200, gin.H{"message": "error", "data": "拉起支付失败"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "拉起支付失败"}) return } if !resp.IsSuccess() { - log.Printf("Waffo 创建订单业务失败: [%s] %s, 完整响应: %+v", resp.Code, resp.Message, resp) + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Waffo 创建订单业务失败 user_id=%d trade_no=%s code=%s message=%q response=%q", id, merchantOrderId, resp.Code, resp.Message, common.GetJsonString(resp))) topUp.Status = common.TopUpStatusFailed _ = topUp.Update() - c.JSON(200, gin.H{"message": "error", "data": "拉起支付失败"}) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "拉起支付失败"}) return } orderData := resp.GetData() - log.Printf("Waffo 订单创建成功 - 用户: %d, 订单: %s, 金额: %.2f", id, merchantOrderId, payMoney) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Waffo 充值订单创建成功 user_id=%d trade_no=%s amount=%d money=%.2f pay_method_type=%s pay_method_name=%q", id, merchantOrderId, req.Amount, payMoney, resolvedPayMethodType, resolvedPayMethodName)) paymentUrl := orderData.FetchRedirectURL() if paymentUrl == "" { paymentUrl = orderData.OrderAction } - c.JSON(200, gin.H{ + c.JSON(http.StatusOK, gin.H{ "message": "success", "data": gin.H{ "payment_url": paymentUrl, @@ -287,16 +317,22 @@ type webhookSubscriptionInfo struct { // WaffoWebhook 处理 Waffo 回调通知(支付/退款/订阅) func WaffoWebhook(c *gin.Context) { + if !isWaffoWebhookEnabled() { + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Waffo webhook 被拒绝 reason=webhook_disabled path=%q client_ip=%s", c.Request.RequestURI, c.ClientIP())) + c.AbortWithStatus(http.StatusForbidden) + return + } + bodyBytes, err := io.ReadAll(c.Request.Body) if err != nil { - log.Printf("Waffo Webhook 读取 body 失败: %v", err) + logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo webhook 读取请求体失败 path=%q client_ip=%s error=%q", c.Request.RequestURI, c.ClientIP(), err.Error())) c.AbortWithStatus(http.StatusBadRequest) return } sdk, err := getWaffoSDK() if err != nil { - log.Printf("Waffo Webhook SDK 初始化失败: %v", err) + logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo webhook SDK 初始化失败 path=%q client_ip=%s error=%q", c.Request.RequestURI, c.ClientIP(), err.Error())) c.AbortWithStatus(http.StatusInternalServerError) return } @@ -304,17 +340,18 @@ func WaffoWebhook(c *gin.Context) { wh := sdk.Webhook() bodyStr := string(bodyBytes) signature := c.GetHeader("X-SIGNATURE") + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Waffo webhook 收到请求 path=%q client_ip=%s signature=%q body=%q", c.Request.RequestURI, c.ClientIP(), signature, bodyStr)) // 验证请求签名 if !wh.VerifySignature(bodyStr, signature) { - log.Printf("Waffo webhook 签名验证失败") + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Waffo webhook 验签失败 path=%q client_ip=%s signature=%q body=%q", c.Request.RequestURI, c.ClientIP(), signature, bodyStr)) c.AbortWithStatus(http.StatusBadRequest) return } var event core.WebhookEvent if err := common.Unmarshal(bodyBytes, &event); err != nil { - log.Printf("Waffo Webhook 解析失败: %v", err) + logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo webhook 解析失败 path=%q client_ip=%s error=%q body=%q", c.Request.RequestURI, c.ClientIP(), err.Error(), bodyStr)) sendWaffoWebhookResponse(c, wh, false, "invalid payload") return } @@ -324,14 +361,14 @@ func WaffoWebhook(c *gin.Context) { // 解析为扩展类型,区分普通支付和订阅支付 var payload webhookPayloadWithSubInfo if err := common.Unmarshal(bodyBytes, &payload); err != nil { + logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo 支付回调载荷解析失败 event_type=%s client_ip=%s error=%q body=%q", event.EventType, c.ClientIP(), err.Error(), bodyStr)) sendWaffoWebhookResponse(c, wh, false, "invalid payment payload") return } - log.Printf("Waffo Webhook - EventType: %s, MerchantOrderId: %s, OrderStatus: %s", - event.EventType, payload.Result.MerchantOrderID, payload.Result.OrderStatus) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Waffo webhook 验签并解析成功 event_type=%s merchant_order_id=%s order_status=%s client_ip=%s", event.EventType, payload.Result.MerchantOrderID, payload.Result.OrderStatus, c.ClientIP())) handleWaffoPayment(c, wh, &payload.Result.PaymentNotificationResult) default: - log.Printf("Waffo Webhook 未知事件: %s", event.EventType) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Waffo webhook 忽略事件 event_type=%s client_ip=%s", event.EventType, c.ClientIP())) sendWaffoWebhookResponse(c, wh, true, "") } } @@ -339,13 +376,13 @@ func WaffoWebhook(c *gin.Context) { // handleWaffoPayment 处理支付完成通知 func handleWaffoPayment(c *gin.Context, wh *core.WebhookHandler, result *core.PaymentNotificationResult) { if result.OrderStatus != "PAY_SUCCESS" { - log.Printf("Waffo 订单状态非成功: %s, 订单: %s", result.OrderStatus, result.MerchantOrderID) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Waffo 订单状态非成功,忽略充值 trade_no=%s order_status=%s client_ip=%s", result.MerchantOrderID, result.OrderStatus, c.ClientIP())) // 终态失败订单标记为 failed,避免永远停在 pending if result.MerchantOrderID != "" { - if topUp := model.GetTopUpByTradeNo(result.MerchantOrderID); topUp != nil && - topUp.Status == common.TopUpStatusPending { - topUp.Status = common.TopUpStatusFailed - _ = topUp.Update() + if err := model.UpdatePendingTopUpStatus(result.MerchantOrderID, model.PaymentMethodWaffo, common.TopUpStatusFailed); err != nil && + !errors.Is(err, model.ErrTopUpNotFound) && + !errors.Is(err, model.ErrTopUpStatusInvalid) { + logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo 标记失败订单状态失败 trade_no=%s error=%q", result.MerchantOrderID, err.Error())) } } sendWaffoWebhookResponse(c, wh, true, "") @@ -358,12 +395,12 @@ func handleWaffoPayment(c *gin.Context, wh *core.WebhookHandler, result *core.Pa defer UnlockOrder(merchantOrderId) if err := model.RechargeWaffo(merchantOrderId, c.ClientIP()); err != nil { - log.Printf("Waffo 充值处理失败: %v, 订单: %s", err, merchantOrderId) + logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo 充值处理失败 trade_no=%s client_ip=%s error=%q", merchantOrderId, c.ClientIP(), err.Error())) sendWaffoWebhookResponse(c, wh, false, err.Error()) return } - log.Printf("Waffo 充值成功 - 订单: %s", merchantOrderId) + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Waffo 充值成功 trade_no=%s client_ip=%s", merchantOrderId, c.ClientIP())) sendWaffoWebhookResponse(c, wh, true, "") } diff --git a/controller/topup_waffo_pancake.go b/controller/topup_waffo_pancake.go new file mode 100644 index 00000000..81515a56 --- /dev/null +++ b/controller/topup_waffo_pancake.go @@ -0,0 +1,259 @@ +package controller + +import ( + "fmt" + "io" + "net/http" + "strings" + "time" + + "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/logger" + "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/service" + "github.com/QuantumNous/new-api/setting" + "github.com/QuantumNous/new-api/setting/operation_setting" + "github.com/QuantumNous/new-api/setting/system_setting" + "github.com/gin-gonic/gin" + "github.com/shopspring/decimal" + "github.com/thanhpk/randstr" +) + +type WaffoPancakePayRequest struct { + Amount int64 `json:"amount"` +} + +func RequestWaffoPancakeAmount(c *gin.Context) { + var req WaffoPancakePayRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "参数错误"}) + return + } + + if req.Amount < int64(setting.WaffoPancakeMinTopUp) { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", setting.WaffoPancakeMinTopUp)}) + return + } + + id := c.GetInt("id") + group, err := model.GetUserGroup(id, true) + if err != nil { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "获取用户分组失败"}) + return + } + + payMoney := getWaffoPancakePayMoney(req.Amount, group) + if payMoney <= 0.01 { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "充值金额过低"}) + return + } + + c.JSON(http.StatusOK, gin.H{"message": "success", "data": fmt.Sprintf("%.2f", payMoney)}) +} + +func getWaffoPancakePayMoney(amount int64, group string) float64 { + dAmount := decimal.NewFromInt(amount) + if operation_setting.GetQuotaDisplayType() == operation_setting.QuotaDisplayTypeTokens { + dAmount = dAmount.Div(decimal.NewFromFloat(common.QuotaPerUnit)) + } + + topupGroupRatio := common.GetTopupGroupRatio(group) + if topupGroupRatio == 0 { + topupGroupRatio = 1 + } + + discount := 1.0 + if ds, ok := operation_setting.GetPaymentSetting().AmountDiscount[int(amount)]; ok && ds > 0 { + discount = ds + } + + payMoney := dAmount. + Mul(decimal.NewFromFloat(setting.WaffoPancakeUnitPrice)). + Mul(decimal.NewFromFloat(topupGroupRatio)). + Mul(decimal.NewFromFloat(discount)) + + return payMoney.InexactFloat64() +} + +func normalizeWaffoPancakeTopUpAmount(amount int64) int64 { + if operation_setting.GetQuotaDisplayType() != operation_setting.QuotaDisplayTypeTokens { + return amount + } + + normalized := decimal.NewFromInt(amount). + Div(decimal.NewFromFloat(common.QuotaPerUnit)). + IntPart() + if normalized < 1 { + return 1 + } + return normalized +} + +func formatWaffoPancakeAmount(payMoney float64) string { + return decimal.NewFromFloat(payMoney).StringFixed(2) +} + +func getWaffoPancakeBuyerEmail(user *model.User) string { + if user != nil && strings.TrimSpace(user.Email) != "" { + return user.Email + } + if user != nil { + return fmt.Sprintf("%d@new-api.local", user.Id) + } + return "" +} + +func getWaffoPancakeReturnURL() string { + if strings.TrimSpace(setting.WaffoPancakeReturnURL) != "" { + return setting.WaffoPancakeReturnURL + } + return strings.TrimRight(system_setting.ServerAddress, "/") + "/console/topup?show_history=true" +} + +func RequestWaffoPancakePay(c *gin.Context) { + if !setting.WaffoPancakeEnabled { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "Waffo Pancake 支付未启用"}) + return + } + currentWebhookKey := setting.WaffoPancakeWebhookPublicKey + if setting.WaffoPancakeSandbox { + currentWebhookKey = setting.WaffoPancakeWebhookTestKey + } + if strings.TrimSpace(setting.WaffoPancakeMerchantID) == "" || + strings.TrimSpace(setting.WaffoPancakePrivateKey) == "" || + strings.TrimSpace(currentWebhookKey) == "" || + strings.TrimSpace(setting.WaffoPancakeStoreID) == "" || + strings.TrimSpace(setting.WaffoPancakeProductID) == "" { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "Waffo Pancake 配置不完整"}) + return + } + + var req WaffoPancakePayRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "参数错误"}) + return + } + if req.Amount < int64(setting.WaffoPancakeMinTopUp) { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": fmt.Sprintf("充值数量不能小于 %d", setting.WaffoPancakeMinTopUp)}) + return + } + + id := c.GetInt("id") + user, err := model.GetUserById(id, false) + if err != nil || user == nil { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "用户不存在"}) + return + } + + group, err := model.GetUserGroup(id, true) + if err != nil { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "获取用户分组失败"}) + return + } + + payMoney := getWaffoPancakePayMoney(req.Amount, group) + if payMoney < 0.01 { + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "充值金额过低"}) + return + } + + tradeNo := fmt.Sprintf("WAFFO_PANCAKE-%d-%d-%s", id, time.Now().UnixMilli(), randstr.String(6)) + topUp := &model.TopUp{ + UserId: id, + Amount: normalizeWaffoPancakeTopUpAmount(req.Amount), + Money: payMoney, + TradeNo: tradeNo, + PaymentMethod: model.PaymentMethodWaffoPancake, + CreateTime: time.Now().Unix(), + Status: common.TopUpStatusPending, + } + if err := topUp.Insert(); err != nil { + logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo Pancake 创建充值订单失败 user_id=%d trade_no=%s amount=%d error=%q", id, tradeNo, req.Amount, err.Error())) + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "创建订单失败"}) + return + } + + expiresInSeconds := 45 * 60 + session, err := service.CreateWaffoPancakeCheckoutSession(c.Request.Context(), &service.WaffoPancakeCreateSessionParams{ + StoreID: setting.WaffoPancakeStoreID, + ProductID: setting.WaffoPancakeProductID, + ProductType: "onetime", + Currency: strings.ToUpper(strings.TrimSpace(setting.WaffoPancakeCurrency)), + PriceSnapshot: &service.WaffoPancakePriceSnapshot{ + Amount: formatWaffoPancakeAmount(payMoney), + TaxIncluded: false, + TaxCategory: "saas", + }, + BuyerEmail: getWaffoPancakeBuyerEmail(user), + SuccessURL: getWaffoPancakeReturnURL(), + ExpiresInSeconds: &expiresInSeconds, + }) + if err != nil { + logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo Pancake 创建结账会话失败 user_id=%d trade_no=%s error=%q", id, tradeNo, err.Error())) + topUp.Status = common.TopUpStatusFailed + _ = topUp.Update() + c.JSON(http.StatusOK, gin.H{"message": "error", "data": "拉起支付失败"}) + return + } + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Waffo Pancake 充值订单创建成功 user_id=%d trade_no=%s session_id=%s amount=%d money=%.2f", id, tradeNo, session.SessionID, req.Amount, payMoney)) + + c.JSON(http.StatusOK, gin.H{ + "message": "success", + "data": gin.H{ + "checkout_url": session.CheckoutURL, + "session_id": session.SessionID, + "expires_at": session.ExpiresAt, + "order_id": tradeNo, + }, + }) +} + +func WaffoPancakeWebhook(c *gin.Context) { + if !isWaffoPancakeWebhookEnabled() { + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Waffo Pancake webhook 被拒绝 reason=webhook_disabled path=%q client_ip=%s", c.Request.RequestURI, c.ClientIP())) + c.String(http.StatusForbidden, "webhook disabled") + return + } + + bodyBytes, err := io.ReadAll(c.Request.Body) + if err != nil { + logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo Pancake webhook 读取请求体失败 path=%q client_ip=%s error=%q", c.Request.RequestURI, c.ClientIP(), err.Error())) + c.String(http.StatusBadRequest, "bad request") + return + } + + signature := c.GetHeader("X-Waffo-Signature") + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Waffo Pancake webhook 收到请求 path=%q client_ip=%s signature=%q body=%q", c.Request.RequestURI, c.ClientIP(), signature, string(bodyBytes))) + + event, err := service.VerifyConfiguredWaffoPancakeWebhook(string(bodyBytes), signature) + if err != nil { + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Waffo Pancake webhook 验签失败 path=%q client_ip=%s signature=%q body=%q error=%q", c.Request.RequestURI, c.ClientIP(), signature, string(bodyBytes), err.Error())) + c.String(http.StatusUnauthorized, "invalid signature") + return + } + + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Waffo Pancake webhook 验签成功 event_type=%s event_id=%s order_id=%s client_ip=%s", event.NormalizedEventType(), event.ID, event.Data.OrderID, c.ClientIP())) + if event.NormalizedEventType() != "order.completed" { + c.String(http.StatusOK, "OK") + return + } + + tradeNo, err := service.ResolveWaffoPancakeTradeNo(event) + if err != nil { + logger.LogWarn(c.Request.Context(), fmt.Sprintf("Waffo Pancake webhook 订单号映射失败 event_id=%s order_id=%s error=%q", event.ID, event.Data.OrderID, err.Error())) + c.String(http.StatusOK, "OK") + return + } + + LockOrder(tradeNo) + defer UnlockOrder(tradeNo) + + if err := model.RechargeWaffoPancake(tradeNo); err != nil { + logger.LogError(c.Request.Context(), fmt.Sprintf("Waffo Pancake 充值处理失败 trade_no=%s event_id=%s order_id=%s client_ip=%s error=%q", tradeNo, event.ID, event.Data.OrderID, c.ClientIP(), err.Error())) + c.String(http.StatusInternalServerError, "retry") + return + } + + logger.LogInfo(c.Request.Context(), fmt.Sprintf("Waffo Pancake 充值成功 trade_no=%s event_id=%s order_id=%s client_ip=%s", tradeNo, event.ID, event.Data.OrderID, c.ClientIP())) + c.String(http.StatusOK, "OK") +} diff --git a/controller/topup_waffo_pancake_test.go b/controller/topup_waffo_pancake_test.go new file mode 100644 index 00000000..483dd7b7 --- /dev/null +++ b/controller/topup_waffo_pancake_test.go @@ -0,0 +1,91 @@ +package controller + +import ( + "testing" + + "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/setting" + "github.com/QuantumNous/new-api/setting/operation_setting" + "github.com/stretchr/testify/require" +) + +func TestFormatWaffoPancakeAmount_UsesDisplayPriceString(t *testing.T) { + testCases := []struct { + name string + amount float64 + expected string + }{ + {name: "whole amount", amount: 29, expected: "29.00"}, + {name: "decimal amount", amount: 29.9, expected: "29.90"}, + {name: "round half up to cents", amount: 29.999, expected: "30.00"}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.expected, formatWaffoPancakeAmount(tc.amount)) + }) + } +} + +func TestGetWaffoPancakePayMoney(t *testing.T) { + originalUnitPrice := setting.WaffoPancakeUnitPrice + originalQuotaDisplayType := operation_setting.GetGeneralSetting().QuotaDisplayType + originalDiscounts := make(map[int]float64, len(operation_setting.GetPaymentSetting().AmountDiscount)) + for k, v := range operation_setting.GetPaymentSetting().AmountDiscount { + originalDiscounts[k] = v + } + originalTopupGroupRatio := common.TopupGroupRatio2JSONString() + + t.Cleanup(func() { + setting.WaffoPancakeUnitPrice = originalUnitPrice + operation_setting.GetGeneralSetting().QuotaDisplayType = originalQuotaDisplayType + operation_setting.GetPaymentSetting().AmountDiscount = originalDiscounts + require.NoError(t, common.UpdateTopupGroupRatioByJSONString(originalTopupGroupRatio)) + }) + + setting.WaffoPancakeUnitPrice = 2.5 + operation_setting.GetPaymentSetting().AmountDiscount = map[int]float64{ + 10: 0.8, + int(common.QuotaPerUnit * 3): 0.5, + 20: 0, + } + require.NoError(t, common.UpdateTopupGroupRatioByJSONString(`{"default":1,"vip":1.2}`)) + + testCases := []struct { + name string + amount int64 + group string + quotaDisplayType string + expected float64 + }{ + { + name: "currency display applies unit price group ratio and discount", + amount: 10, + group: "vip", + quotaDisplayType: operation_setting.QuotaDisplayTypeUSD, + expected: 24, + }, + { + name: "tokens display converts quota to display units before pricing", + amount: int64(common.QuotaPerUnit * 3), + group: "vip", + quotaDisplayType: operation_setting.QuotaDisplayTypeTokens, + expected: 4.5, + }, + { + name: "non-positive discount falls back to no discount", + amount: 20, + group: "default", + quotaDisplayType: operation_setting.QuotaDisplayTypeUSD, + expected: 50, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + operation_setting.GetGeneralSetting().QuotaDisplayType = tc.quotaDisplayType + actual := getWaffoPancakePayMoney(tc.amount, tc.group) + require.InDelta(t, tc.expected, actual, 0.000001) + }) + } +} diff --git a/dto/values.go b/dto/values.go index 860d5fae..5d748264 100644 --- a/dto/values.go +++ b/dto/values.go @@ -5,6 +5,28 @@ import ( "strconv" ) +type StringValue string + +func (s *StringValue) UnmarshalJSON(data []byte) error { + var str string + if err := json.Unmarshal(data, &str); err == nil { + *s = StringValue(str) + return nil + } + + var raw json.Number + if err := json.Unmarshal(data, &raw); err == nil { + *s = StringValue(raw.String()) + return nil + } + + return json.Unmarshal(data, &str) +} + +func (s StringValue) MarshalJSON() ([]byte, error) { + return json.Marshal(string(s)) +} + type IntValue int func (i *IntValue) UnmarshalJSON(b []byte) error { diff --git a/model/option.go b/model/option.go index efa8c01d..37fb6cf5 100644 --- a/model/option.go +++ b/model/option.go @@ -106,6 +106,18 @@ func InitOptionMap() { common.OptionMap["WaffoUnitPrice"] = strconv.FormatFloat(setting.WaffoUnitPrice, 'f', -1, 64) common.OptionMap["WaffoMinTopUp"] = strconv.Itoa(setting.WaffoMinTopUp) common.OptionMap["WaffoPayMethods"] = setting.WaffoPayMethods2JsonString() + common.OptionMap["WaffoPancakeEnabled"] = strconv.FormatBool(setting.WaffoPancakeEnabled) + common.OptionMap["WaffoPancakeSandbox"] = strconv.FormatBool(setting.WaffoPancakeSandbox) + common.OptionMap["WaffoPancakeMerchantID"] = setting.WaffoPancakeMerchantID + common.OptionMap["WaffoPancakePrivateKey"] = setting.WaffoPancakePrivateKey + common.OptionMap["WaffoPancakeWebhookPublicKey"] = setting.WaffoPancakeWebhookPublicKey + common.OptionMap["WaffoPancakeWebhookTestKey"] = setting.WaffoPancakeWebhookTestKey + common.OptionMap["WaffoPancakeStoreID"] = setting.WaffoPancakeStoreID + common.OptionMap["WaffoPancakeProductID"] = setting.WaffoPancakeProductID + common.OptionMap["WaffoPancakeReturnURL"] = setting.WaffoPancakeReturnURL + common.OptionMap["WaffoPancakeCurrency"] = setting.WaffoPancakeCurrency + common.OptionMap["WaffoPancakeUnitPrice"] = strconv.FormatFloat(setting.WaffoPancakeUnitPrice, 'f', -1, 64) + common.OptionMap["WaffoPancakeMinTopUp"] = strconv.Itoa(setting.WaffoPancakeMinTopUp) common.OptionMap["TopupGroupRatio"] = common.TopupGroupRatio2JSONString() common.OptionMap["Chats"] = setting.Chats2JsonString() common.OptionMap["AutoGroups"] = setting.AutoGroups2JsonString() @@ -407,6 +419,30 @@ func updateOptionMap(key string, value string) (err error) { setting.WaffoUnitPrice, _ = strconv.ParseFloat(value, 64) case "WaffoMinTopUp": setting.WaffoMinTopUp, _ = strconv.Atoi(value) + case "WaffoPancakeEnabled": + setting.WaffoPancakeEnabled = value == "true" + case "WaffoPancakeSandbox": + setting.WaffoPancakeSandbox = value == "true" + case "WaffoPancakeMerchantID": + setting.WaffoPancakeMerchantID = value + case "WaffoPancakePrivateKey": + setting.WaffoPancakePrivateKey = value + case "WaffoPancakeWebhookPublicKey": + setting.WaffoPancakeWebhookPublicKey = value + case "WaffoPancakeWebhookTestKey": + setting.WaffoPancakeWebhookTestKey = value + case "WaffoPancakeStoreID": + setting.WaffoPancakeStoreID = value + case "WaffoPancakeProductID": + setting.WaffoPancakeProductID = value + case "WaffoPancakeReturnURL": + setting.WaffoPancakeReturnURL = value + case "WaffoPancakeCurrency": + setting.WaffoPancakeCurrency = value + case "WaffoPancakeUnitPrice": + setting.WaffoPancakeUnitPrice, _ = strconv.ParseFloat(value, 64) + case "WaffoPancakeMinTopUp": + setting.WaffoPancakeMinTopUp, _ = strconv.Atoi(value) case "TopupGroupRatio": err = common.UpdateTopupGroupRatioByJSONString(value) case "GitHubClientId": diff --git a/model/payment_method_guard_test.go b/model/payment_method_guard_test.go new file mode 100644 index 00000000..9bc29244 --- /dev/null +++ b/model/payment_method_guard_test.go @@ -0,0 +1,172 @@ +package model + +import ( + "testing" + "time" + + "github.com/QuantumNous/new-api/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func insertUserForPaymentGuardTest(t *testing.T, id int, quota int) { + t.Helper() + user := &User{ + Id: id, + Username: "payment_guard_user", + Status: common.UserStatusEnabled, + Quota: quota, + } + require.NoError(t, DB.Create(user).Error) +} + +func insertSubscriptionPlanForPaymentGuardTest(t *testing.T, id int) *SubscriptionPlan { + t.Helper() + plan := &SubscriptionPlan{ + Id: id, + Title: "Guard Plan", + PriceAmount: 9.99, + Currency: "USD", + DurationUnit: SubscriptionDurationMonth, + DurationValue: 1, + Enabled: true, + TotalAmount: 1000, + } + require.NoError(t, DB.Create(plan).Error) + return plan +} + +func insertSubscriptionOrderForPaymentGuardTest(t *testing.T, tradeNo string, userID int, planID int, paymentMethod string) { + t.Helper() + order := &SubscriptionOrder{ + UserId: userID, + PlanId: planID, + Money: 9.99, + TradeNo: tradeNo, + PaymentMethod: paymentMethod, + Status: common.TopUpStatusPending, + CreateTime: time.Now().Unix(), + } + require.NoError(t, order.Insert()) +} + +func insertTopUpForPaymentGuardTest(t *testing.T, tradeNo string, userID int, paymentMethod string) { + t.Helper() + topUp := &TopUp{ + UserId: userID, + Amount: 2, + Money: 9.99, + TradeNo: tradeNo, + PaymentMethod: paymentMethod, + Status: common.TopUpStatusPending, + CreateTime: time.Now().Unix(), + } + require.NoError(t, topUp.Insert()) +} + +func getTopUpStatusForPaymentGuardTest(t *testing.T, tradeNo string) string { + t.Helper() + topUp := GetTopUpByTradeNo(tradeNo) + require.NotNil(t, topUp) + return topUp.Status +} + +func countUserSubscriptionsForPaymentGuardTest(t *testing.T, userID int) int64 { + t.Helper() + var count int64 + require.NoError(t, DB.Model(&UserSubscription{}).Where("user_id = ?", userID).Count(&count).Error) + return count +} + +func getUserQuotaForPaymentGuardTest(t *testing.T, userID int) int { + t.Helper() + var user User + require.NoError(t, DB.Select("quota").Where("id = ?", userID).First(&user).Error) + return user.Quota +} + +func TestRechargeWaffoPancake_RejectsMismatchedPaymentMethod(t *testing.T) { + truncateTables(t) + + insertUserForPaymentGuardTest(t, 101, 0) + insertTopUpForPaymentGuardTest(t, "waffo-pancake-guard", 101, PaymentMethodStripe) + + err := RechargeWaffoPancake("waffo-pancake-guard") + require.Error(t, err) + + topUp := GetTopUpByTradeNo("waffo-pancake-guard") + require.NotNil(t, topUp) + assert.Equal(t, common.TopUpStatusPending, topUp.Status) + assert.Equal(t, 0, getUserQuotaForPaymentGuardTest(t, 101)) +} + +func TestUpdatePendingTopUpStatus_RejectsMismatchedPaymentMethod(t *testing.T) { + testCases := []struct { + name string + tradeNo string + storedPaymentMethod string + expectedPaymentMethod string + targetStatus string + }{ + { + name: "stripe expire", + tradeNo: "stripe-expire-guard", + storedPaymentMethod: PaymentMethodCreem, + expectedPaymentMethod: PaymentMethodStripe, + targetStatus: common.TopUpStatusExpired, + }, + { + name: "waffo failed", + tradeNo: "waffo-failed-guard", + storedPaymentMethod: PaymentMethodStripe, + expectedPaymentMethod: PaymentMethodWaffo, + targetStatus: common.TopUpStatusFailed, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + truncateTables(t) + insertUserForPaymentGuardTest(t, 150, 0) + insertTopUpForPaymentGuardTest(t, tc.tradeNo, 150, tc.storedPaymentMethod) + + err := UpdatePendingTopUpStatus(tc.tradeNo, tc.expectedPaymentMethod, tc.targetStatus) + require.ErrorIs(t, err, ErrPaymentMethodMismatch) + assert.Equal(t, common.TopUpStatusPending, getTopUpStatusForPaymentGuardTest(t, tc.tradeNo)) + }) + } +} + +func TestCompleteSubscriptionOrder_RejectsMismatchedPaymentMethod(t *testing.T) { + truncateTables(t) + + insertUserForPaymentGuardTest(t, 202, 0) + plan := insertSubscriptionPlanForPaymentGuardTest(t, 301) + insertSubscriptionOrderForPaymentGuardTest(t, "sub-guard-order", 202, plan.Id, PaymentMethodStripe) + + err := CompleteSubscriptionOrder("sub-guard-order", `{"provider":"epay"}`, "alipay") + require.ErrorIs(t, err, ErrPaymentMethodMismatch) + + order := GetSubscriptionOrderByTradeNo("sub-guard-order") + require.NotNil(t, order) + assert.Equal(t, common.TopUpStatusPending, order.Status) + assert.Zero(t, countUserSubscriptionsForPaymentGuardTest(t, 202)) + + topUp := GetTopUpByTradeNo("sub-guard-order") + assert.Nil(t, topUp) +} + +func TestExpireSubscriptionOrder_RejectsMismatchedPaymentMethod(t *testing.T) { + truncateTables(t) + + insertUserForPaymentGuardTest(t, 303, 0) + plan := insertSubscriptionPlanForPaymentGuardTest(t, 401) + insertSubscriptionOrderForPaymentGuardTest(t, "sub-expire-guard", 303, plan.Id, PaymentMethodStripe) + + err := ExpireSubscriptionOrder("sub-expire-guard", PaymentMethodCreem) + require.ErrorIs(t, err, ErrPaymentMethodMismatch) + + order := GetSubscriptionOrderByTradeNo("sub-expire-guard") + require.NotNil(t, order) + assert.Equal(t, common.TopUpStatusPending, order.Status) +} diff --git a/model/subscription.go b/model/subscription.go index 2d23a8b5..10e750c3 100644 --- a/model/subscription.go +++ b/model/subscription.go @@ -505,7 +505,7 @@ func CreateUserSubscriptionFromPlanTx(tx *gorm.DB, userId int, plan *Subscriptio } // Complete a subscription order (idempotent). Creates a UserSubscription snapshot from the plan. -func CompleteSubscriptionOrder(tradeNo string, providerPayload string) error { +func CompleteSubscriptionOrder(tradeNo string, providerPayload string, expectedPaymentMethod string) error { if tradeNo == "" { return errors.New("tradeNo is empty") } @@ -523,6 +523,9 @@ func CompleteSubscriptionOrder(tradeNo string, providerPayload string) error { if err := tx.Set("gorm:query_option", "FOR UPDATE").Where(refCol+" = ?", tradeNo).First(&order).Error; err != nil { return ErrSubscriptionOrderNotFound } + if expectedPaymentMethod != "" && order.PaymentMethod != expectedPaymentMethod { + return ErrPaymentMethodMismatch + } if order.Status == common.TopUpStatusSuccess { return nil } @@ -596,6 +599,8 @@ func upsertSubscriptionTopUpTx(tx *gorm.DB, order *SubscriptionOrder) error { topup.Money = order.Money if topup.PaymentMethod == "" { topup.PaymentMethod = order.PaymentMethod + } else if topup.PaymentMethod != order.PaymentMethod { + return ErrPaymentMethodMismatch } if topup.CreateTime == 0 { topup.CreateTime = order.CreateTime @@ -605,7 +610,7 @@ func upsertSubscriptionTopUpTx(tx *gorm.DB, order *SubscriptionOrder) error { return tx.Save(&topup).Error } -func ExpireSubscriptionOrder(tradeNo string) error { +func ExpireSubscriptionOrder(tradeNo string, expectedPaymentMethod string) error { if tradeNo == "" { return errors.New("tradeNo is empty") } @@ -618,6 +623,9 @@ func ExpireSubscriptionOrder(tradeNo string) error { if err := tx.Set("gorm:query_option", "FOR UPDATE").Where(refCol+" = ?", tradeNo).First(&order).Error; err != nil { return ErrSubscriptionOrderNotFound } + if expectedPaymentMethod != "" && order.PaymentMethod != expectedPaymentMethod { + return ErrPaymentMethodMismatch + } if order.Status != common.TopUpStatusPending { return nil } diff --git a/model/task_cas_test.go b/model/task_cas_test.go index 3449c6d2..ba34a732 100644 --- a/model/task_cas_test.go +++ b/model/task_cas_test.go @@ -33,7 +33,17 @@ func TestMain(m *testing.M) { } sqlDB.SetMaxOpenConns(1) - if err := db.AutoMigrate(&Task{}, &User{}, &Token{}, &Log{}, &Channel{}); err != nil { + if err := db.AutoMigrate( + &Task{}, + &User{}, + &Token{}, + &Log{}, + &Channel{}, + &TopUp{}, + &SubscriptionPlan{}, + &SubscriptionOrder{}, + &UserSubscription{}, + ); err != nil { panic("failed to migrate: " + err.Error()) } @@ -48,6 +58,10 @@ func truncateTables(t *testing.T) { DB.Exec("DELETE FROM tokens") DB.Exec("DELETE FROM logs") DB.Exec("DELETE FROM channels") + DB.Exec("DELETE FROM top_ups") + DB.Exec("DELETE FROM subscription_orders") + DB.Exec("DELETE FROM subscription_plans") + DB.Exec("DELETE FROM user_subscriptions") }) } diff --git a/model/topup.go b/model/topup.go index d1b0c5cb..c1ac663f 100644 --- a/model/topup.go +++ b/model/topup.go @@ -23,7 +23,18 @@ type TopUp struct { Status string `json:"status"` } -var ErrPaymentMethodMismatch = errors.New("payment method mismatch") +const ( + PaymentMethodStripe = "stripe" + PaymentMethodCreem = "creem" + PaymentMethodWaffo = "waffo" + PaymentMethodWaffoPancake = "waffo_pancake" +) + +var ( + ErrPaymentMethodMismatch = errors.New("payment method mismatch") + ErrTopUpNotFound = errors.New("topup not found") + ErrTopUpStatusInvalid = errors.New("topup status invalid") +) func (topUp *TopUp) Insert() error { var err error @@ -57,6 +68,33 @@ func GetTopUpByTradeNo(tradeNo string) *TopUp { return topUp } +func UpdatePendingTopUpStatus(tradeNo string, expectedPaymentMethod string, targetStatus string) error { + if tradeNo == "" { + return errors.New("未提供支付单号") + } + + refCol := "`trade_no`" + if common.UsingPostgreSQL { + refCol = `"trade_no"` + } + + return DB.Transaction(func(tx *gorm.DB) error { + topUp := &TopUp{} + if err := tx.Set("gorm:query_option", "FOR UPDATE").Where(refCol+" = ?", tradeNo).First(topUp).Error; err != nil { + return ErrTopUpNotFound + } + if expectedPaymentMethod != "" && topUp.PaymentMethod != expectedPaymentMethod { + return ErrPaymentMethodMismatch + } + if topUp.Status != common.TopUpStatusPending { + return ErrTopUpStatusInvalid + } + + topUp.Status = targetStatus + return tx.Save(topUp).Error + }) +} + func Recharge(referenceId string, customerId string, callerIp string) (err error) { if referenceId == "" { return errors.New("未提供支付单号") @@ -76,7 +114,7 @@ func Recharge(referenceId string, customerId string, callerIp string) (err error return errors.New("充值订单不存在") } - if topUp.PaymentMethod != "stripe" { + if topUp.PaymentMethod != PaymentMethodStripe { return ErrPaymentMethodMismatch } @@ -105,7 +143,7 @@ func Recharge(referenceId string, customerId string, callerIp string) (err error return errors.New("充值失败,请稍后重试") } - RecordTopupLog(topUp.UserId, fmt.Sprintf("使用在线充值成功,充值金额: %v,支付金额:%d", logger.FormatQuota(int(quota)), topUp.Amount), callerIp, topUp.PaymentMethod, "stripe") + RecordTopupLog(topUp.UserId, fmt.Sprintf("使用在线充值成功,充值金额: %v,支付金额:%d", logger.FormatQuota(int(quota)), topUp.Amount), callerIp, topUp.PaymentMethod, PaymentMethodStripe) return nil } @@ -302,7 +340,7 @@ func ManualCompleteTopUp(tradeNo string, callerIp string) error { // 计算应充值额度: // - Stripe 订单:Money 代表经分组倍率换算后的美元数量,直接 * QuotaPerUnit // - 其他订单(如易支付):Amount 为美元数量,* QuotaPerUnit - if topUp.PaymentMethod == "stripe" { + if topUp.PaymentMethod == PaymentMethodStripe { dQuotaPerUnit := decimal.NewFromFloat(common.QuotaPerUnit) quotaToAdd = int(decimal.NewFromFloat(topUp.Money).Mul(dQuotaPerUnit).IntPart()) } else { @@ -359,7 +397,7 @@ func RechargeCreem(referenceId string, customerEmail string, customerName string return errors.New("充值订单不存在") } - if topUp.PaymentMethod != "creem" { + if topUp.PaymentMethod != PaymentMethodCreem { return ErrPaymentMethodMismatch } @@ -410,7 +448,7 @@ func RechargeCreem(referenceId string, customerEmail string, customerName string return errors.New("充值失败,请稍后重试") } - RecordTopupLog(topUp.UserId, fmt.Sprintf("使用Creem充值成功,充值额度: %v,支付金额:%.2f", quota, topUp.Money), callerIp, topUp.PaymentMethod, "creem") + RecordTopupLog(topUp.UserId, fmt.Sprintf("使用Creem充值成功,充值额度: %v,支付金额:%.2f", quota, topUp.Money), callerIp, topUp.PaymentMethod, PaymentMethodCreem) return nil } @@ -434,7 +472,7 @@ func RechargeWaffo(tradeNo string, callerIp string) (err error) { return errors.New("充值订单不存在") } - if topUp.PaymentMethod != "waffo" { + if topUp.PaymentMethod != PaymentMethodWaffo { return ErrPaymentMethodMismatch } @@ -472,7 +510,68 @@ func RechargeWaffo(tradeNo string, callerIp string) (err error) { } if quotaToAdd > 0 { - RecordTopupLog(topUp.UserId, fmt.Sprintf("Waffo充值成功,充值额度: %v,支付金额: %.2f", logger.FormatQuota(quotaToAdd), topUp.Money), callerIp, topUp.PaymentMethod, "waffo") + RecordTopupLog(topUp.UserId, fmt.Sprintf("Waffo充值成功,充值额度: %v,支付金额: %.2f", logger.FormatQuota(quotaToAdd), topUp.Money), callerIp, topUp.PaymentMethod, PaymentMethodWaffo) + } + + return nil +} + +func RechargeWaffoPancake(tradeNo string) (err error) { + if tradeNo == "" { + return errors.New("未提供支付单号") + } + + var quotaToAdd int + topUp := &TopUp{} + + refCol := "`trade_no`" + if common.UsingPostgreSQL { + refCol = `"trade_no"` + } + + err = DB.Transaction(func(tx *gorm.DB) error { + err := tx.Set("gorm:query_option", "FOR UPDATE").Where(refCol+" = ?", tradeNo).First(topUp).Error + if err != nil { + return errors.New("充值订单不存在") + } + + if topUp.PaymentMethod != PaymentMethodWaffoPancake { + return ErrPaymentMethodMismatch + } + + if topUp.Status == common.TopUpStatusSuccess { + return nil + } + + if topUp.Status != common.TopUpStatusPending { + return errors.New("充值订单状态错误") + } + + quotaToAdd = int(decimal.NewFromInt(topUp.Amount).Mul(decimal.NewFromFloat(common.QuotaPerUnit)).IntPart()) + if quotaToAdd <= 0 { + return errors.New("无效的充值额度") + } + + topUp.CompleteTime = common.GetTimestamp() + topUp.Status = common.TopUpStatusSuccess + if err := tx.Save(topUp).Error; err != nil { + return err + } + + if err := tx.Model(&User{}).Where("id = ?", topUp.UserId).Update("quota", gorm.Expr("quota + ?", quotaToAdd)).Error; err != nil { + return err + } + + return nil + }) + + if err != nil { + common.SysError("waffo pancake topup failed: " + err.Error()) + return errors.New("充值失败,请稍后重试") + } + + if quotaToAdd > 0 { + RecordLog(topUp.UserId, LogTypeTopup, fmt.Sprintf("Waffo Pancake充值成功,充值额度: %v,支付金额: %.2f", logger.FormatQuota(quotaToAdd), topUp.Money)) } return nil diff --git a/router/api-router.go b/router/api-router.go index acc2241b..83f5e4ae 100644 --- a/router/api-router.go +++ b/router/api-router.go @@ -49,6 +49,7 @@ func SetApiRouter(router *gin.Engine) { apiRouter.POST("/stripe/webhook", controller.StripeWebhook) apiRouter.POST("/creem/webhook", controller.CreemWebhook) apiRouter.POST("/waffo/webhook", controller.WaffoWebhook) + //apiRouter.POST("/waffo-pancake/webhook", controller.WaffoPancakeWebhook) // Universal secure verification routes apiRouter.POST("/verify", middleware.UserAuth(), middleware.CriticalRateLimit(), controller.UniversalVerify) @@ -90,7 +91,10 @@ func SetApiRouter(router *gin.Engine) { selfRoute.POST("/stripe/pay", middleware.CriticalRateLimit(), controller.RequestStripePay) selfRoute.POST("/stripe/amount", controller.RequestStripeAmount) selfRoute.POST("/creem/pay", middleware.CriticalRateLimit(), controller.RequestCreemPay) + selfRoute.POST("/waffo/amount", controller.RequestWaffoAmount) selfRoute.POST("/waffo/pay", middleware.CriticalRateLimit(), controller.RequestWaffoPay) + //selfRoute.POST("/waffo-pancake/amount", controller.RequestWaffoPancakeAmount) + //selfRoute.POST("/waffo-pancake/pay", middleware.CriticalRateLimit(), controller.RequestWaffoPancakePay) selfRoute.POST("/aff_transfer", controller.TransferAffQuota) selfRoute.PUT("/setting", controller.UpdateUserSetting) diff --git a/service/task_billing_test.go b/service/task_billing_test.go index 79c8c49e..39cb8f1d 100644 --- a/service/task_billing_test.go +++ b/service/task_billing_test.go @@ -42,6 +42,7 @@ func TestMain(m *testing.M) { &model.Token{}, &model.Log{}, &model.Channel{}, + &model.TopUp{}, &model.UserSubscription{}, ); err != nil { panic("failed to migrate: " + err.Error()) @@ -62,6 +63,7 @@ func truncate(t *testing.T) { model.DB.Exec("DELETE FROM tokens") model.DB.Exec("DELETE FROM logs") model.DB.Exec("DELETE FROM channels") + model.DB.Exec("DELETE FROM top_ups") model.DB.Exec("DELETE FROM user_subscriptions") }) } diff --git a/service/waffo_pancake.go b/service/waffo_pancake.go new file mode 100644 index 00000000..9033c37f --- /dev/null +++ b/service/waffo_pancake.go @@ -0,0 +1,398 @@ +package service + +import ( + "bytes" + "context" + "crypto" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "fmt" + "io" + "math" + "net/http" + "strconv" + "strings" + "time" + + "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/setting" +) + +const ( + waffoPancakeAuthBaseURL = "https://waffo-pancake-auth-service.vercel.app" + waffoPancakeCheckoutPath = "/v1/actions/checkout/create-session" + waffoPancakeDefaultTolerance = 5 * time.Minute +) + +type WaffoPancakePriceSnapshot struct { + Amount string `json:"amount"` + TaxIncluded bool `json:"taxIncluded"` + TaxCategory string `json:"taxCategory"` +} + +type WaffoPancakeCreateSessionParams struct { + StoreID string `json:"storeId"` + ProductID string `json:"productId"` + ProductType string `json:"productType"` + Currency string `json:"currency"` + PriceSnapshot *WaffoPancakePriceSnapshot `json:"priceSnapshot,omitempty"` + BuyerEmail string `json:"buyerEmail,omitempty"` + SuccessURL string `json:"successUrl,omitempty"` + ExpiresInSeconds *int `json:"expiresInSeconds,omitempty"` +} + +type WaffoPancakeCheckoutSession struct { + SessionID string `json:"sessionId"` + CheckoutURL string `json:"checkoutUrl"` + ExpiresAt string `json:"expiresAt"` + OrderID string `json:"orderId"` +} + +type waffoPancakeAPIError struct { + Message string `json:"message"` + Layer string `json:"layer"` +} + +type waffoPancakeCreateSessionResponse struct { + Data *WaffoPancakeCheckoutSession `json:"data"` + Errors []waffoPancakeAPIError `json:"errors"` +} + +type waffoPancakeWebhookData struct { + ID string `json:"id"` + OrderID string `json:"orderId"` + BuyerEmail string `json:"buyerEmail"` + Currency string `json:"currency"` + Amount dto.StringValue `json:"amount"` + TaxAmount dto.StringValue `json:"taxAmount"` + ProductName string `json:"productName"` +} + +type waffoPancakeWebhookEvent struct { + ID string `json:"id"` + Timestamp string `json:"timestamp"` + EventType string `json:"eventType"` + EventID string `json:"eventId"` + StoreID string `json:"storeId"` + Mode string `json:"mode"` + Data waffoPancakeWebhookData `json:"data"` +} + +func (e *waffoPancakeWebhookEvent) NormalizedEventType() string { + if e == nil { + return "" + } + return e.EventType +} + +func CreateWaffoPancakeCheckoutSession(ctx context.Context, params *WaffoPancakeCreateSessionParams) (*WaffoPancakeCheckoutSession, error) { + if params == nil { + return nil, fmt.Errorf("missing checkout params") + } + + body, err := common.Marshal(params) + if err != nil { + return nil, fmt.Errorf("marshal Waffo Pancake checkout payload: %w", err) + } + + privateKey, err := normalizeRSAPrivateKey(setting.WaffoPancakePrivateKey) + if err != nil { + return nil, err + } + + timestamp := strconv.FormatInt(time.Now().Unix(), 10) + signature, err := signWaffoPancakeRequest(http.MethodPost, waffoPancakeCheckoutPath, timestamp, string(body), privateKey) + if err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(ctx, http.MethodPost, waffoPancakeAuthBaseURL+waffoPancakeCheckoutPath, bytes.NewReader(body)) + if err != nil { + return nil, fmt.Errorf("build Waffo Pancake checkout request: %w", err) + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Merchant-Id", setting.WaffoPancakeMerchantID) + req.Header.Set("X-Timestamp", timestamp) + req.Header.Set("X-Signature", signature) + if setting.WaffoPancakeSandbox { + req.Header.Set("X-Environment", "test") + } else { + req.Header.Set("X-Environment", "prod") + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, fmt.Errorf("request Waffo Pancake checkout session: %w", err) + } + defer resp.Body.Close() + + responseBody, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("read Waffo Pancake checkout response: %w", err) + } + + var result waffoPancakeCreateSessionResponse + if err := common.Unmarshal(responseBody, &result); err != nil { + return nil, fmt.Errorf("decode Waffo Pancake checkout response: %w", err) + } + if resp.StatusCode >= http.StatusBadRequest { + if len(result.Errors) > 0 { + return nil, fmt.Errorf("Waffo Pancake error (%d): %s", resp.StatusCode, result.Errors[0].Message) + } + return nil, fmt.Errorf("Waffo Pancake checkout request failed with status %d", resp.StatusCode) + } + if len(result.Errors) > 0 { + return nil, fmt.Errorf("Waffo Pancake error: %s", result.Errors[0].Message) + } + if result.Data == nil || result.Data.CheckoutURL == "" || strings.TrimSpace(result.Data.SessionID) == "" { + return nil, fmt.Errorf("Waffo Pancake returned empty checkout session") + } + return result.Data, nil +} + +func VerifyConfiguredWaffoPancakeWebhook(payload string, signatureHeader string) (*waffoPancakeWebhookEvent, error) { + environment := resolveWaffoPancakeWebhookEnvironment(payload) + return verifyWaffoPancakeWebhook(payload, signatureHeader, environment) +} + +func ResolveWaffoPancakeTradeNo(event *waffoPancakeWebhookEvent) (string, error) { + if event == nil { + return "", fmt.Errorf("missing webhook event") + } + + if tradeNo := strings.TrimSpace(event.Data.OrderID); tradeNo != "" { + topUp := model.GetTopUpByTradeNo(tradeNo) + if topUp != nil && topUp.PaymentMethod == model.PaymentMethodWaffoPancake { + return tradeNo, nil + } + return "", fmt.Errorf("waffo pancake order not found for webhook orderId=%s", tradeNo) + } + + return "", fmt.Errorf("missing webhook orderId") +} + +func normalizeRSAPrivateKey(raw string) (string, error) { + return normalizePEMKey(raw, "PRIVATE KEY", "RSA PRIVATE KEY") +} + +func normalizeRSAPublicKey(raw string) (string, error) { + return normalizePEMKey(raw, "PUBLIC KEY", "RSA PUBLIC KEY") +} + +func normalizePEMKey(raw string, pkcs8Type string, pkcs1Type string) (string, error) { + if strings.TrimSpace(raw) == "" { + return "", fmt.Errorf("%s is empty", strings.ToLower(pkcs8Type)) + } + + normalized := strings.TrimSpace(strings.ReplaceAll(raw, `\n`, "\n")) + if strings.Contains(normalized, "BEGIN ") { + block, _ := pem.Decode([]byte(normalized)) + if block == nil { + return "", fmt.Errorf("invalid PEM encoded %s", strings.ToLower(pkcs8Type)) + } + return string(pem.EncodeToMemory(block)), nil + } + + der, err := base64.StdEncoding.DecodeString(strings.ReplaceAll(normalized, "\n", "")) + if err != nil { + return "", fmt.Errorf("invalid base64 encoded %s: %w", strings.ToLower(pkcs8Type), err) + } + + pemType := pkcs8Type + if pkcs8Type == "PRIVATE KEY" { + if _, err := x509.ParsePKCS8PrivateKey(der); err != nil { + if _, err := x509.ParsePKCS1PrivateKey(der); err == nil { + pemType = pkcs1Type + } else { + return "", fmt.Errorf("invalid RSA private key") + } + } + } else { + if _, err := x509.ParsePKIXPublicKey(der); err != nil { + if _, err := x509.ParsePKCS1PublicKey(der); err == nil { + pemType = pkcs1Type + } else { + return "", fmt.Errorf("invalid RSA public key") + } + } + } + + return string(pem.EncodeToMemory(&pem.Block{Type: pemType, Bytes: der})), nil +} + +func signWaffoPancakeRequest(method string, path string, timestamp string, body string, privateKeyPEM string) (string, error) { + block, _ := pem.Decode([]byte(privateKeyPEM)) + if block == nil { + return "", fmt.Errorf("invalid RSA private key PEM") + } + + var privateKey *rsa.PrivateKey + switch block.Type { + case "PRIVATE KEY": + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return "", fmt.Errorf("parse PKCS#8 private key: %w", err) + } + parsed, ok := key.(*rsa.PrivateKey) + if !ok { + return "", fmt.Errorf("private key is not RSA") + } + privateKey = parsed + case "RSA PRIVATE KEY": + key, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return "", fmt.Errorf("parse PKCS#1 private key: %w", err) + } + privateKey = key + default: + return "", fmt.Errorf("unsupported private key type: %s", block.Type) + } + + canonicalRequest := buildWaffoPancakeCanonicalRequest(method, path, timestamp, body) + digest := sha256.Sum256([]byte(canonicalRequest)) + signature, err := rsa.SignPKCS1v15(nil, privateKey, crypto.SHA256, digest[:]) + if err != nil { + return "", fmt.Errorf("sign Waffo Pancake request: %w", err) + } + return base64.StdEncoding.EncodeToString(signature), nil +} + +func buildWaffoPancakeCanonicalRequest(method string, path string, timestamp string, body string) string { + bodyHash := sha256.Sum256([]byte(body)) + return fmt.Sprintf( + "%s\n%s\n%s\n%s", + strings.ToUpper(method), + path, + timestamp, + base64.StdEncoding.EncodeToString(bodyHash[:]), + ) +} + +func verifyWaffoPancakeWebhook(payload string, signatureHeader string, environment string) (*waffoPancakeWebhookEvent, error) { + if signatureHeader == "" { + return nil, fmt.Errorf("missing X-Waffo-Signature header") + } + + timestampPart, signaturePart := parseWaffoPancakeSignatureHeader(signatureHeader) + if timestampPart == "" || signaturePart == "" { + return nil, fmt.Errorf("malformed X-Waffo-Signature header") + } + + timestampMs, err := strconv.ParseInt(timestampPart, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid timestamp in X-Waffo-Signature header") + } + if math.Abs(float64(time.Now().UnixMilli()-timestampMs)) > float64(waffoPancakeDefaultTolerance.Milliseconds()) { + return nil, fmt.Errorf("webhook timestamp outside tolerance window") + } + + signatureInput := fmt.Sprintf("%s.%s", timestampPart, payload) + if err := verifyWaffoPancakeWebhookWithKey(signatureInput, signaturePart, resolveWaffoPancakeWebhookPublicKey(environment)); err != nil { + return nil, fmt.Errorf("invalid webhook signature") + } + + var event waffoPancakeWebhookEvent + if err := common.Unmarshal([]byte(payload), &event); err != nil { + return nil, fmt.Errorf("parse Waffo Pancake webhook payload: %w", err) + } + return &event, nil +} + +func parseWaffoPancakeSignatureHeader(header string) (string, string) { + var timestampPart string + var signaturePart string + for _, pair := range strings.Split(header, ",") { + key, value, found := strings.Cut(strings.TrimSpace(pair), "=") + if !found { + continue + } + switch key { + case "t": + timestampPart = value + case "v1": + signaturePart = value + } + } + return timestampPart, signaturePart +} + +func resolveWaffoPancakeWebhookEnvironment(payload string) string { + var envelope struct { + Mode string `json:"mode"` + } + if err := common.Unmarshal([]byte(payload), &envelope); err != nil { + if setting.WaffoPancakeSandbox { + return "test" + } + return "prod" + } + + switch strings.ToLower(strings.TrimSpace(envelope.Mode)) { + case "test": + return "test" + case "prod": + return "prod" + default: + if setting.WaffoPancakeSandbox { + return "test" + } + return "prod" + } +} + +func resolveWaffoPancakeWebhookPublicKey(environment string) string { + if environment == "prod" { + return strings.TrimSpace(setting.WaffoPancakeWebhookPublicKey) + } + return strings.TrimSpace(setting.WaffoPancakeWebhookTestKey) +} + +func verifyWaffoPancakeWebhookWithKey(signatureInput string, signaturePart string, rawPublicKey string) error { + publicKeyPEM, err := normalizeRSAPublicKey(rawPublicKey) + if err != nil { + return err + } + + block, _ := pem.Decode([]byte(publicKeyPEM)) + if block == nil { + return fmt.Errorf("invalid RSA public key PEM") + } + + var publicKey *rsa.PublicKey + switch block.Type { + case "PUBLIC KEY": + key, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return fmt.Errorf("parse PKIX public key: %w", err) + } + parsed, ok := key.(*rsa.PublicKey) + if !ok { + return fmt.Errorf("public key is not RSA") + } + publicKey = parsed + case "RSA PUBLIC KEY": + key, err := x509.ParsePKCS1PublicKey(block.Bytes) + if err != nil { + return fmt.Errorf("parse PKCS#1 public key: %w", err) + } + publicKey = key + default: + return fmt.Errorf("unsupported public key type: %s", block.Type) + } + + signature, err := base64.StdEncoding.DecodeString(signaturePart) + if err != nil { + return fmt.Errorf("decode webhook signature: %w", err) + } + + digest := sha256.Sum256([]byte(signatureInput)) + if err := rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, digest[:], signature); err != nil { + return fmt.Errorf("verify webhook signature: %w", err) + } + return nil +} diff --git a/service/waffo_pancake_test.go b/service/waffo_pancake_test.go new file mode 100644 index 00000000..eeb1012b --- /dev/null +++ b/service/waffo_pancake_test.go @@ -0,0 +1,157 @@ +package service + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/setting" + "github.com/glebarez/sqlite" + "github.com/stretchr/testify/require" + "gorm.io/gorm" +) + +func setupWaffoPancakeTestDB(t *testing.T) *gorm.DB { + t.Helper() + + common.UsingSQLite = true + common.UsingMySQL = false + common.UsingPostgreSQL = false + common.RedisEnabled = false + + dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", strings.ReplaceAll(t.Name(), "/", "_")) + db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{}) + require.NoError(t, err) + + model.DB = db + model.LOG_DB = db + + require.NoError(t, db.AutoMigrate(&model.User{}, &model.TopUp{})) + + t.Cleanup(func() { + sqlDB, err := db.DB() + if err == nil { + _ = sqlDB.Close() + } + }) + + return db +} + +func TestWaffoPancakeCreateSessionResponseParsesDocumentedPayload(t *testing.T) { + var result waffoPancakeCreateSessionResponse + err := common.Unmarshal([]byte(`{ + "data": { + "sessionId": "cs_550e8400-e29b-41d4-a716-446655440000", + "checkoutUrl": "https://checkout.waffo.ai/my-store-abc123/checkout/cs_550e8400-e29b-41d4-a716-446655440000", + "expiresAt": "2026-01-22T10:30:00.000Z" + } + }`), &result) + require.NoError(t, err) + require.NotNil(t, result.Data) + require.Equal(t, "cs_550e8400-e29b-41d4-a716-446655440000", result.Data.SessionID) + require.Empty(t, result.Data.OrderID) +} + +func TestResolveWaffoPancakeTradeNo_UsesWebhookOrderIDWhenLocalOrderExists(t *testing.T) { + db := setupWaffoPancakeTestDB(t) + + topUp := &model.TopUp{ + UserId: 1, + Amount: 10, + Money: 29, + TradeNo: "ORD_5dXBtmF2HLlHfbPNm0Wcnz", + PaymentMethod: model.PaymentMethodWaffoPancake, + CreateTime: time.Now().Unix(), + Status: common.TopUpStatusPending, + } + require.NoError(t, db.Create(topUp).Error) + + tradeNo, err := ResolveWaffoPancakeTradeNo(&waffoPancakeWebhookEvent{ + Data: waffoPancakeWebhookData{ + OrderID: "ORD_5dXBtmF2HLlHfbPNm0Wcnz", + }, + }) + require.NoError(t, err) + require.Equal(t, "ORD_5dXBtmF2HLlHfbPNm0Wcnz", tradeNo) +} + +func TestResolveWaffoPancakeTradeNo_FailsWhenWebhookOrderIDIsUnknown(t *testing.T) { + db := setupWaffoPancakeTestDB(t) + + user := &model.User{ + Id: 42, + Email: "buyer@example.com", + Username: "buyer", + Status: common.UserStatusEnabled, + } + require.NoError(t, db.Create(user).Error) + + topUp := &model.TopUp{ + UserId: user.Id, + Amount: 10, + Money: 29, + TradeNo: "WAFFO_PANCAKE-42-123456-abc123", + PaymentMethod: model.PaymentMethodWaffoPancake, + CreateTime: time.Now().Unix(), + Status: common.TopUpStatusPending, + } + require.NoError(t, db.Create(topUp).Error) + + tradeNo, err := ResolveWaffoPancakeTradeNo(&waffoPancakeWebhookEvent{ + Data: waffoPancakeWebhookData{ + OrderID: "ORD_unknown", + BuyerEmail: user.Email, + Amount: "29.00", + }, + }) + require.Error(t, err) + require.Empty(t, tradeNo) +} + +func TestResolveWaffoPancakeWebhookEnvironment(t *testing.T) { + originalSandbox := setting.WaffoPancakeSandbox + t.Cleanup(func() { + setting.WaffoPancakeSandbox = originalSandbox + }) + + testCases := []struct { + name string + payload string + expected string + sandbox bool + }{ + { + name: "test mode", + payload: `{"mode":"test"}`, + expected: "test", + }, + { + name: "prod mode", + payload: `{"mode":"prod"}`, + expected: "prod", + }, + { + name: "missing mode falls back to sandbox", + payload: `{}`, + expected: "test", + sandbox: true, + }, + { + name: "invalid mode falls back to prod", + payload: `{"mode":"staging"}`, + expected: "prod", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + setting.WaffoPancakeSandbox = tc.sandbox + environment := resolveWaffoPancakeWebhookEnvironment(tc.payload) + require.Equal(t, tc.expected, environment) + }) + } +} diff --git a/setting/payment_waffo_pancake.go b/setting/payment_waffo_pancake.go new file mode 100644 index 00000000..d655059a --- /dev/null +++ b/setting/payment_waffo_pancake.go @@ -0,0 +1,16 @@ +package setting + +var ( + WaffoPancakeEnabled bool + WaffoPancakeSandbox bool + WaffoPancakeMerchantID string + WaffoPancakePrivateKey string + WaffoPancakeWebhookPublicKey string + WaffoPancakeWebhookTestKey string + WaffoPancakeStoreID string + WaffoPancakeProductID string + WaffoPancakeReturnURL string + WaffoPancakeCurrency string = "USD" + WaffoPancakeUnitPrice float64 = 1.0 + WaffoPancakeMinTopUp int = 1 +) diff --git a/web/src/components/settings/PaymentSetting.jsx b/web/src/components/settings/PaymentSetting.jsx index 928d58a7..080c3e6e 100644 --- a/web/src/components/settings/PaymentSetting.jsx +++ b/web/src/components/settings/PaymentSetting.jsx @@ -18,12 +18,13 @@ For commercial licensing, please contact support@quantumnous.com */ import React, { useEffect, useState } from 'react'; -import { Card, Spin } from '@douyinfe/semi-ui'; +import { Card, Spin, Tabs } from '@douyinfe/semi-ui'; import SettingsGeneralPayment from '../../pages/Setting/Payment/SettingsGeneralPayment'; import SettingsPaymentGateway from '../../pages/Setting/Payment/SettingsPaymentGateway'; import SettingsPaymentGatewayStripe from '../../pages/Setting/Payment/SettingsPaymentGatewayStripe'; import SettingsPaymentGatewayCreem from '../../pages/Setting/Payment/SettingsPaymentGatewayCreem'; import SettingsPaymentGatewayWaffo from '../../pages/Setting/Payment/SettingsPaymentGatewayWaffo'; +import SettingsPaymentGatewayWaffoPancake from '../../pages/Setting/Payment/SettingsPaymentGatewayWaffoPancake'; import { API, showError, toBoolean } from '../../helpers'; import { useTranslation } from 'react-i18next'; @@ -48,6 +49,17 @@ const PaymentSetting = () => { StripeUnitPrice: 8.0, StripeMinTopUp: 1, StripePromotionCodesEnabled: false, + + WaffoPancakeEnabled: false, + WaffoPancakeSandbox: false, + WaffoPancakeMerchantID: '', + WaffoPancakePrivateKey: '', + WaffoPancakeStoreID: '', + WaffoPancakeProductID: '', + WaffoPancakeReturnURL: '', + WaffoPancakeCurrency: 'USD', + WaffoPancakeUnitPrice: 1.0, + WaffoPancakeMinTopUp: 1, }); let [loading, setLoading] = useState(false); @@ -96,8 +108,21 @@ const PaymentSetting = () => { case 'MinTopUp': case 'StripeUnitPrice': case 'StripeMinTopUp': + case 'WaffoPancakeUnitPrice': + case 'WaffoPancakeMinTopUp': newInputs[item.key] = parseFloat(item.value); break; + case 'WaffoPancakeMerchantID': + case 'WaffoPancakePrivateKey': + case 'WaffoPancakeStoreID': + case 'WaffoPancakeProductID': + case 'WaffoPancakeReturnURL': + case 'WaffoPancakeCurrency': + newInputs[item.key] = item.value; + break; + case 'WaffoPancakeSandbox': + newInputs[item.key] = toBoolean(item.value); + break; default: if (item.key.endsWith('Enabled')) { newInputs[item.key] = toBoolean(item.value); @@ -108,7 +133,7 @@ const PaymentSetting = () => { } }); - setInputs(newInputs); + setInputs((prev) => ({ ...prev, ...newInputs })); } else { showError(t(message)); } @@ -133,19 +158,54 @@ const PaymentSetting = () => { <> - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + {/**/} + {/* */} + {/**/} + diff --git a/web/src/components/topup/RechargeCard.jsx b/web/src/components/topup/RechargeCard.jsx index f37d129b..17ff77e4 100644 --- a/web/src/components/topup/RechargeCard.jsx +++ b/web/src/components/topup/RechargeCard.jsx @@ -21,7 +21,6 @@ import React, { useEffect, useRef, useState } from 'react'; import { Avatar, Typography, - Tag, Card, Button, Banner, @@ -88,8 +87,7 @@ const RechargeCard = ({ topupInfo, onOpenHistory, enableWaffoTopUp, - waffoTopUp, - waffoPayMethods, + enableWaffoPancakeTopUp, subscriptionLoading = false, subscriptionPlans = [], billingPreference, @@ -105,6 +103,7 @@ const RechargeCard = ({ const [activeTab, setActiveTab] = useState('topup'); const shouldShowSubscription = !subscriptionLoading && subscriptionPlans.length > 0; + const regularPayMethods = payMethods || []; useEffect(() => { if (initialTabSetRef.current) return; @@ -227,19 +226,31 @@ const RechargeCard = ({
- ) : enableOnlineTopUp || enableStripeTopUp || enableCreemTopUp || enableWaffoTopUp ? ( + ) : enableOnlineTopUp || + enableStripeTopUp || + enableCreemTopUp || + enableWaffoTopUp || + enableWaffoPancakeTopUp ? (
(onlineFormApiRef.current = api)} initValues={{ topUpCount: topUpCount }} >
- {(enableOnlineTopUp || enableStripeTopUp || enableWaffoTopUp) && ( + {(enableOnlineTopUp || + enableStripeTopUp || + enableWaffoTopUp || + enableWaffoPancakeTopUp) && ( - {payMethods && payMethods.filter(m => m.type !== 'waffo').length > 0 && ( - - + {regularPayMethods.length > 0 && ( + + - {payMethods.filter(m => m.type !== 'waffo').map((payMethod) => { - const minTopupVal = Number(payMethod.min_topup) || 0; + {regularPayMethods.map((payMethod) => { + const minTopupVal = + Number(payMethod.min_topup) || 0; const isStripe = payMethod.type === 'stripe'; + const isWaffo = + typeof payMethod.type === 'string' && + payMethod.type.startsWith('waffo:'); + const isWaffoPancake = + payMethod.type === 'waffo_pancake'; const disabled = - (!enableOnlineTopUp && !isStripe) || + (!enableOnlineTopUp && + !isStripe && + !isWaffo && + !isWaffoPancake) || (!enableStripeTopUp && isStripe) || + (!enableWaffoTopUp && isWaffo) || + (!enableWaffoPancakeTopUp && isWaffoPancake) || minTopupVal > Number(topUpCount || 0); const buttonEl = ( @@ -320,6 +342,21 @@ const RechargeCard = ({ ) : payMethod.type === 'stripe' ? ( + ) : payMethod.icon ? ( + {payMethod.name} + ) : payMethod.type === 'waffo_pancake' ? ( + ) : ( - - + + )} )} @@ -388,7 +425,9 @@ const RechargeCard = ({
{presetAmounts.map((preset, index) => { const discount = - preset.discount || topupInfo?.discount?.[preset.value] || 1.0; + preset.discount || + topupInfo?.discount?.[preset.value] || + 1.0; const originalPrice = preset.value * priceRatio; const discountedPrice = originalPrice * discount; const hasDiscount = discount < 1.0; @@ -404,7 +443,7 @@ const RechargeCard = ({ const s = JSON.parse(statusStr); usdRate = s?.usd_exchange_rate || 7; } - } catch (e) { } + } catch (e) {} let displayValue = preset.value; // 显示的数量 let displayActualPay = actualPay; @@ -455,7 +494,10 @@ const RechargeCard = ({ {hasDiscount && ( {t('折').includes('off') - ? ((1 - parseFloat(discount)) * 100).toFixed(1) + ? ( + (1 - parseFloat(discount)) * + 100 + ).toFixed(1) : (discount * 10).toFixed(1)} {t('折')} @@ -482,46 +524,6 @@ const RechargeCard = ({ )} - {/* Waffo 充值区域 */} - {enableWaffoTopUp && - waffoPayMethods && - waffoPayMethods.length > 0 && ( - - - {waffoPayMethods.map((method, index) => ( - - ))} - - - )} - {/* Creem 充值区域 */} {enableCreemTopUp && creemProducts.length > 0 && ( diff --git a/web/src/components/topup/index.jsx b/web/src/components/topup/index.jsx index 0348e3c8..1c23ca92 100644 --- a/web/src/components/topup/index.jsx +++ b/web/src/components/topup/index.jsx @@ -75,6 +75,8 @@ const TopUp = () => { const [enableWaffoTopUp, setEnableWaffoTopUp] = useState(false); const [waffoPayMethods, setWaffoPayMethods] = useState([]); const [waffoMinTopUp, setWaffoMinTopUp] = useState(1); + const [enableWaffoPancakeTopUp, setEnableWaffoPancakeTopUp] = useState(false); + const [waffoPancakeMinTopUp, setWaffoPancakeMinTopUp] = useState(1); const [isSubmitting, setIsSubmitting] = useState(false); const [open, setOpen] = useState(false); @@ -112,6 +114,39 @@ const TopUp = () => { discount: {}, }); + const confirmPayMethods = [ + ...payMethods, + ...waffoPayMethods.map((method, index) => ({ + ...method, + type: `waffo:${index}`, + min_topup: waffoMinTopUp, + color: method.color || 'rgba(var(--semi-primary-5), 1)', + })), + ]; + + const getPayMethodConfig = (payment) => + confirmPayMethods.find((method) => method.type === payment); + + const getPaymentMinTopUp = (payment) => { + const configuredMinTopUp = Number(getPayMethodConfig(payment)?.min_topup); + return Number.isFinite(configuredMinTopUp) && configuredMinTopUp > 0 + ? configuredMinTopUp + : minTopUp; + }; + + const requestAmountByPayment = async (payment, value) => { + if (payment === 'stripe') { + return getStripeAmount(value); + } + if (payment === 'waffo_pancake') { + return getWaffoPancakeAmount(value); + } + if (typeof payment === 'string' && payment.startsWith('waffo:')) { + return getWaffoAmount(value); + } + return getAmount(value); + }; + const topUp = async () => { if (redemptionCode === '') { showInfo(t('请输入兑换码!')); @@ -162,6 +197,16 @@ const TopUp = () => { showError(t('管理员未开启Stripe充值!')); return; } + } else if (payment === 'waffo_pancake') { + if (!enableWaffoPancakeTopUp) { + showError(t('管理员未开启 Waffo Pancake 充值!')); + return; + } + } else if (payment.startsWith('waffo:')) { + if (!enableWaffoTopUp) { + showError(t('管理员未开启 Waffo 充值!')); + return; + } } else { if (!enableOnlineTopUp) { showError(t('管理员未开启在线充值!')); @@ -172,14 +217,11 @@ const TopUp = () => { setPayWay(payment); setPaymentLoading(true); try { - if (payment === 'stripe') { - await getStripeAmount(); - } else { - await getAmount(); - } + const selectedMinTopUp = getPaymentMinTopUp(payment); + await requestAmountByPayment(payment); - if (topUpCount < minTopUp) { - showError(t('充值数量不能小于') + minTopUp); + if (topUpCount < selectedMinTopUp) { + showError(t('充值数量不能小于') + selectedMinTopUp); return; } setOpen(true); @@ -191,6 +233,29 @@ const TopUp = () => { }; const onlineTopUp = async () => { + if (payWay === 'waffo_pancake') { + setConfirmLoading(true); + try { + await waffoPancakeTopUp(); + } finally { + setOpen(false); + setConfirmLoading(false); + } + return; + } + + if (payWay.startsWith('waffo:')) { + const payMethodIndex = Number(payWay.split(':')[1]); + setConfirmLoading(true); + try { + await waffoTopUp(Number.isFinite(payMethodIndex) ? payMethodIndex : 0); + } finally { + setOpen(false); + setConfirmLoading(false); + } + return; + } + if (payWay === 'stripe') { // Stripe 支付处理 if (amount === 0) { @@ -317,32 +382,122 @@ const TopUp = () => { const waffoTopUp = async (payMethodIndex) => { try { - if (topUpCount < waffoMinTopUp) { - showError(t('充值数量不能小于') + waffoMinTopUp); - return; - } - setPaymentLoading(true); - const requestBody = { - amount: parseInt(topUpCount), - }; - if (payMethodIndex != null) { - requestBody.pay_method_index = payMethodIndex; - } - const res = await API.post('/api/user/waffo/pay', requestBody); - if (res !== undefined) { - const { message, data } = res.data; - if (message === 'success' && data?.payment_url) { - window.open(data.payment_url, '_blank'); - } else { - showError(data || t('支付请求失败')); - } + if (topUpCount < waffoMinTopUp) { + showError(t('充值数量不能小于') + waffoMinTopUp); + return; + } + setPaymentLoading(true); + const requestBody = { + amount: parseInt(topUpCount), + }; + if (payMethodIndex != null) { + requestBody.pay_method_index = payMethodIndex; + } + const res = await API.post('/api/user/waffo/pay', requestBody); + if (res !== undefined) { + const { message, data } = res.data; + if (message === 'success' && data?.payment_url) { + window.open(data.payment_url, '_blank'); } else { - showError(res); + showError(data || t('支付请求失败')); } + } else { + showError(res); + } } catch (e) { - showError(t('支付请求失败')); + showError(t('支付请求失败')); } finally { - setPaymentLoading(false); + setPaymentLoading(false); + } + }; + + const getWaffoAmount = async (value) => { + if (value === undefined) { + value = topUpCount; + } + setAmountLoading(true); + try { + const res = await API.post('/api/user/waffo/amount', { + amount: parseInt(value), + }); + if (res !== undefined) { + const { message, data } = res.data; + if (message === 'success') { + setAmount(parseFloat(data)); + } else { + setAmount(0); + Toast.error({ content: '错误:' + data, id: 'getAmount' }); + } + } else { + showError(res); + } + } catch (err) { + // amount fetch failed silently + } finally { + setAmountLoading(false); + } + }; + + const waffoPancakeTopUp = async () => { + const minTopUpValue = Number(waffoPancakeMinTopUp || 1); + if (topUpCount < minTopUpValue) { + showError(t('充值数量不能小于') + minTopUpValue); + return; + } + + setPaymentLoading(true); + try { + const res = await API.post('/api/user/waffo-pancake/pay', { + amount: parseInt(topUpCount), + }); + if (res !== undefined) { + const { message, data } = res.data; + if (message === 'success') { + const checkoutUrl = data?.checkout_url || ''; + if (checkoutUrl) { + window.open(checkoutUrl, '_blank'); + } else { + showError(t('支付请求失败')); + } + } else { + const errorMsg = + typeof data === 'string' ? data : message || t('支付请求失败'); + showError(errorMsg); + } + } else { + showError(res); + } + } catch (e) { + showError(t('支付请求失败')); + } finally { + setPaymentLoading(false); + } + }; + + const getWaffoPancakeAmount = async (value) => { + if (value === undefined) { + value = topUpCount; + } + setAmountLoading(true); + try { + const res = await API.post('/api/user/waffo-pancake/amount', { + amount: parseInt(value), + }); + if (res !== undefined) { + const { message, data } = res.data; + if (message === 'success') { + setAmount(parseFloat(data)); + } else { + setAmount(0); + Toast.error({ content: '错误:' + data, id: 'getAmount' }); + } + } else { + showError(res); + } + } catch (err) { + // amount fetch failed silently + } finally { + setAmountLoading(false); } }; @@ -481,20 +636,26 @@ const TopUp = () => { const enableStripeTopUp = data.enable_stripe_topup || false; const enableOnlineTopUp = data.enable_online_topup || false; const enableCreemTopUp = data.enable_creem_topup || false; + const enableWaffoTopUp = data.enable_waffo_topup || false; + const enableWaffoPancakeTopUp = + data.enable_waffo_pancake_topup || false; const minTopUpValue = enableOnlineTopUp ? data.min_topup : enableStripeTopUp ? data.stripe_min_topup - : data.enable_waffo_topup + : enableWaffoTopUp ? data.waffo_min_topup + : enableWaffoPancakeTopUp + ? data.waffo_pancake_min_topup : 1; setEnableOnlineTopUp(enableOnlineTopUp); setEnableStripeTopUp(enableStripeTopUp); setEnableCreemTopUp(enableCreemTopUp); - const enableWaffoTopUp = data.enable_waffo_topup || false; setEnableWaffoTopUp(enableWaffoTopUp); setWaffoPayMethods(data.waffo_pay_methods || []); setWaffoMinTopUp(data.waffo_min_topup || 1); + setEnableWaffoPancakeTopUp(enableWaffoPancakeTopUp); + setWaffoPancakeMinTopUp(data.waffo_pancake_min_topup || 1); setMinTopUp(minTopUpValue); setTopUpCount(minTopUpValue); @@ -739,7 +900,7 @@ const TopUp = () => { amountLoading={amountLoading} renderAmount={renderAmount} payWay={payWay} - payMethods={payMethods} + payMethods={confirmPayMethods} amountNumber={amount} discountRate={topupInfo?.discount?.[topUpCount] || 1.0} /> @@ -789,8 +950,7 @@ const TopUp = () => { creemProducts={creemProducts} creemPreTopUp={creemPreTopUp} enableWaffoTopUp={enableWaffoTopUp} - waffoTopUp={waffoTopUp} - waffoPayMethods={waffoPayMethods} + enableWaffoPancakeTopUp={enableWaffoPancakeTopUp} presetAmounts={presetAmounts} selectedPreset={selectedPreset} selectPresetAmount={selectPresetAmount} @@ -804,7 +964,7 @@ const TopUp = () => { setSelectedPreset={setSelectedPreset} renderAmount={renderAmount} amountLoading={amountLoading} - payMethods={payMethods} + payMethods={confirmPayMethods} preTopUp={preTopUp} paymentLoading={paymentLoading} payWay={payWay} diff --git a/web/src/components/topup/modals/PaymentConfirmModal.jsx b/web/src/components/topup/modals/PaymentConfirmModal.jsx index 8bd5455c..f1c53c12 100644 --- a/web/src/components/topup/modals/PaymentConfirmModal.jsx +++ b/web/src/components/topup/modals/PaymentConfirmModal.jsx @@ -140,6 +140,17 @@ const PaymentConfirmModal = ({ size={16} color='#635BFF' /> + ) : payMethod.icon ? ( + {payMethod.name} ) : ( { if (props.options && formApiRef.current) { const currentInputs = { ServerAddress: props.options.ServerAddress || '', + CustomCallbackAddress: props.options.CustomCallbackAddress || '', + TopupGroupRatio: props.options.TopupGroupRatio || '', + PayMethods: props.options.PayMethods || '', + AmountOptions: props.options.AmountOptions || '', + AmountDiscount: props.options.AmountDiscount || '', }; setInputs(currentInputs); + setOriginInputs({ ...currentInputs }); formApiRef.current.setValues(currentInputs); } }, [props.options]); @@ -49,19 +63,93 @@ export default function SettingsGeneralPayment(props) { setInputs(values); }; - const submitServerAddress = async () => { + const submitGeneralSettings = async () => { + if ( + originInputs.TopupGroupRatio !== inputs.TopupGroupRatio && + !verifyJSON(inputs.TopupGroupRatio) + ) { + showError(t('充值分组倍率不是合法的 JSON 字符串')); + return; + } + + if ( + originInputs.PayMethods !== inputs.PayMethods && + !verifyJSON(inputs.PayMethods) + ) { + showError(t('充值方式设置不是合法的 JSON 字符串')); + return; + } + + if ( + originInputs.AmountOptions !== inputs.AmountOptions && + inputs.AmountOptions.trim() !== '' && + !verifyJSON(inputs.AmountOptions) + ) { + showError(t('自定义充值数量选项不是合法的 JSON 数组')); + return; + } + + if ( + originInputs.AmountDiscount !== inputs.AmountDiscount && + inputs.AmountDiscount.trim() !== '' && + !verifyJSON(inputs.AmountDiscount) + ) { + showError(t('充值金额折扣配置不是合法的 JSON 对象')); + return; + } + setLoading(true); try { - let ServerAddress = removeTrailingSlash(inputs.ServerAddress); - const res = await API.put('/api/option/', { - key: 'ServerAddress', - value: ServerAddress, - }); - if (res.data.success) { + const options = [ + { + key: 'ServerAddress', + value: removeTrailingSlash(inputs.ServerAddress), + }, + ]; + + if (inputs.CustomCallbackAddress !== '') { + options.push({ + key: 'CustomCallbackAddress', + value: removeTrailingSlash(inputs.CustomCallbackAddress), + }); + } + if (originInputs.TopupGroupRatio !== inputs.TopupGroupRatio) { + options.push({ key: 'TopupGroupRatio', value: inputs.TopupGroupRatio }); + } + if (originInputs.PayMethods !== inputs.PayMethods) { + options.push({ key: 'PayMethods', value: inputs.PayMethods }); + } + if (originInputs.AmountOptions !== inputs.AmountOptions) { + options.push({ + key: 'payment_setting.amount_options', + value: inputs.AmountOptions, + }); + } + if (originInputs.AmountDiscount !== inputs.AmountDiscount) { + options.push({ + key: 'payment_setting.amount_discount', + value: inputs.AmountDiscount, + }); + } + + const results = await Promise.all( + options.map((option) => + API.put('/api/option/', { + key: option.key, + value: option.value, + }), + ), + ); + + const errorResults = results.filter((res) => !res.data.success); + if (errorResults.length === 0) { showSuccess(t('更新成功')); + setOriginInputs({ ...inputs }); props.refresh && props.refresh(); } else { - showError(res.data.message); + errorResults.forEach((res) => { + showError(res.data.message); + }); } } catch (error) { showError(t('更新失败')); @@ -76,7 +164,7 @@ export default function SettingsGeneralPayment(props) { onValueChange={handleFormChange} getFormApi={(api) => (formApiRef.current = api)} > - + - + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/src/pages/Setting/Payment/SettingsPaymentGateway.jsx b/web/src/pages/Setting/Payment/SettingsPaymentGateway.jsx index a4f1029a..d0398a24 100644 --- a/web/src/pages/Setting/Payment/SettingsPaymentGateway.jsx +++ b/web/src/pages/Setting/Payment/SettingsPaymentGateway.jsx @@ -18,19 +18,19 @@ For commercial licensing, please contact support@quantumnous.com */ import React, { useEffect, useState, useRef } from 'react'; -import { Button, Form, Row, Col, Typography, Spin } from '@douyinfe/semi-ui'; -const { Text } = Typography; +import { Banner, Button, Form, Row, Col, Spin } from '@douyinfe/semi-ui'; import { API, removeTrailingSlash, showError, showSuccess, - verifyJSON, } from '../../../helpers'; import { useTranslation } from 'react-i18next'; +import { Info } from 'lucide-react'; export default function SettingsPaymentGateway(props) { const { t } = useTranslation(); + const sectionTitle = props.hideSectionTitle ? undefined : t('易支付设置'); const [loading, setLoading] = useState(false); const [inputs, setInputs] = useState({ PayAddress: '', @@ -38,13 +38,7 @@ export default function SettingsPaymentGateway(props) { EpayKey: '', Price: 7.3, MinTopUp: 1, - TopupGroupRatio: '', - CustomCallbackAddress: '', - PayMethods: '', - AmountOptions: '', - AmountDiscount: '', }); - const [originInputs, setOriginInputs] = useState({}); const formApiRef = useRef(null); useEffect(() => { @@ -61,35 +55,9 @@ export default function SettingsPaymentGateway(props) { props.options.MinTopUp !== undefined ? parseFloat(props.options.MinTopUp) : 1, - TopupGroupRatio: props.options.TopupGroupRatio || '', - CustomCallbackAddress: props.options.CustomCallbackAddress || '', - PayMethods: props.options.PayMethods || '', - AmountOptions: props.options.AmountOptions || '', - AmountDiscount: props.options.AmountDiscount || '', }; - // 美化 JSON 展示 - try { - if (currentInputs.AmountOptions) { - currentInputs.AmountOptions = JSON.stringify( - JSON.parse(currentInputs.AmountOptions), - null, - 2, - ); - } - } catch {} - try { - if (currentInputs.AmountDiscount) { - currentInputs.AmountDiscount = JSON.stringify( - JSON.parse(currentInputs.AmountDiscount), - null, - 2, - ); - } - } catch {} - setInputs(currentInputs); - setOriginInputs({ ...currentInputs }); formApiRef.current.setValues(currentInputs); } }, [props.options]); @@ -104,40 +72,6 @@ export default function SettingsPaymentGateway(props) { return; } - if (originInputs['TopupGroupRatio'] !== inputs.TopupGroupRatio) { - if (!verifyJSON(inputs.TopupGroupRatio)) { - showError(t('充值分组倍率不是合法的 JSON 字符串')); - return; - } - } - - if (originInputs['PayMethods'] !== inputs.PayMethods) { - if (!verifyJSON(inputs.PayMethods)) { - showError(t('充值方式设置不是合法的 JSON 字符串')); - return; - } - } - - if ( - originInputs['AmountOptions'] !== inputs.AmountOptions && - inputs.AmountOptions.trim() !== '' - ) { - if (!verifyJSON(inputs.AmountOptions)) { - showError(t('自定义充值数量选项不是合法的 JSON 数组')); - return; - } - } - - if ( - originInputs['AmountDiscount'] !== inputs.AmountDiscount && - inputs.AmountDiscount.trim() !== '' - ) { - if (!verifyJSON(inputs.AmountDiscount)) { - showError(t('充值金额折扣配置不是合法的 JSON 对象')); - return; - } - } - setLoading(true); try { const options = [ @@ -156,32 +90,7 @@ export default function SettingsPaymentGateway(props) { if (inputs.MinTopUp !== '') { options.push({ key: 'MinTopUp', value: inputs.MinTopUp.toString() }); } - if (inputs.CustomCallbackAddress !== '') { - options.push({ - key: 'CustomCallbackAddress', - value: inputs.CustomCallbackAddress, - }); - } - if (originInputs['TopupGroupRatio'] !== inputs.TopupGroupRatio) { - options.push({ key: 'TopupGroupRatio', value: inputs.TopupGroupRatio }); - } - if (originInputs['PayMethods'] !== inputs.PayMethods) { - options.push({ key: 'PayMethods', value: inputs.PayMethods }); - } - if (originInputs['AmountOptions'] !== inputs.AmountOptions) { - options.push({ - key: 'payment_setting.amount_options', - value: inputs.AmountOptions, - }); - } - if (originInputs['AmountDiscount'] !== inputs.AmountDiscount) { - options.push({ - key: 'payment_setting.amount_discount', - value: inputs.AmountDiscount, - }); - } - // 发送请求 const requestQueue = options.map((opt) => API.put('/api/option/', { key: opt.key, @@ -191,7 +100,6 @@ export default function SettingsPaymentGateway(props) { const results = await Promise.all(requestQueue); - // 检查所有请求是否成功 const errorResults = results.filter((res) => !res.data.success); if (errorResults.length > 0) { errorResults.forEach((res) => { @@ -199,8 +107,6 @@ export default function SettingsPaymentGateway(props) { }); } else { showSuccess(t('更新成功')); - // 更新本地存储的原始值 - setOriginInputs({ ...inputs }); props.refresh && props.refresh(); } } catch (error) { @@ -216,12 +122,15 @@ export default function SettingsPaymentGateway(props) { onValueChange={handleFormChange} getFormApi={(api) => (formApiRef.current = api)} > - - - {t( - '(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)', + + } + description={t( + '当前仅支持易支付接口,回调地址请在通用设置中配置。', )} - + style={{ marginBottom: 16 }} + /> @@ -250,14 +159,7 @@ export default function SettingsPaymentGateway(props) { gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }} style={{ marginTop: 16 }} > - - - - + - + - - - - - - - - - - - - - - - - + diff --git a/web/src/pages/Setting/Payment/SettingsPaymentGatewayCreem.jsx b/web/src/pages/Setting/Payment/SettingsPaymentGatewayCreem.jsx index 41de8d20..dc1c310e 100644 --- a/web/src/pages/Setting/Payment/SettingsPaymentGatewayCreem.jsx +++ b/web/src/pages/Setting/Payment/SettingsPaymentGatewayCreem.jsx @@ -34,10 +34,11 @@ import { const { Text } = Typography; import { API, showError, showSuccess } from '../../../helpers'; import { useTranslation } from 'react-i18next'; -import { Plus, Trash2 } from 'lucide-react'; +import { BookOpen, Plus, Trash2 } from 'lucide-react'; export default function SettingsPaymentGatewayCreem(props) { const { t } = useTranslation(); + const sectionTitle = props.hideSectionTitle ? undefined : t('Creem 设置'); const [loading, setLoading] = useState(false); const [inputs, setInputs] = useState({ CreemApiKey: '', @@ -259,15 +260,22 @@ export default function SettingsPaymentGatewayCreem(props) { onValueChange={handleFormChange} getFormApi={(api) => (formApiRef.current = api)} > - - - {t('Creem 介绍')} - - Creem Official Site - -
-
- + + } + description={ + <> + {t('Creem 介绍')} + + Creem Official Site + +
+ {t('Creem Setting Tips')} + + } + style={{ marginBottom: 16 }} + /> @@ -281,7 +289,7 @@ export default function SettingsPaymentGatewayCreem(props) { diff --git a/web/src/pages/Setting/Payment/SettingsPaymentGatewayStripe.jsx b/web/src/pages/Setting/Payment/SettingsPaymentGatewayStripe.jsx index e4ddea11..c8d3da2e 100644 --- a/web/src/pages/Setting/Payment/SettingsPaymentGatewayStripe.jsx +++ b/web/src/pages/Setting/Payment/SettingsPaymentGatewayStripe.jsx @@ -18,16 +18,7 @@ For commercial licensing, please contact support@quantumnous.com */ import React, { useEffect, useState, useRef } from 'react'; -import { - Banner, - Button, - Form, - Row, - Col, - Typography, - Spin, -} from '@douyinfe/semi-ui'; -const { Text } = Typography; +import { Banner, Button, Form, Row, Col, Spin } from '@douyinfe/semi-ui'; import { API, removeTrailingSlash, @@ -35,9 +26,11 @@ import { showSuccess, } from '../../../helpers'; import { useTranslation } from 'react-i18next'; +import { BookOpen, TriangleAlert } from 'lucide-react'; export default function SettingsPaymentGateway(props) { const { t } = useTranslation(); + const sectionTitle = props.hideSectionTitle ? undefined : t('Stripe 设置'); const [loading, setLoading] = useState(false); const [inputs, setInputs] = useState({ StripeApiSecret: '', @@ -165,42 +158,53 @@ export default function SettingsPaymentGateway(props) { onValueChange={handleFormChange} getFormApi={(api) => (formApiRef.current = api)} > - - - Stripe 密钥、Webhook 等设置请 - - 点击此处 - - 进行设置,最好先在 - - 测试环境 - - 进行测试。 -
-
+ } + description={ + <> + Stripe 密钥、Webhook 等设置请 + + 点击此处 + + 进行设置,建议先在 + + 测试环境 + + 完成联调。 +
+ {t('回调地址')}: + {props.options.ServerAddress + ? removeTrailingSlash(props.options.ServerAddress) + : t('网站地址')} + /api/stripe/webhook + + } + style={{ marginBottom: 12 }} /> } + description='需要包含事件:checkout.session.completed 和 checkout.session.expired' + style={{ marginBottom: 16 }} /> @@ -209,7 +213,8 @@ export default function SettingsPaymentGateway(props) { @@ -217,7 +222,8 @@ export default function SettingsPaymentGateway(props) { @@ -231,6 +237,7 @@ export default function SettingsPaymentGateway(props) { precision={2} label={t('充值价格(x元/美金)')} placeholder={t('例如:7,就是7元/美金')} + extraText={t('按 1 美元对应的站内价格填写')} /> @@ -238,6 +245,7 @@ export default function SettingsPaymentGateway(props) { field='StripeMinTopUp' label={t('最低充值美元数量')} placeholder={t('例如:2,就是最低充值2$')} + extraText={t('用户单次最少可充值的美元数量')} /> diff --git a/web/src/pages/Setting/Payment/SettingsPaymentGatewayWaffo.jsx b/web/src/pages/Setting/Payment/SettingsPaymentGatewayWaffo.jsx index 29c8cdfa..46802820 100644 --- a/web/src/pages/Setting/Payment/SettingsPaymentGatewayWaffo.jsx +++ b/web/src/pages/Setting/Payment/SettingsPaymentGatewayWaffo.jsx @@ -31,13 +31,21 @@ import { Input, Space, } from '@douyinfe/semi-ui'; -import { API, showError, showSuccess } from '../../../helpers'; +import { + API, + removeTrailingSlash, + showError, + showSuccess, +} from '../../../helpers'; import { useTranslation } from 'react-i18next'; +import { BookOpen, TriangleAlert } from 'lucide-react'; const { Text } = Typography; +const toBoolean = (value) => value === true || value === 'true'; export default function SettingsPaymentGatewayWaffo(props) { const { t } = useTranslation(); + const sectionTitle = props.hideSectionTitle ? undefined : t('Waffo 设置'); const [loading, setLoading] = useState(false); const [inputs, setInputs] = useState({ WaffoEnabled: false, @@ -55,7 +63,6 @@ export default function SettingsPaymentGatewayWaffo(props) { WaffoNotifyUrl: '', WaffoReturnUrl: '', }); - const [originInputs, setOriginInputs] = useState({}); const formApiRef = useRef(null); const iconFileInputRef = useRef(null); @@ -93,14 +100,14 @@ export default function SettingsPaymentGatewayWaffo(props) { useEffect(() => { if (props.options && formApiRef.current) { const currentInputs = { - WaffoEnabled: props.options.WaffoEnabled === 'true' || props.options.WaffoEnabled === true, + WaffoEnabled: toBoolean(props.options.WaffoEnabled), WaffoApiKey: props.options.WaffoApiKey || '', WaffoPrivateKey: props.options.WaffoPrivateKey || '', WaffoPublicCert: props.options.WaffoPublicCert || '', WaffoSandboxPublicCert: props.options.WaffoSandboxPublicCert || '', WaffoSandboxApiKey: props.options.WaffoSandboxApiKey || '', WaffoSandboxPrivateKey: props.options.WaffoSandboxPrivateKey || '', - WaffoSandbox: props.options.WaffoSandbox === 'true', + WaffoSandbox: toBoolean(props.options.WaffoSandbox), WaffoMerchantId: props.options.WaffoMerchantId || '', WaffoCurrency: props.options.WaffoCurrency || 'USD', WaffoUnitPrice: parseFloat(props.options.WaffoUnitPrice) || 1.0, @@ -109,7 +116,6 @@ export default function SettingsPaymentGatewayWaffo(props) { WaffoReturnUrl: props.options.WaffoReturnUrl || '', }; setInputs(currentInputs); - setOriginInputs({ ...currentInputs }); formApiRef.current.setValues(currentInputs); // 解析支付方式列表 @@ -149,15 +155,30 @@ export default function SettingsPaymentGatewayWaffo(props) { options.push({ key: 'WaffoPrivateKey', value: inputs.WaffoPrivateKey }); } - options.push({ key: 'WaffoPublicCert', value: inputs.WaffoPublicCert || '' }); - options.push({ key: 'WaffoSandboxPublicCert', value: inputs.WaffoSandboxPublicCert || '' }); + options.push({ + key: 'WaffoPublicCert', + value: inputs.WaffoPublicCert || '', + }); + options.push({ + key: 'WaffoSandboxPublicCert', + value: inputs.WaffoSandboxPublicCert || '', + }); if (inputs.WaffoSandboxApiKey && inputs.WaffoSandboxApiKey !== '') { - options.push({ key: 'WaffoSandboxApiKey', value: inputs.WaffoSandboxApiKey }); + options.push({ + key: 'WaffoSandboxApiKey', + value: inputs.WaffoSandboxApiKey, + }); } - if (inputs.WaffoSandboxPrivateKey && inputs.WaffoSandboxPrivateKey !== '') { - options.push({ key: 'WaffoSandboxPrivateKey', value: inputs.WaffoSandboxPrivateKey }); + if ( + inputs.WaffoSandboxPrivateKey && + inputs.WaffoSandboxPrivateKey !== '' + ) { + options.push({ + key: 'WaffoSandboxPrivateKey', + value: inputs.WaffoSandboxPrivateKey, + }); } options.push({ @@ -165,7 +186,10 @@ export default function SettingsPaymentGatewayWaffo(props) { value: inputs.WaffoSandbox ? 'true' : 'false', }); - options.push({ key: 'WaffoMerchantId', value: inputs.WaffoMerchantId || '' }); + options.push({ + key: 'WaffoMerchantId', + value: inputs.WaffoMerchantId || '', + }); options.push({ key: 'WaffoCurrency', value: inputs.WaffoCurrency || '' }); options.push({ @@ -178,8 +202,14 @@ export default function SettingsPaymentGatewayWaffo(props) { value: String(inputs.WaffoMinTopUp || 1), }); - options.push({ key: 'WaffoNotifyUrl', value: inputs.WaffoNotifyUrl || '' }); - options.push({ key: 'WaffoReturnUrl', value: inputs.WaffoReturnUrl || '' }); + options.push({ + key: 'WaffoNotifyUrl', + value: inputs.WaffoNotifyUrl || '', + }); + options.push({ + key: 'WaffoReturnUrl', + value: inputs.WaffoReturnUrl || '', + }); // 保存支付方式列表 options.push({ @@ -205,8 +235,6 @@ export default function SettingsPaymentGatewayWaffo(props) { }); } else { showSuccess(t('更新成功')); - // 更新本地存储的原始值 - setOriginInputs({ ...inputs }); props.refresh?.(); } } catch (error) { @@ -218,7 +246,12 @@ export default function SettingsPaymentGatewayWaffo(props) { // 打开新增弹窗 const openAddPayMethodModal = () => { setEditingPayMethodIndex(-1); - setPayMethodForm({ name: '', icon: '', payMethodType: '', payMethodName: '' }); + setPayMethodForm({ + name: '', + icon: '', + payMethodType: '', + payMethodName: '', + }); setPayMethodModalVisible(true); }; @@ -324,19 +357,32 @@ export default function SettingsPaymentGatewayWaffo(props) { onValueChange={handleFormChange} getFormApi={(api) => (formApiRef.current = api)} > - - - {t('Waffo 是一个支付聚合平台,支持多种支付方式。')} - - Waffo Official Site - -
-
+ } + description={ + <> + Waffo 密钥、商户和支付方式等设置请 + + 点击此处 + + 进行配置,切换沙盒模式时请同步填写对应环境的密钥。 +
+ {t('回调地址')}: + {props.options.ServerAddress + ? removeTrailingSlash(props.options.ServerAddress) + : t('网站地址')} + /api/waffo/webhook + + } + style={{ marginBottom: 12 }} + /> + } + description={t('请确认商户和所选环境密钥一致。')} + style={{ marginBottom: 16 }} /> @@ -356,161 +402,188 @@ export default function SettingsPaymentGatewayWaffo(props) { size='default' checkedText='|' uncheckedText='〇' - extraText={t('启用后将使用 Waffo 沙盒环境')} + extraText={t('用于切换当前下单和回调校验所使用的环境')} /> - - - - - - - - - - - - - + - - + + + + + - - - - - - - + - - - - + + + + + + + + + + + + + + + + +
+ + + {t( + '这里配置 Waffo 下展示给用户的 Card、Apple Pay、Google Pay 等子支付方式。', + )} + +
+ +
+ index} + pagination={false} + size='small' + empty={ + {t('暂无支付方式,点击上方按钮新增')} + } + /> - {/* 支付方式配置区块(独立于 Form,使用独立状态管理) */} -
- {t('支付方式')} - - {t('配置 Waffo 充值时可用的支付方式,保存后在充值页面展示给用户。')} - -
- -
-
index} - pagination={false} - size='small' - empty={{t('暂无支付方式,点击上方按钮新增')}} - /> - - - {/* 新增/编辑支付方式弹窗 */} setPayMethodModalVisible(false)} @@ -521,14 +594,22 @@ export default function SettingsPaymentGatewayWaffo(props) {
{t('显示名称')} - * + + * +
setPayMethodForm({ ...payMethodForm, name: val })} + onChange={(val) => + setPayMethodForm({ ...payMethodForm, name: val }) + } placeholder={t('例如:Credit Card')} /> - {t('用户在充值页面看到的支付方式名称,例如:Credit Card')} + + {t('用户在充值页面看到的支付方式名称,例如:Credit Card')} +
@@ -574,32 +655,44 @@ export default function SettingsPaymentGatewayWaffo(props) { )}
- {t('上传 PNG/JPG/SVG 图片,建议尺寸 ≤ 128×128px')} + + {t('上传 PNG/JPG/SVG 图片,建议尺寸 ≤ 128×128px')} +
- {t('Pay Method Type')} + {t('支付方式类型')}
setPayMethodForm({ ...payMethodForm, payMethodType: val })} + onChange={(val) => + setPayMethodForm({ ...payMethodForm, payMethodType: val }) + } placeholder='CREDITCARD,DEBITCARD' maxLength={64} /> - {t('Waffo API 参数,可空,例如:CREDITCARD,DEBITCARD(最多64位)')} + + {t( + 'Waffo API 参数,可空,例如:CREDITCARD,DEBITCARD(最多64位)', + )} +
- {t('Pay Method Name')} + {t('支付方式名称')}
setPayMethodForm({ ...payMethodForm, payMethodName: val })} + onChange={(val) => + setPayMethodForm({ ...payMethodForm, payMethodName: val }) + } placeholder={t('可空')} maxLength={64} /> - {t('Waffo API 参数,可空(最多64位)')} + + {t('Waffo API 参数,可空(最多64位)')} +
diff --git a/web/src/pages/Setting/Payment/SettingsPaymentGatewayWaffoPancake.jsx b/web/src/pages/Setting/Payment/SettingsPaymentGatewayWaffoPancake.jsx new file mode 100644 index 00000000..202576df --- /dev/null +++ b/web/src/pages/Setting/Payment/SettingsPaymentGatewayWaffoPancake.jsx @@ -0,0 +1,411 @@ +/* +Copyright (C) 2025 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 . + +For commercial licensing, please contact support@quantumnous.com +*/ + +import React, { useEffect, useRef, useState } from 'react'; +import { Banner, Button, Col, Form, Row, Spin } from '@douyinfe/semi-ui'; +import { + API, + removeTrailingSlash, + showError, + showSuccess, +} from '../../../helpers'; +import { useTranslation } from 'react-i18next'; +import { BookOpen, TriangleAlert } from 'lucide-react'; + +const defaultInputs = { + WaffoPancakeEnabled: false, + WaffoPancakeSandbox: false, + WaffoPancakeMerchantID: '', + WaffoPancakePrivateKey: '', + WaffoPancakeWebhookPublicKey: '', + WaffoPancakeWebhookTestKey: '', + WaffoPancakeStoreID: '', + WaffoPancakeProductID: '', + WaffoPancakeReturnURL: '', + WaffoPancakeCurrency: 'USD', + WaffoPancakeUnitPrice: 1.0, + WaffoPancakeMinTopUp: 1, +}; + +const toBoolean = (value) => value === true || value === 'true'; + +export default function SettingsPaymentGatewayWaffoPancake(props) { + const { t } = useTranslation(); + const sectionTitle = props.hideSectionTitle + ? undefined + : t('Waffo Pancake 设置'); + const [loading, setLoading] = useState(false); + const [inputs, setInputs] = useState(defaultInputs); + const formApiRef = useRef(null); + + useEffect(() => { + if (!props.options || !formApiRef.current) return; + + const currentInputs = { + WaffoPancakeEnabled: toBoolean(props.options.WaffoPancakeEnabled), + WaffoPancakeSandbox: toBoolean(props.options.WaffoPancakeSandbox), + WaffoPancakeMerchantID: props.options.WaffoPancakeMerchantID || '', + WaffoPancakePrivateKey: props.options.WaffoPancakePrivateKey || '', + WaffoPancakeWebhookPublicKey: + props.options.WaffoPancakeWebhookPublicKey || '', + WaffoPancakeWebhookTestKey: + props.options.WaffoPancakeWebhookTestKey || '', + WaffoPancakeStoreID: props.options.WaffoPancakeStoreID || '', + WaffoPancakeProductID: props.options.WaffoPancakeProductID || '', + WaffoPancakeReturnURL: props.options.WaffoPancakeReturnURL || '', + WaffoPancakeCurrency: props.options.WaffoPancakeCurrency || 'USD', + WaffoPancakeUnitPrice: + props.options.WaffoPancakeUnitPrice !== undefined + ? parseFloat(props.options.WaffoPancakeUnitPrice) + : 1.0, + WaffoPancakeMinTopUp: + props.options.WaffoPancakeMinTopUp !== undefined + ? parseFloat(props.options.WaffoPancakeMinTopUp) + : 1, + }; + + setInputs(currentInputs); + formApiRef.current.setValues(currentInputs); + }, [props.options]); + + const handleFormChange = (values) => { + setInputs(values); + }; + + const submitWaffoPancakeSetting = async () => { + const values = { + ...inputs, + ...(formApiRef.current?.getValues?.() || {}), + }; + values.WaffoPancakeEnabled = toBoolean(values.WaffoPancakeEnabled); + values.WaffoPancakeSandbox = toBoolean(values.WaffoPancakeSandbox); + const currentWebhookField = values.WaffoPancakeSandbox + ? 'WaffoPancakeWebhookTestKey' + : 'WaffoPancakeWebhookPublicKey'; + const currentWebhookLabel = values.WaffoPancakeSandbox + ? t('Webhook 公钥(测试环境)') + : t('Webhook 公钥(生产环境)'); + + if (values.WaffoPancakeEnabled && !values.WaffoPancakeMerchantID.trim()) { + showError(t('请输入商户 ID')); + return; + } + + if (values.WaffoPancakeEnabled && !values.WaffoPancakeStoreID.trim()) { + showError(t('请输入 Store ID')); + return; + } + + if (values.WaffoPancakeEnabled && !values.WaffoPancakeProductID.trim()) { + showError(t('请输入 Product ID')); + return; + } + + if ( + values.WaffoPancakeEnabled && + !String(values[currentWebhookField] || '').trim() + ) { + showError(currentWebhookLabel); + return; + } + + if ( + values.WaffoPancakeEnabled && + Number(values.WaffoPancakeUnitPrice) <= 0 + ) { + showError(t('充值价格必须大于 0')); + return; + } + + if (values.WaffoPancakeEnabled && Number(values.WaffoPancakeMinTopUp) < 1) { + showError(t('最低充值美元数量必须大于 0')); + return; + } + + setLoading(true); + try { + const options = [ + { + key: 'WaffoPancakeEnabled', + value: values.WaffoPancakeEnabled ? 'true' : 'false', + }, + { + key: 'WaffoPancakeSandbox', + value: values.WaffoPancakeSandbox ? 'true' : 'false', + }, + { + key: 'WaffoPancakeMerchantID', + value: values.WaffoPancakeMerchantID || '', + }, + { + key: 'WaffoPancakeStoreID', + value: values.WaffoPancakeStoreID || '', + }, + { + key: 'WaffoPancakeProductID', + value: values.WaffoPancakeProductID || '', + }, + { + key: 'WaffoPancakeReturnURL', + value: removeTrailingSlash(values.WaffoPancakeReturnURL || ''), + }, + { + key: 'WaffoPancakeCurrency', + value: values.WaffoPancakeCurrency || 'USD', + }, + { + key: 'WaffoPancakeUnitPrice', + value: String(values.WaffoPancakeUnitPrice), + }, + { + key: 'WaffoPancakeMinTopUp', + value: String(values.WaffoPancakeMinTopUp), + }, + ]; + + if ((values.WaffoPancakePrivateKey || '').trim()) { + options.push({ + key: 'WaffoPancakePrivateKey', + value: values.WaffoPancakePrivateKey, + }); + } + + if ((values.WaffoPancakeWebhookPublicKey || '').trim()) { + options.push({ + key: 'WaffoPancakeWebhookPublicKey', + value: values.WaffoPancakeWebhookPublicKey, + }); + } + + if ((values.WaffoPancakeWebhookTestKey || '').trim()) { + options.push({ + key: 'WaffoPancakeWebhookTestKey', + value: values.WaffoPancakeWebhookTestKey, + }); + } + + const results = await Promise.all( + options.map((opt) => + API.put('/api/option/', { + key: opt.key, + value: opt.value, + }), + ), + ); + + const errorResults = results.filter((res) => !res.data.success); + if (errorResults.length > 0) { + errorResults.forEach((res) => showError(res.data.message)); + return; + } + + showSuccess(t('更新成功')); + props.refresh?.(); + } catch (error) { + showError(t('更新失败')); + } finally { + setLoading(false); + } + }; + + return ( + +
(formApiRef.current = api)} + > + + } + description={ + <> + Waffo Pancake 的商户、商品和签名密钥请 + + 点击此处 + + 获取,建议先在测试环境完成联调。 +
+ {t('回调地址')}: + {props.options.ServerAddress + ? removeTrailingSlash(props.options.ServerAddress) + : t('网站地址')} + /api/waffo-pancake/webhook + + } + style={{ marginBottom: 12 }} + /> + } + description={t( + '请确认 Merchant、Store、Product 和所选环境密钥一致。', + )} + style={{ marginBottom: 16 }} + /> + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} From 600ae85998eaec63dd7dbd81d57dbaa177b11b08 Mon Sep 17 00:00:00 2001 From: uskyu Date: Sun, 19 Apr 2026 23:09:33 +0800 Subject: [PATCH 34/46] Show user ID in admin topup bills --- web/src/components/topup/modals/TopupHistoryModal.jsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/web/src/components/topup/modals/TopupHistoryModal.jsx b/web/src/components/topup/modals/TopupHistoryModal.jsx index 9659e929..1abd45df 100644 --- a/web/src/components/topup/modals/TopupHistoryModal.jsx +++ b/web/src/components/topup/modals/TopupHistoryModal.jsx @@ -161,6 +161,16 @@ const TopupHistoryModal = ({ visible, onCancel, t }) => { const columns = useMemo(() => { const baseColumns = [ + ...(userIsAdmin + ? [ + { + title: t('用户ID'), + dataIndex: 'user_id', + key: 'user_id', + render: (userId) => {userId ?? '-'}, + }, + ] + : []), { title: t('订单号'), dataIndex: 'trade_no', From b60bc94f9ce69a5a49c411a3ff7dc31c4effb3f6 Mon Sep 17 00:00:00 2001 From: feitianbubu Date: Tue, 21 Apr 2026 17:20:01 +0800 Subject: [PATCH 35/46] feat: show last used time column in tokens table --- web/src/components/table/tokens/TokensColumnDefs.jsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/web/src/components/table/tokens/TokensColumnDefs.jsx b/web/src/components/table/tokens/TokensColumnDefs.jsx index 86b33dd1..71edfcdc 100644 --- a/web/src/components/table/tokens/TokensColumnDefs.jsx +++ b/web/src/components/table/tokens/TokensColumnDefs.jsx @@ -536,6 +536,13 @@ export const getTokensColumns = ({ return
{renderTimestamp(text)}
; }, }, + { + title: t('最后使用时间'), + dataIndex: 'accessed_time', + render: (text, record, index) => { + return
{text ? renderTimestamp(text) : '-'}
; + }, + }, { title: t('过期时间'), dataIndex: 'expired_time', From 6afaa58d287381301c75feb1e4d065207b0e094e Mon Sep 17 00:00:00 2001 From: gaoren002 <83566620+gaoren002@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:22:09 +0800 Subject: [PATCH 36/46] fix(topup): import missing Tag in recharge card (#4388) --- web/src/components/topup/RechargeCard.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/src/components/topup/RechargeCard.jsx b/web/src/components/topup/RechargeCard.jsx index 17ff77e4..1e2923ca 100644 --- a/web/src/components/topup/RechargeCard.jsx +++ b/web/src/components/topup/RechargeCard.jsx @@ -31,6 +31,7 @@ import { Col, Spin, Tooltip, + Tag, Tabs, TabPane, } from '@douyinfe/semi-ui'; From d586a567e459e13cc73df707533e4200a0b07dcb Mon Sep 17 00:00:00 2001 From: Seefs <40468931+seefs001@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:54:28 +0800 Subject: [PATCH 37/46] chore: refine codex usage modal layout (#4386) * chore: refine codex usage modal layout * fix: polish codex usage modal responsiveness --- .../table/channels/modals/CodexUsageModal.jsx | 197 ++++++++++++++++-- 1 file changed, 178 insertions(+), 19 deletions(-) diff --git a/web/src/components/table/channels/modals/CodexUsageModal.jsx b/web/src/components/table/channels/modals/CodexUsageModal.jsx index 1a42eafd..3b5ad035 100644 --- a/web/src/components/table/channels/modals/CodexUsageModal.jsx +++ b/web/src/components/table/channels/modals/CodexUsageModal.jsx @@ -29,6 +29,7 @@ import { Collapse, } from '@douyinfe/semi-ui'; import { API, showError } from '../../../../helpers'; +import { MOBILE_BREAKPOINT } from '../../../../hooks/common/useIsMobile'; const { Text } = Typography; @@ -98,10 +99,12 @@ const resolveRateLimitWindows = (data) => { } if (!fiveHourWindow) { - fiveHourWindow = windows.find((windowData) => windowData !== weeklyWindow) ?? null; + fiveHourWindow = + windows.find((windowData) => windowData !== weeklyWindow) ?? null; } if (!weeklyWindow) { - weeklyWindow = windows.find((windowData) => windowData !== fiveHourWindow) ?? null; + weeklyWindow = + windows.find((windowData) => windowData !== fiveHourWindow) ?? null; } return { fiveHourWindow, weeklyWindow }; @@ -135,6 +138,40 @@ const getDisplayText = (value) => { return String(value).trim(); }; +const isMobileViewport = () => + typeof window !== 'undefined' && window.innerWidth < MOBILE_BREAKPOINT; + +const getCodexUsageModalLayout = () => { + if (isMobileViewport()) { + return { + width: 'calc(100vw - 16px)', + style: { + top: 8, + maxWidth: 'calc(100vw - 16px)', + margin: '0 auto', + }, + bodyStyle: { + maxHeight: 'calc(100vh - 148px)', + overflowY: 'auto', + padding: '16px 16px 12px', + }, + }; + } + + return { + width: 900, + style: { + top: 24, + maxWidth: 'min(900px, 92vw)', + }, + bodyStyle: { + maxHeight: 'calc(100vh - 172px)', + overflowY: 'auto', + padding: '20px 24px 16px', + }, + }; +}; + const formatAccountTypeLabel = (value, t) => { const tt = typeof t === 'function' ? t : (v) => v; const normalized = normalizePlanType(value); @@ -224,7 +261,7 @@ const RateLimitWindowCard = ({ t, title, windowData }) => { return (
-
+
{title}
{tt('重置时间:')} @@ -262,12 +299,86 @@ const RateLimitWindowCard = ({ t, title, windowData }) => { ); }; +const RateLimitWindowGrid = ({ t, fiveHourWindow, weeklyWindow }) => { + const tt = typeof t === 'function' ? t : (v) => v; + + return ( +
+ + +
+ ); +}; + +const RateLimitGroupSection = ({ + t, + title, + description, + rateLimitSource, + statusTag, + meteredFeature, +}) => { + const tt = typeof t === 'function' ? t : (v) => v; + const { fiveHourWindow, weeklyWindow } = + resolveRateLimitWindows(rateLimitSource); + const featureText = getDisplayText(meteredFeature); + + return ( +
+
+
+
+
+ {title} +
+ {statusTag} +
+ {(description || featureText) && ( +
+ {description ? {description} : null} + {featureText ? ( +
+ + metered_feature + + + {featureText} + +
+ ) : null} +
+ )} +
+
+ + +
+ ); +}; + const CodexUsageView = ({ t, record, payload, onCopy, onRefresh }) => { const tt = typeof t === 'function' ? t : (v) => v; const [showRawJson, setShowRawJson] = useState(false); const data = payload?.data ?? null; const rateLimit = data?.rate_limit ?? {}; - const { fiveHourWindow, weeklyWindow } = resolveRateLimitWindows(data); + const additionalRateLimits = Array.isArray(data?.additional_rate_limits) + ? data.additional_rate_limits.filter( + (item) => + item && typeof item === 'object' && Object.keys(item).length > 0, + ) + : []; const upstreamStatus = payload?.upstream_status; const accountType = data?.plan_type ?? rateLimit?.plan_type; const accountTypeLabel = formatAccountTypeLabel(accountType, tt); @@ -277,7 +388,9 @@ const CodexUsageView = ({ t, record, payload, onCopy, onRefresh }) => { const email = data?.email; const accountId = data?.account_id; const errorMessage = - payload?.success === false ? getDisplayText(payload?.message) || tt('获取用量失败') : ''; + payload?.success === false + ? getDisplayText(payload?.message) || tt('获取用量失败') + : ''; const rawText = typeof data === 'string' ? data : JSON.stringify(data ?? payload, null, 2); @@ -313,7 +426,12 @@ const CodexUsageView = ({ t, record, payload, onCopy, onRefresh }) => {
-
@@ -355,22 +473,61 @@ const CodexUsageView = ({ t, record, payload, onCopy, onRefresh }) => { {tt('额度窗口')} - {tt('用于观察当前帐号在 Codex 上游的限额使用情况')} + {tt( + '用于观察当前帐号在 Codex 上游的基础限额与附加计费能力使用情况', + )} -
- + - + + {additionalRateLimits.length > 0 ? ( +
+
+
+ {tt('附加额度')} +
+ + {tt('按模型或能力拆分的附加计费能力窗口')} + +
+ +
+ {additionalRateLimits.map((item, index) => { + const limitName = + getDisplayText(item?.limit_name) || + getDisplayText(item?.metered_feature) || + `${tt('附加额度')} ${index + 1}`; + + return ( +
0 ? 'border-t border-semi-color-border pt-4' : '' + } + > + +
+ ); + })} +
+
+ ) : null}
{ export const openCodexUsageModal = ({ t, record, payload, onCopy }) => { const tt = typeof t === 'function' ? t : (v) => v; + const layout = getCodexUsageModalLayout(); Modal.info({ title: tt('Codex 帐号与用量'), - centered: true, - width: 900, - style: { maxWidth: '95vw' }, + centered: false, + width: layout.width, + style: layout.style, + bodyStyle: layout.bodyStyle, content: ( Date: Wed, 22 Apr 2026 22:54:41 +0800 Subject: [PATCH 38/46] fix: use stream for codex auto test (#4325) --- controller/channel-test.go | 40 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/controller/channel-test.go b/controller/channel-test.go index 8d62a4ac..78a90ec6 100644 --- a/controller/channel-test.go +++ b/controller/channel-test.go @@ -460,7 +460,7 @@ func testChannel(channel *model.Channel, testModel string, endpointType string, newAPIError: types.NewOpenAIError(err, types.ErrorCodeReadResponseBodyFailed, http.StatusInternalServerError), } } - if bodyErr := detectErrorFromTestResponseBody(respBody); bodyErr != nil { + if bodyErr := validateTestResponseBody(respBody, isStream); bodyErr != nil { return testResult{ context: c, localErr: bodyErr, @@ -570,6 +570,42 @@ func detectErrorFromTestResponseBody(respBody []byte) error { return nil } +func validateStreamTestResponseBody(respBody []byte) error { + b := bytes.TrimSpace(respBody) + if len(b) == 0 { + return errors.New("stream response body is empty") + } + + for _, line := range bytes.Split(b, []byte{'\n'}) { + line = bytes.TrimSpace(line) + if len(line) == 0 || !bytes.HasPrefix(line, []byte("data:")) { + continue + } + payload := bytes.TrimSpace(bytes.TrimPrefix(line, []byte("data:"))) + if len(payload) == 0 || bytes.Equal(payload, []byte("[DONE]")) { + continue + } + + return nil + } + + return errors.New("stream response body does not contain a valid stream event") +} + +func validateTestResponseBody(respBody []byte, isStream bool) error { + if bodyErr := detectErrorFromTestResponseBody(respBody); bodyErr != nil { + return bodyErr + } + if isStream { + return validateStreamTestResponseBody(respBody) + } + return nil +} + +func shouldUseStreamForAutomaticChannelTest(channel *model.Channel) bool { + return channel != nil && channel.Type == constant.ChannelTypeCodex +} + func detectErrorMessageFromJSONBytes(jsonBytes []byte) string { if len(jsonBytes) == 0 { return "" @@ -822,7 +858,7 @@ func testAllChannels(notify bool) error { } isChannelEnabled := channel.Status == common.ChannelStatusEnabled tik := time.Now() - result := testChannel(channel, "", "", false) + result := testChannel(channel, "", "", shouldUseStreamForAutomaticChannelTest(channel)) tok := time.Now() milliseconds := tok.Sub(tik).Milliseconds() From e729b22197598ce95c79a83d189af767d939b1e1 Mon Sep 17 00:00:00 2001 From: Seefs <40468931+seefs001@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:54:52 +0800 Subject: [PATCH 39/46] fix: refresh codex credentials for auto-disabled channels (#4324) --- service/codex_credential_refresh_task.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/service/codex_credential_refresh_task.go b/service/codex_credential_refresh_task.go index 627ab929..5cc53fea 100644 --- a/service/codex_credential_refresh_task.go +++ b/service/codex_credential_refresh_task.go @@ -28,6 +28,10 @@ var ( codexCredentialRefreshRunning atomic.Bool ) +func shouldAutoRefreshCodexChannelStatus(status int) bool { + return status == common.ChannelStatusEnabled || status == common.ChannelStatusAutoDisabled +} + func StartCodexCredentialAutoRefreshTask() { codexCredentialRefreshOnce.Do(func() { if !common.IsMasterNode { @@ -65,7 +69,11 @@ func runCodexCredentialAutoRefreshOnce() { var channels []*model.Channel err := model.DB. Select("id", "name", "key", "status", "channel_info"). - Where("type = ? AND status = 1", constant.ChannelTypeCodex). + Where("type = ? AND (status = ? OR status = ?)", + constant.ChannelTypeCodex, + common.ChannelStatusEnabled, + common.ChannelStatusAutoDisabled, + ). Order("id asc"). Limit(codexCredentialRefreshBatchSize). Offset(offset). From 1d83b5472ac2f90c72acd9f8e9d26e42a4641dac Mon Sep 17 00:00:00 2001 From: Seefs <40468931+seefs001@users.noreply.github.com> Date: Wed, 22 Apr 2026 22:55:06 +0800 Subject: [PATCH 40/46] fix: require proper verification for passkey changes (#4393) --- controller/passkey.go | 70 +++++++++ controller/secure_verification.go | 10 +- middleware/secure_verification.go | 22 +-- .../components/settings/PersonalSetting.jsx | 145 +++++++++++++++--- 4 files changed, 214 insertions(+), 33 deletions(-) diff --git a/controller/passkey.go b/controller/passkey.go index d37fb9f7..79930fdf 100644 --- a/controller/passkey.go +++ b/controller/passkey.go @@ -36,6 +36,10 @@ func PasskeyRegisterBegin(c *gin.Context) { return } + if !requirePasskeyRegistrationVerification(c, user.Id) { + return + } + credential, err := model.GetPasskeyByUserID(user.Id) if err != nil && !errors.Is(err, model.ErrPasskeyNotFound) { common.ApiError(c, err) @@ -96,6 +100,10 @@ func PasskeyRegisterFinish(c *gin.Context) { return } + if !requirePasskeyRegistrationVerification(c, user.Id) { + return + } + wa, err := passkeysvc.BuildWebAuthn(c.Request) if err != nil { common.ApiError(c, err) @@ -151,6 +159,10 @@ func PasskeyDelete(c *gin.Context) { return } + if !requirePasskeyDeleteVerification(c, user.Id) { + return + } + if err := model.DeletePasskeyByUserID(user.Id); err != nil { common.ApiError(c, err) return @@ -474,6 +486,7 @@ func PasskeyVerifyFinish(c *gin.Context) { // Mark passkey as ready; /api/verify will convert this into the final secure verification session. session.Set(PasskeyReadySessionKey, time.Now().Unix()) session.Delete(SecureVerificationSessionKey) + session.Delete(secureVerificationMethodSessionKey) if err := session.Save(); err != nil { common.ApiError(c, fmt.Errorf("保存验证状态失败: %v", err)) return @@ -504,3 +517,60 @@ func getSessionUser(c *gin.Context) (*model.User, error) { } return user, nil } + +func requirePasskeyRegistrationVerification(c *gin.Context, userID int) bool { + twoFA, err := model.GetTwoFAByUserId(userID) + if err != nil { + common.ApiError(c, err) + return false + } + if twoFA == nil || !twoFA.IsEnabled { + return true + } + return requireSecureVerificationMethod(c, secureVerificationMethod2FA) +} + +func requirePasskeyDeleteVerification(c *gin.Context, userID int) bool { + twoFA, err := model.GetTwoFAByUserId(userID) + if err != nil { + common.ApiError(c, err) + return false + } + if twoFA != nil && twoFA.IsEnabled { + return requireSecureVerificationMethod(c, secureVerificationMethod2FA) + } + + _, err = model.GetPasskeyByUserID(userID) + if err != nil { + if errors.Is(err, model.ErrPasskeyNotFound) { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": "该用户尚未绑定 Passkey", + }) + return false + } + common.ApiError(c, err) + return false + } + + return requireSecureVerificationMethod(c, secureVerificationMethodPasskey) +} + +func requireSecureVerificationMethod(c *gin.Context, method string) bool { + session := sessions.Default(c) + verifiedAt, ok := session.Get(SecureVerificationSessionKey).(int64) + if !ok || time.Now().Unix()-verifiedAt >= SecureVerificationTimeout { + session.Delete(SecureVerificationSessionKey) + session.Delete(secureVerificationMethodSessionKey) + _ = session.Save() + common.ApiErrorMsg(c, "请先完成安全验证") + return false + } + + if verifiedMethod, ok := session.Get(secureVerificationMethodSessionKey).(string); !ok || verifiedMethod != method { + common.ApiErrorMsg(c, "请先完成对应的安全验证") + return false + } + + return true +} diff --git a/controller/secure_verification.go b/controller/secure_verification.go index b229a66b..7640269e 100644 --- a/controller/secure_verification.go +++ b/controller/secure_verification.go @@ -13,7 +13,10 @@ import ( const ( // SecureVerificationSessionKey means the user has fully passed secure verification. - SecureVerificationSessionKey = "secure_verified_at" + SecureVerificationSessionKey = "secure_verified_at" + secureVerificationMethodSessionKey = "secure_verified_method" + secureVerificationMethod2FA = "2fa" + secureVerificationMethodPasskey = "passkey" // PasskeyReadySessionKey means WebAuthn finished and /api/verify can finalize step-up verification. PasskeyReadySessionKey = "secure_passkey_ready_at" // SecureVerificationTimeout 验证有效期(秒) @@ -120,7 +123,7 @@ func UniversalVerify(c *gin.Context) { } // 验证成功,在 session 中记录时间戳 - now, err := setSecureVerificationSession(c) + now, err := setSecureVerificationSession(c, req.Method) if err != nil { common.ApiError(c, fmt.Errorf("保存验证状态失败: %v", err)) return @@ -139,11 +142,12 @@ func UniversalVerify(c *gin.Context) { }) } -func setSecureVerificationSession(c *gin.Context) (int64, error) { +func setSecureVerificationSession(c *gin.Context, method string) (int64, error) { session := sessions.Default(c) session.Delete(PasskeyReadySessionKey) now := time.Now().Unix() session.Set(SecureVerificationSessionKey, now) + session.Set(secureVerificationMethodSessionKey, method) if err := session.Save(); err != nil { return 0, err } diff --git a/middleware/secure_verification.go b/middleware/secure_verification.go index 19fae9a5..b218f6b1 100644 --- a/middleware/secure_verification.go +++ b/middleware/secure_verification.go @@ -10,7 +10,8 @@ import ( const ( // SecureVerificationSessionKey 安全验证的 session key(与 controller 保持一致) - SecureVerificationSessionKey = "secure_verified_at" + SecureVerificationSessionKey = "secure_verified_at" + secureVerificationMethodSessionKey = "secure_verified_method" // SecureVerificationTimeout 验证有效期(秒) SecureVerificationTimeout = 300 // 5分钟 ) @@ -48,8 +49,7 @@ func SecureVerificationRequired() gin.HandlerFunc { verifiedAt, ok := verifiedAtRaw.(int64) if !ok { // session 数据格式错误 - session.Delete(SecureVerificationSessionKey) - _ = session.Save() + clearSecureVerificationSession(session) c.JSON(http.StatusForbidden, gin.H{ "success": false, "message": "验证状态异常,请重新验证", @@ -63,8 +63,7 @@ func SecureVerificationRequired() gin.HandlerFunc { elapsed := time.Now().Unix() - verifiedAt if elapsed >= SecureVerificationTimeout { // 验证已过期,清除 session - session.Delete(SecureVerificationSessionKey) - _ = session.Save() + clearSecureVerificationSession(session) c.JSON(http.StatusForbidden, gin.H{ "success": false, "message": "验证已过期,请重新验证", @@ -74,11 +73,16 @@ func SecureVerificationRequired() gin.HandlerFunc { return } - // 验证有效,继续处理请求 c.Next() } } +func clearSecureVerificationSession(session sessions.Session) { + session.Delete(SecureVerificationSessionKey) + session.Delete(secureVerificationMethodSessionKey) + _ = session.Save() +} + // OptionalSecureVerification 可选的安全验证中间件 // 如果用户已验证,则在 context 中设置标记,但不阻止请求继续 // 用于某些需要区分是否已验证的场景 @@ -109,8 +113,7 @@ func OptionalSecureVerification() gin.HandlerFunc { elapsed := time.Now().Unix() - verifiedAt if elapsed >= SecureVerificationTimeout { - session.Delete(SecureVerificationSessionKey) - _ = session.Save() + clearSecureVerificationSession(session) c.Set("secure_verified", false) c.Next() return @@ -126,6 +129,5 @@ func OptionalSecureVerification() gin.HandlerFunc { // 用于用户登出或需要强制重新验证的场景 func ClearSecureVerification(c *gin.Context) { session := sessions.Default(c) - session.Delete(SecureVerificationSessionKey) - _ = session.Save() + clearSecureVerificationSession(session) } diff --git a/web/src/components/settings/PersonalSetting.jsx b/web/src/components/settings/PersonalSetting.jsx index 4f9ce2d6..e735c877 100644 --- a/web/src/components/settings/PersonalSetting.jsx +++ b/web/src/components/settings/PersonalSetting.jsx @@ -45,6 +45,8 @@ import EmailBindModal from './personal/modals/EmailBindModal'; import WeChatBindModal from './personal/modals/WeChatBindModal'; import AccountDeleteModal from './personal/modals/AccountDeleteModal'; import ChangePasswordModal from './personal/modals/ChangePasswordModal'; +import SecureVerificationModal from '../common/modals/SecureVerificationModal'; +import { useSecureVerification } from '../../hooks/common/useSecureVerification'; const PersonalSetting = () => { const [userState, userDispatch] = useContext(UserContext); @@ -76,6 +78,10 @@ const PersonalSetting = () => { const [passkeyRegisterLoading, setPasskeyRegisterLoading] = useState(false); const [passkeyDeleteLoading, setPasskeyDeleteLoading] = useState(false); const [passkeySupported, setPasskeySupported] = useState(false); + const [ + passkeyRequiredVerificationMethod, + setPasskeyRequiredVerificationMethod, + ] = useState(null); const [notificationSettings, setNotificationSettings] = useState({ warningType: 'email', warningThreshold: 100000, @@ -91,6 +97,34 @@ const PersonalSetting = () => { recordIpLog: false, }); + const { + isModalVisible: isPasskeyVerificationModalVisible, + verificationMethods: passkeyVerificationMethods, + verificationState: passkeyVerificationState, + startVerification: startPasskeyVerification, + executeVerification: executePasskeyVerification, + cancelVerification: cancelPasskeyVerification, + setVerificationCode: setPasskeyVerificationCode, + switchVerificationMethod: switchPasskeyVerificationMethod, + checkVerificationMethods: checkPasskeyVerificationMethods, + } = useSecureVerification({ + onSuccess: () => { + setPasskeyRequiredVerificationMethod(null); + }, + }); + + const visiblePasskeyVerificationMethods = passkeyRequiredVerificationMethod + ? { + ...passkeyVerificationMethods, + has2FA: + passkeyRequiredVerificationMethod === '2fa' && + passkeyVerificationMethods.has2FA, + hasPasskey: + passkeyRequiredVerificationMethod === 'passkey' && + passkeyVerificationMethods.hasPasskey, + } + : passkeyVerificationMethods; + useEffect(() => { let saved = localStorage.getItem('status'); if (saved) { @@ -203,18 +237,57 @@ const PersonalSetting = () => { } }; - const handleRegisterPasskey = async () => { - if (!passkeySupported || !window.PublicKeyCredential) { + const startPasskeyManagementVerification = async (apiCall, options = {}) => { + const methods = await checkPasskeyVerificationMethods(); + const requiredMethod = methods.has2FA + ? '2fa' + : methods.hasPasskey + ? 'passkey' + : null; + + if (!requiredMethod) { + showError(t('您需要先启用两步验证或 Passkey 才能执行此操作')); + return; + } + + if (requiredMethod === 'passkey' && !methods.passkeySupported) { showInfo(t('当前设备不支持 Passkey')); return; } + + setPasskeyRequiredVerificationMethod(requiredMethod); + await startPasskeyVerification(apiCall, { + preferredMethod: requiredMethod, + title: t('安全验证'), + ...options, + }); + }; + + const startPasskeyRegistration = async () => { + const methods = await checkPasskeyVerificationMethods(); + if (!methods.has2FA) { + try { + await registerPasskey(); + } catch (error) { + showError(error.message || t('Passkey 注册失败,请重试')); + } + return; + } + + setPasskeyRequiredVerificationMethod('2fa'); + await startPasskeyVerification(registerPasskey, { + preferredMethod: '2fa', + title: t('安全验证'), + }); + }; + + const registerPasskey = async () => { setPasskeyRegisterLoading(true); try { const beginRes = await API.post('/api/user/passkey/register/begin'); const { success, message, data } = beginRes.data; if (!success) { - showError(message || t('无法发起 Passkey 注册')); - return; + throw new Error(message || t('无法发起 Passkey 注册')); } const publicKey = prepareCredentialCreationOptions( @@ -223,49 +296,69 @@ const PersonalSetting = () => { const credential = await navigator.credentials.create({ publicKey }); const payload = buildRegistrationResult(credential); if (!payload) { - showError(t('Passkey 注册失败,请重试')); - return; + throw new Error(t('Passkey 注册失败,请重试')); } const finishRes = await API.post( '/api/user/passkey/register/finish', payload, ); - if (finishRes.data.success) { - showSuccess(t('Passkey 注册成功')); - await loadPasskeyStatus(); - } else { - showError(finishRes.data.message || t('Passkey 注册失败,请重试')); + if (!finishRes.data.success) { + throw new Error( + finishRes.data.message || t('Passkey 注册失败,请重试'), + ); } + + showSuccess(t('Passkey 注册成功')); + await loadPasskeyStatus(); + return finishRes.data; } catch (error) { if (error?.name === 'AbortError') { showInfo(t('已取消 Passkey 注册')); - } else { - showError(t('Passkey 注册失败,请重试')); + return { cancelled: true }; } + throw new Error(error?.message || t('Passkey 注册失败,请重试')); } finally { setPasskeyRegisterLoading(false); } }; - const handleRemovePasskey = async () => { + const handleRegisterPasskey = async () => { + if (!passkeySupported || !window.PublicKeyCredential) { + showInfo(t('当前设备不支持 Passkey')); + return; + } + await startPasskeyRegistration(); + }; + + const removePasskey = async () => { setPasskeyDeleteLoading(true); try { const res = await API.delete('/api/user/passkey'); const { success, message } = res.data; - if (success) { - showSuccess(t('Passkey 已解绑')); - await loadPasskeyStatus(); - } else { - showError(message || t('操作失败,请重试')); + if (!success) { + throw new Error(message || t('操作失败,请重试')); } + + showSuccess(t('Passkey 已解绑')); + await loadPasskeyStatus(); + return res.data; } catch (error) { - showError(t('操作失败,请重试')); + throw new Error(error?.message || t('操作失败,请重试')); } finally { setPasskeyDeleteLoading(false); } }; + const handleRemovePasskey = async () => { + await startPasskeyManagementVerification(removePasskey); + }; + + const handlePasskeyVerificationCancel = () => { + setPasskeyRequiredVerificationMethod(null); + cancelPasskeyVerification(); + }; + const getUserData = async () => { let res = await API.get(`/api/user/self`); const { success, message, data } = res.data; @@ -556,6 +649,18 @@ const PersonalSetting = () => { turnstileSiteKey={turnstileSiteKey} setTurnstileToken={setTurnstileToken} /> + + ); }; From 6c69d60fbba28b91a879ac9410eced4378bf3664 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 00:44:49 +0000 Subject: [PATCH 41/46] chore(deps): bump github.com/jackc/pgx/v5 from 5.9.0 to 5.9.2 Bumps [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) from 5.9.0 to 5.9.2. - [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md) - [Commits](https://github.com/jackc/pgx/compare/v5.9.0...v5.9.2) --- updated-dependencies: - dependency-name: github.com/jackc/pgx/v5 dependency-version: 5.9.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ac2acdab..23f2b3aa 100644 --- a/go.mod +++ b/go.mod @@ -96,7 +96,7 @@ require ( github.com/icza/bitio v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgx/v5 v5.9.0 // indirect + github.com/jackc/pgx/v5 v5.9.2 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jfreymuth/vorbis v1.0.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect diff --git a/go.sum b/go.sum index 2aafa91f..08affe8a 100644 --- a/go.sum +++ b/go.sum @@ -152,8 +152,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.9.0 h1:T/dI+2TvmI2H8s/KH1/lXIbz1CUFk3gn5oTjr0/mBsE= -github.com/jackc/pgx/v5 v5.9.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= +github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jfreymuth/oggvorbis v1.0.5 h1:u+Ck+R0eLSRhgq8WTmffYnrVtSztJcYrl588DM4e3kQ= From 346de026832cfb7c8a9a5d56313f663283497c0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 02:02:02 +0000 Subject: [PATCH 42/46] chore(deps-dev): bump @xmldom/xmldom from 0.8.12 to 0.8.13 in /electron Bumps [@xmldom/xmldom](https://github.com/xmldom/xmldom) from 0.8.12 to 0.8.13. - [Release notes](https://github.com/xmldom/xmldom/releases) - [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md) - [Commits](https://github.com/xmldom/xmldom/compare/0.8.12...0.8.13) --- updated-dependencies: - dependency-name: "@xmldom/xmldom" dependency-version: 0.8.13 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- electron/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/electron/package-lock.json b/electron/package-lock.json index f9897b96..995c272f 100644 --- a/electron/package-lock.json +++ b/electron/package-lock.json @@ -777,9 +777,9 @@ } }, "node_modules/@xmldom/xmldom": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz", - "integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==", + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", + "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", "dev": true, "license": "MIT", "engines": { From 2431efc01ff8d4452185b77f4814eac5ad4bcf44 Mon Sep 17 00:00:00 2001 From: XiaoAI1024 Date: Thu, 23 Apr 2026 13:29:00 +0800 Subject: [PATCH 43/46] Support longer legacy token keys --- controller/token_test.go | 25 +++++++++++++++++++++++++ model/token.go | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/controller/token_test.go b/controller/token_test.go index 3eea6730..65fd7d95 100644 --- a/controller/token_test.go +++ b/controller/token_test.go @@ -38,6 +38,11 @@ type tokenKeyResponse struct { Key string `json:"key"` } +type sqliteColumnInfo struct { + Name string `gorm:"column:name"` + Type string `gorm:"column:type"` +} + func setupTokenControllerTestDB(t *testing.T) *gorm.DB { t.Helper() @@ -124,6 +129,26 @@ func decodeAPIResponse(t *testing.T, recorder *httptest.ResponseRecorder) tokenA return response } +func TestTokenAutoMigrateUsesVarchar128KeyColumn(t *testing.T) { + db := setupTokenControllerTestDB(t) + + var columns []sqliteColumnInfo + if err := db.Raw("PRAGMA table_info(tokens)").Scan(&columns).Error; err != nil { + t.Fatalf("failed to inspect token table schema: %v", err) + } + + for _, column := range columns { + if column.Name == "key" { + if strings.ToLower(column.Type) != "varchar(128)" { + t.Fatalf("expected key column type varchar(128), got %q", column.Type) + } + return + } + } + + t.Fatal("key column not found in token table schema") +} + func TestGetAllTokensMasksKeyInResponse(t *testing.T) { db := setupTokenControllerTestDB(t) token := seedToken(t, db, 1, "list-token", "abcd1234efgh5678") diff --git a/model/token.go b/model/token.go index 0529e2c7..ab841f60 100644 --- a/model/token.go +++ b/model/token.go @@ -14,7 +14,7 @@ import ( type Token struct { Id int `json:"id"` UserId int `json:"user_id" gorm:"index"` - Key string `json:"key" gorm:"type:char(48);uniqueIndex"` + Key string `json:"key" gorm:"type:varchar(128);uniqueIndex"` Status int `json:"status" gorm:"default:1"` Name string `json:"name" gorm:"index" ` CreatedTime int64 `json:"created_time" gorm:"bigint"` From 81ddf6e722941fff33aa18d561258fed4d71c665 Mon Sep 17 00:00:00 2001 From: XiaoAI1024 Date: Thu, 23 Apr 2026 13:29:00 +0800 Subject: [PATCH 44/46] Add legacy token migration test --- controller/token_test.go | 122 ++++++++++++++++++++++++++++++++++----- 1 file changed, 107 insertions(+), 15 deletions(-) diff --git a/controller/token_test.go b/controller/token_test.go index 65fd7d95..d7e0d640 100644 --- a/controller/token_test.go +++ b/controller/token_test.go @@ -43,7 +43,31 @@ type sqliteColumnInfo struct { Type string `gorm:"column:type"` } -func setupTokenControllerTestDB(t *testing.T) *gorm.DB { +type legacyToken struct { + Id int `gorm:"primaryKey"` + UserId int `gorm:"index"` + Key string `gorm:"column:key;type:char(48);uniqueIndex"` + Status int `gorm:"default:1"` + Name string `gorm:"index"` + CreatedTime int64 `gorm:"bigint"` + AccessedTime int64 `gorm:"bigint"` + ExpiredTime int64 `gorm:"bigint;default:-1"` + RemainQuota int `gorm:"default:0"` + UnlimitedQuota bool + ModelLimitsEnabled bool + ModelLimits string `gorm:"type:text"` + AllowIps *string `gorm:"default:''"` + UsedQuota int `gorm:"default:0"` + Group string `gorm:"column:group;default:''"` + CrossGroupRetry bool + DeletedAt gorm.DeletedAt `gorm:"index"` +} + +func (legacyToken) TableName() string { + return "tokens" +} + +func openTokenControllerTestDB(t *testing.T) *gorm.DB { t.Helper() gin.SetMode(gin.TestMode) @@ -60,10 +84,6 @@ func setupTokenControllerTestDB(t *testing.T) *gorm.DB { model.DB = db model.LOG_DB = db - if err := db.AutoMigrate(&model.Token{}); err != nil { - t.Fatalf("failed to migrate token table: %v", err) - } - t.Cleanup(func() { sqlDB, err := db.DB() if err == nil { @@ -74,6 +94,22 @@ func setupTokenControllerTestDB(t *testing.T) *gorm.DB { return db } +func migrateTokenControllerTestDB(t *testing.T, db *gorm.DB) { + t.Helper() + + if err := db.AutoMigrate(&model.Token{}); err != nil { + t.Fatalf("failed to migrate token table: %v", err) + } +} + +func setupTokenControllerTestDB(t *testing.T) *gorm.DB { + t.Helper() + + db := openTokenControllerTestDB(t) + migrateTokenControllerTestDB(t, db) + return db +} + func seedToken(t *testing.T, db *gorm.DB, userID int, name string, rawKey string) *model.Token { t.Helper() @@ -129,24 +165,80 @@ func decodeAPIResponse(t *testing.T, recorder *httptest.ResponseRecorder) tokenA return response } -func TestTokenAutoMigrateUsesVarchar128KeyColumn(t *testing.T) { - db := setupTokenControllerTestDB(t) +func getSQLiteColumnType(t *testing.T, db *gorm.DB, tableName string, columnName string) string { + t.Helper() var columns []sqliteColumnInfo - if err := db.Raw("PRAGMA table_info(tokens)").Scan(&columns).Error; err != nil { - t.Fatalf("failed to inspect token table schema: %v", err) + if err := db.Raw("PRAGMA table_info(" + tableName + ")").Scan(&columns).Error; err != nil { + t.Fatalf("failed to inspect %s schema: %v", tableName, err) } for _, column := range columns { - if column.Name == "key" { - if strings.ToLower(column.Type) != "varchar(128)" { - t.Fatalf("expected key column type varchar(128), got %q", column.Type) - } - return + if column.Name == columnName { + return strings.ToLower(column.Type) } } - t.Fatal("key column not found in token table schema") + t.Fatalf("column %s not found in %s schema", columnName, tableName) + return "" +} + +func TestTokenAutoMigrateUsesVarchar128KeyColumn(t *testing.T) { + db := setupTokenControllerTestDB(t) + + if got := getSQLiteColumnType(t, db, "tokens", "key"); got != "varchar(128)" { + t.Fatalf("expected key column type varchar(128), got %q", got) + } +} + +func TestTokenMigrationFromChar48ToVarchar128(t *testing.T) { + db := openTokenControllerTestDB(t) + legacyKey := strings.Repeat("a", 48) + + if err := db.AutoMigrate(&legacyToken{}); err != nil { + t.Fatalf("failed to create legacy token schema: %v", err) + } + if err := db.Create(&legacyToken{ + Id: 1, + UserId: 7, + Key: legacyKey, + Status: common.TokenStatusEnabled, + Name: "legacy-token", + CreatedTime: 1, + AccessedTime: 1, + ExpiredTime: -1, + RemainQuota: 100, + UnlimitedQuota: true, + ModelLimitsEnabled: false, + ModelLimits: "", + AllowIps: common.GetPointer(""), + UsedQuota: 0, + Group: "default", + CrossGroupRetry: false, + }).Error; err != nil { + t.Fatalf("failed to seed legacy token row: %v", err) + } + + if got := getSQLiteColumnType(t, db, "tokens", "key"); got != "char(48)" { + t.Fatalf("expected legacy key column type char(48), got %q", got) + } + + migrateTokenControllerTestDB(t, db) + + if got := getSQLiteColumnType(t, db, "tokens", "key"); got != "varchar(128)" { + t.Fatalf("expected migrated key column type varchar(128), got %q", got) + } + + var migratedToken model.Token + if err := db.First(&migratedToken, "id = ?", 1).Error; err != nil { + t.Fatalf("failed to load migrated token row: %v", err) + } + if migratedToken.Key != legacyKey { + t.Fatalf("expected migrated token key %q, got %q", legacyKey, migratedToken.Key) + } + if migratedToken.Name != "legacy-token" { + t.Fatalf("expected migrated token name to be preserved, got %q", migratedToken.Name) + } } func TestGetAllTokensMasksKeyInResponse(t *testing.T) { From 0feb6f2c3c7739c86a59ebacf0323ff96003677a Mon Sep 17 00:00:00 2001 From: XiaoAI1024 Date: Thu, 23 Apr 2026 13:29:00 +0800 Subject: [PATCH 45/46] Add cross-database token migration tests --- controller/token_test.go | 163 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 152 insertions(+), 11 deletions(-) diff --git a/controller/token_test.go b/controller/token_test.go index d7e0d640..7ffb38b4 100644 --- a/controller/token_test.go +++ b/controller/token_test.go @@ -2,10 +2,12 @@ package controller import ( "bytes" + "database/sql" "encoding/json" "fmt" "net/http" "net/http/httptest" + "os" "strconv" "strings" "testing" @@ -14,6 +16,8 @@ import ( "github.com/QuantumNous/new-api/model" "github.com/gin-gonic/gin" "github.com/glebarez/sqlite" + "gorm.io/driver/mysql" + "gorm.io/driver/postgres" "gorm.io/gorm" ) @@ -110,6 +114,45 @@ func setupTokenControllerTestDB(t *testing.T) *gorm.DB { return db } +func openTokenControllerExternalDB(t *testing.T, dialect string, dsn string) *gorm.DB { + t.Helper() + + gin.SetMode(gin.TestMode) + common.RedisEnabled = false + common.UsingSQLite = false + common.UsingMySQL = dialect == "mysql" + common.UsingPostgreSQL = dialect == "postgres" + + var ( + db *gorm.DB + err error + ) + switch dialect { + case "mysql": + db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) + case "postgres": + db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) + default: + t.Fatalf("unsupported dialect %q", dialect) + } + if err != nil { + t.Fatalf("failed to open %s db: %v", dialect, err) + } + + model.DB = db + model.LOG_DB = db + + t.Cleanup(func() { + _ = db.Exec("DROP TABLE IF EXISTS tokens").Error + sqlDB, err := db.DB() + if err == nil { + _ = sqlDB.Close() + } + }) + + return db +} + func seedToken(t *testing.T, db *gorm.DB, userID int, name string, rawKey string) *model.Token { t.Helper() @@ -183,23 +226,59 @@ func getSQLiteColumnType(t *testing.T, db *gorm.DB, tableName string, columnName return "" } -func TestTokenAutoMigrateUsesVarchar128KeyColumn(t *testing.T) { - db := setupTokenControllerTestDB(t) +func getTokenKeyColumnType(t *testing.T, db *gorm.DB, dialect string) string { + t.Helper() - if got := getSQLiteColumnType(t, db, "tokens", "key"); got != "varchar(128)" { - t.Fatalf("expected key column type varchar(128), got %q", got) + switch dialect { + case "sqlite": + return getSQLiteColumnType(t, db, "tokens", "key") + case "mysql": + var columnType string + if err := db.Raw(`SELECT COLUMN_TYPE FROM information_schema.columns + WHERE table_schema = DATABASE() AND table_name = ? AND column_name = ?`, + "tokens", "key").Scan(&columnType).Error; err != nil { + t.Fatalf("failed to inspect mysql token key column: %v", err) + } + return strings.ToLower(columnType) + case "postgres": + var dataType string + var maxLength sql.NullInt64 + if err := db.Raw(`SELECT data_type, character_maximum_length + FROM information_schema.columns + WHERE table_schema = current_schema() AND table_name = ? AND column_name = ?`, + "tokens", "key").Row().Scan(&dataType, &maxLength); err != nil { + t.Fatalf("failed to inspect postgres token key column: %v", err) + } + switch strings.ToLower(dataType) { + case "character varying": + return fmt.Sprintf("varchar(%d)", maxLength.Int64) + case "character": + return fmt.Sprintf("char(%d)", maxLength.Int64) + default: + if maxLength.Valid { + return fmt.Sprintf("%s(%d)", strings.ToLower(dataType), maxLength.Int64) + } + return strings.ToLower(dataType) + } + default: + t.Fatalf("unsupported dialect %q", dialect) + return "" } } -func TestTokenMigrationFromChar48ToVarchar128(t *testing.T) { - db := openTokenControllerTestDB(t) - legacyKey := strings.Repeat("a", 48) +func runTokenMigrationCompatibilityTest(t *testing.T, db *gorm.DB, dialect string) { + t.Helper() + legacyKey := strings.Repeat("a", 48) + longKey := strings.Repeat("b", 64) + + if err := db.Exec("DROP TABLE IF EXISTS tokens").Error; err != nil { + t.Fatalf("failed to drop pre-existing token table: %v", err) + } if err := db.AutoMigrate(&legacyToken{}); err != nil { t.Fatalf("failed to create legacy token schema: %v", err) } if err := db.Create(&legacyToken{ - Id: 1, UserId: 7, Key: legacyKey, Status: common.TokenStatusEnabled, @@ -219,18 +298,18 @@ func TestTokenMigrationFromChar48ToVarchar128(t *testing.T) { t.Fatalf("failed to seed legacy token row: %v", err) } - if got := getSQLiteColumnType(t, db, "tokens", "key"); got != "char(48)" { + if got := getTokenKeyColumnType(t, db, dialect); got != "char(48)" { t.Fatalf("expected legacy key column type char(48), got %q", got) } migrateTokenControllerTestDB(t, db) - if got := getSQLiteColumnType(t, db, "tokens", "key"); got != "varchar(128)" { + if got := getTokenKeyColumnType(t, db, dialect); got != "varchar(128)" { t.Fatalf("expected migrated key column type varchar(128), got %q", got) } var migratedToken model.Token - if err := db.First(&migratedToken, "id = ?", 1).Error; err != nil { + if err := db.First(&migratedToken, "name = ?", "legacy-token").Error; err != nil { t.Fatalf("failed to load migrated token row: %v", err) } if migratedToken.Key != legacyKey { @@ -239,6 +318,68 @@ func TestTokenMigrationFromChar48ToVarchar128(t *testing.T) { if migratedToken.Name != "legacy-token" { t.Fatalf("expected migrated token name to be preserved, got %q", migratedToken.Name) } + + inserted := model.Token{ + UserId: 8, + Name: "long-token", + Key: longKey, + Status: common.TokenStatusEnabled, + CreatedTime: 1, + AccessedTime: 1, + ExpiredTime: -1, + RemainQuota: 200, + UnlimitedQuota: true, + ModelLimitsEnabled: false, + ModelLimits: "", + AllowIps: common.GetPointer(""), + UsedQuota: 0, + Group: "default", + CrossGroupRetry: false, + } + if err := db.Create(&inserted).Error; err != nil { + t.Fatalf("failed to insert long token after migration: %v", err) + } + + var fetched model.Token + if err := db.First(&fetched, "id = ?", inserted.Id).Error; err != nil { + t.Fatalf("failed to fetch long token after migration: %v", err) + } + if fetched.Key != longKey { + t.Fatalf("expected long token key %q, got %q", longKey, fetched.Key) + } +} + +func TestTokenAutoMigrateUsesVarchar128KeyColumn(t *testing.T) { + db := setupTokenControllerTestDB(t) + + if got := getTokenKeyColumnType(t, db, "sqlite"); got != "varchar(128)" { + t.Fatalf("expected key column type varchar(128), got %q", got) + } +} + +func TestTokenMigrationFromChar48ToVarchar128(t *testing.T) { + db := openTokenControllerTestDB(t) + runTokenMigrationCompatibilityTest(t, db, "sqlite") +} + +func TestTokenMigrationFromChar48ToVarchar128MySQL(t *testing.T) { + dsn := os.Getenv("TEST_MYSQL_DSN") + if dsn == "" { + t.Skip("set TEST_MYSQL_DSN to run mysql migration compatibility test") + } + + db := openTokenControllerExternalDB(t, "mysql", dsn) + runTokenMigrationCompatibilityTest(t, db, "mysql") +} + +func TestTokenMigrationFromChar48ToVarchar128Postgres(t *testing.T) { + dsn := os.Getenv("TEST_POSTGRES_DSN") + if dsn == "" { + t.Skip("set TEST_POSTGRES_DSN to run postgres migration compatibility test") + } + + db := openTokenControllerExternalDB(t, "postgres", dsn) + runTokenMigrationCompatibilityTest(t, db, "postgres") } func TestGetAllTokensMasksKeyInResponse(t *testing.T) { From 49474520ecc1d37a8faf577fb620b7d8ea4e72ab Mon Sep 17 00:00:00 2001 From: XiaoAI1024 Date: Thu, 23 Apr 2026 13:29:00 +0800 Subject: [PATCH 46/46] Protect external token migration tests --- controller/token_test.go | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/controller/token_test.go b/controller/token_test.go index 7ffb38b4..0c0f504b 100644 --- a/controller/token_test.go +++ b/controller/token_test.go @@ -114,7 +114,7 @@ func setupTokenControllerTestDB(t *testing.T) *gorm.DB { return db } -func openTokenControllerExternalDB(t *testing.T, dialect string, dsn string) *gorm.DB { +func openTokenControllerExternalDB(t *testing.T, dialect string, dsn string) (*gorm.DB, *bool) { t.Helper() gin.SetMode(gin.TestMode) @@ -142,15 +142,23 @@ func openTokenControllerExternalDB(t *testing.T, dialect string, dsn string) *go model.DB = db model.LOG_DB = db + if db.Migrator().HasTable("tokens") { + t.Skipf("refusing to run %s migration compatibility test against external database because tokens table already exists", dialect) + } + + managedTokensTable := new(bool) + t.Cleanup(func() { - _ = db.Exec("DROP TABLE IF EXISTS tokens").Error + if *managedTokensTable && db.Migrator().HasTable("tokens") { + _ = db.Migrator().DropTable("tokens") + } sqlDB, err := db.DB() if err == nil { _ = sqlDB.Close() } }) - return db + return db, managedTokensTable } func seedToken(t *testing.T, db *gorm.DB, userID int, name string, rawKey string) *model.Token { @@ -266,18 +274,18 @@ func getTokenKeyColumnType(t *testing.T, db *gorm.DB, dialect string) string { } } -func runTokenMigrationCompatibilityTest(t *testing.T, db *gorm.DB, dialect string) { +func runTokenMigrationCompatibilityTest(t *testing.T, db *gorm.DB, dialect string, managedTokensTable *bool) { t.Helper() legacyKey := strings.Repeat("a", 48) longKey := strings.Repeat("b", 64) - if err := db.Exec("DROP TABLE IF EXISTS tokens").Error; err != nil { - t.Fatalf("failed to drop pre-existing token table: %v", err) - } if err := db.AutoMigrate(&legacyToken{}); err != nil { t.Fatalf("failed to create legacy token schema: %v", err) } + if managedTokensTable != nil { + *managedTokensTable = true + } if err := db.Create(&legacyToken{ UserId: 7, Key: legacyKey, @@ -359,7 +367,7 @@ func TestTokenAutoMigrateUsesVarchar128KeyColumn(t *testing.T) { func TestTokenMigrationFromChar48ToVarchar128(t *testing.T) { db := openTokenControllerTestDB(t) - runTokenMigrationCompatibilityTest(t, db, "sqlite") + runTokenMigrationCompatibilityTest(t, db, "sqlite", nil) } func TestTokenMigrationFromChar48ToVarchar128MySQL(t *testing.T) { @@ -368,8 +376,8 @@ func TestTokenMigrationFromChar48ToVarchar128MySQL(t *testing.T) { t.Skip("set TEST_MYSQL_DSN to run mysql migration compatibility test") } - db := openTokenControllerExternalDB(t, "mysql", dsn) - runTokenMigrationCompatibilityTest(t, db, "mysql") + db, managedTokensTable := openTokenControllerExternalDB(t, "mysql", dsn) + runTokenMigrationCompatibilityTest(t, db, "mysql", managedTokensTable) } func TestTokenMigrationFromChar48ToVarchar128Postgres(t *testing.T) { @@ -378,8 +386,8 @@ func TestTokenMigrationFromChar48ToVarchar128Postgres(t *testing.T) { t.Skip("set TEST_POSTGRES_DSN to run postgres migration compatibility test") } - db := openTokenControllerExternalDB(t, "postgres", dsn) - runTokenMigrationCompatibilityTest(t, db, "postgres") + db, managedTokensTable := openTokenControllerExternalDB(t, "postgres", dsn) + runTokenMigrationCompatibilityTest(t, db, "postgres", managedTokensTable) } func TestGetAllTokensMasksKeyInResponse(t *testing.T) {