feat(ui): i18n for key info view

This commit is contained in:
吹雪 2026-06-11 06:12:30 +09:00
parent 4eaf3508c1
commit fc31bc2d78
3 changed files with 288 additions and 100 deletions

View file

@ -23,6 +23,7 @@ import ObjectPermissionsView from "../object_permissions_view";
import { RegenerateKeyModal } from "../organisms/RegenerateKeyModal";
import { parseErrorMessage } from "../shared/errorUtils";
import { KeyEditView } from "./key_edit_view";
import { Trans, useTranslation } from "react-i18next";
interface KeyInfoViewProps {
keyId: string;
@ -61,8 +62,9 @@ export default function KeyInfoView({
teams,
onKeyDataUpdate,
onDelete,
backButtonText = "Back to Keys",
backButtonText,
}: KeyInfoViewProps) {
const { t } = useTranslation();
const { accessToken, userId: userID, userRole, premiumUser } = useAuthorized();
const canEditGuardrails = premiumUser || (userRole != null && rolesWithWriteAccess.includes(userRole));
const { teams: teamsData } = useTeams();
@ -140,9 +142,9 @@ export default function KeyInfoView({
return (
<div className="p-4">
<Button icon={ArrowLeftIcon} variant="light" onClick={onClose} className="mb-4">
{backButtonText}
{backButtonText ?? t("templates.keyInfoView.backToKeys")}
</Button>
<Text>Key not found</Text>
<Text>{t("templates.keyInfoView.keyNotFound")}</Text>
</div>
);
}
@ -262,7 +264,7 @@ export default function KeyInfoView({
};
} catch (error) {
console.error("Error parsing metadata JSON:", error);
NotificationManager.error("Invalid metadata JSON");
NotificationManager.error(t("templates.keyInfoView.invalidMetadataJson"));
return;
}
} else {
@ -307,7 +309,7 @@ export default function KeyInfoView({
if (onKeyDataUpdate) {
onKeyDataUpdate(newKeyValues);
}
NotificationManager.success("Key updated successfully");
NotificationManager.success(t("templates.keyInfoView.keyUpdatedSuccess"));
setIsEditing(false);
// Refresh key data here if needed
} catch (error) {
@ -321,7 +323,7 @@ export default function KeyInfoView({
setDeleteLoading(true);
if (!accessToken) return;
await keyDeleteCall(accessToken as string, currentKeyData.token || currentKeyData.token_id);
NotificationManager.success("Key deleted successfully");
NotificationManager.success(t("templates.keyInfoView.keyDeletedSuccess"));
if (onDelete) {
onDelete();
}
@ -401,7 +403,7 @@ export default function KeyInfoView({
if (onKeyDataUpdate) {
onKeyDataUpdate({ spend: 0 });
}
NotificationManager.success("Key spend reset to $0");
NotificationManager.success(t("templates.keyInfoView.keySpendResetSuccess"));
setIsResetSpendModalOpen(false);
},
onError: (error) => {
@ -415,7 +417,7 @@ export default function KeyInfoView({
<div className="w-full h-full overflow-y-auto p-4">
<KeyInfoHeader
data={{
keyName: currentKeyData.key_alias || "Virtual Key",
keyName: currentKeyData.key_alias || t("templates.keyInfoView.virtualKey"),
keyId: currentKeyData.token_id || currentKeyData.token,
userId: currentKeyData.user_id || "",
userEmail: currentKeyData.user_email || "",
@ -427,19 +429,17 @@ export default function KeyInfoView({
"",
createdAt: currentKeyData.created_at ? formatTimestamp(currentKeyData.created_at) : "",
lastUpdated: currentKeyData.updated_at ? formatTimestamp(currentKeyData.updated_at) : "",
lastActive: currentKeyData.last_active ? formatTimestamp(currentKeyData.last_active) : "Never",
expires: currentKeyData.expires ? formatTimestamp(currentKeyData.expires) : "Never",
lastActive: currentKeyData.last_active ? formatTimestamp(currentKeyData.last_active) : t("common.never"),
expires: currentKeyData.expires ? formatTimestamp(currentKeyData.expires) : t("common.never"),
}}
onBack={onClose}
onRegenerate={() => setIsRegenerateModalOpen(true)}
onDelete={() => setIsDeleteModalOpen(true)}
onResetSpend={canResetSpend ? () => setIsResetSpendModalOpen(true) : undefined}
canModifyKey={canModifyKey}
backButtonText={backButtonText}
backButtonText={backButtonText ?? t("templates.keyInfoView.backToKeys")}
regenerateDisabled={!premiumUser}
regenerateTooltip={
!premiumUser ? "This is a LiteLLM Enterprise feature, and requires a valid key to use." : undefined
}
regenerateTooltip={!premiumUser ? t("templates.keyInfoView.enterpriseFeatureTooltip") : undefined}
/>
{/* Add RegenerateKeyModal */}
@ -453,27 +453,27 @@ export default function KeyInfoView({
{/* Delete Confirmation Modal */}
<DeleteResourceModal
isOpen={isDeleteModalOpen}
title="Delete Key"
alertMessage="This action is irreversible and will immediately revoke access for any applications using this key."
message="Are you sure you want to delete this Virtual Key?"
resourceInformationTitle="Key Information"
title={t("templates.keyInfoView.deleteKeyTitle")}
alertMessage={t("templates.keyInfoView.deleteKeyAlertMessage")}
message={t("templates.keyInfoView.deleteKeyMessage")}
resourceInformationTitle={t("templates.keyInfoView.keyInformation")}
resourceInformation={[
{
label: "Key Alias",
label: t("templates.keyInfoView.keyAlias"),
value: currentKeyData?.key_alias || "-",
},
{
label: "Key ID",
label: t("templates.keyInfoView.keyId"),
value: currentKeyData?.token_id || currentKeyData?.token || "-",
code: true,
},
{
label: "Team ID",
label: t("templates.keyInfoView.teamId"),
value: currentKeyData?.team_id || "-",
code: true,
},
{
label: "Spend",
label: t("templates.keyInfoView.spend"),
value: currentKeyData?.spend ? `$${formatNumberWithCommas(currentKeyData.spend, 4)}` : "$0.0000",
},
]}
@ -488,28 +488,36 @@ export default function KeyInfoView({
{/* Reset Spend Confirmation Modal */}
<Modal
title="Reset Key Spend"
title={t("templates.keyInfoView.resetKeySpendTitle")}
open={isResetSpendModalOpen}
onOk={handleResetSpend}
onCancel={() => setIsResetSpendModalOpen(false)}
okText="Reset"
okText={t("common.reset")}
okButtonProps={{ danger: true }}
confirmLoading={resetSpendLoading}
>
<p>
Reset spend for <strong>{currentKeyData?.key_alias || currentKeyData?.token_id || "this key"}</strong> to{" "}
<strong>$0</strong>?
<Trans
i18nKey="templates.keyInfoView.resetSpendConfirm"
values={{
keyName: currentKeyData?.key_alias || currentKeyData?.token_id || t("templates.keyInfoView.thisKey"),
}}
components={{ strong: <strong /> }}
/>
</p>
<p style={{ color: "#666", fontSize: "0.875rem", marginTop: 8 }}>
Current spend: <strong>${formatNumberWithCommas(currentKeyData.spend, 4)}</strong>. Spend history is preserved
in logs. This resets the current period spend counter, the same as an automatic budget reset.
<Trans
i18nKey="templates.keyInfoView.resetSpendCurrentSpend"
values={{ amount: formatNumberWithCommas(currentKeyData.spend, 4) }}
components={{ strong: <strong /> }}
/>
</p>
</Modal>
<TabGroup>
<TabList className="mb-4">
<Tab>Overview</Tab>
<Tab>Settings</Tab>
<Tab>{t("templates.keyInfoView.tabOverview")}</Tab>
<Tab>{t("common.settings")}</Tab>
</TabList>
<TabPanels>
@ -517,28 +525,42 @@ export default function KeyInfoView({
<TabPanel>
<Grid numItems={1} numItemsSm={2} numItemsLg={3} className="gap-6">
<Card>
<Text>Spend</Text>
<Text>{t("templates.keyInfoView.spend")}</Text>
<div className="mt-2">
<Title>${formatNumberWithCommas(currentKeyData.spend, 4)}</Title>
<Text>
of{" "}
{t("templates.keyInfoView.of")}{" "}
{currentKeyData.max_budget !== null
? `$${formatNumberWithCommas(currentKeyData.max_budget, 2)}`
: "Unlimited"}
: t("templates.keyInfoView.unlimited")}
</Text>
</div>
</Card>
<Card>
<Text>Rate Limits</Text>
<Text>{t("templates.keyInfoView.rateLimits")}</Text>
<div className="mt-2">
<Text>TPM: {currentKeyData.tpm_limit !== null ? currentKeyData.tpm_limit : "Unlimited"}</Text>
<Text>RPM: {currentKeyData.rpm_limit !== null ? currentKeyData.rpm_limit : "Unlimited"}</Text>
<Text>
{t("templates.keyInfoView.tpmValue", {
value:
currentKeyData.tpm_limit !== null
? currentKeyData.tpm_limit
: t("templates.keyInfoView.unlimited"),
})}
</Text>
<Text>
{t("templates.keyInfoView.rpmValue", {
value:
currentKeyData.rpm_limit !== null
? currentKeyData.rpm_limit
: t("templates.keyInfoView.unlimited"),
})}
</Text>
</div>
</Card>
<Card>
<Text>Models</Text>
<Text>{t("templates.keyInfoView.models")}</Text>
<div className="mt-2 flex flex-wrap gap-2">
{currentKeyData.models && currentKeyData.models.length > 0 ? (
currentKeyData.models.map((model, index) => (
@ -547,7 +569,7 @@ export default function KeyInfoView({
</Badge>
))
) : (
<Text>No models specified</Text>
<Text>{t("templates.keyInfoView.noModelsSpecified")}</Text>
)}
</div>
</Card>
@ -561,7 +583,7 @@ export default function KeyInfoView({
</Card>
<Card>
<Text className="font-medium mb-3">Guardrails</Text>
<Text className="font-medium mb-3">{t("templates.keyInfoView.guardrails")}</Text>
{Array.isArray(currentKeyData.metadata?.guardrails) && currentKeyData.metadata.guardrails.length > 0 ? (
<div className="flex flex-wrap gap-2">
{currentKeyData.metadata.guardrails.map((guardrail: string, index: number) => (
@ -571,29 +593,35 @@ export default function KeyInfoView({
))}
</div>
) : (
<Text className="text-gray-500">No guardrails configured</Text>
<Text className="text-gray-500">{t("templates.keyInfoView.noGuardrailsConfigured")}</Text>
)}
{typeof currentKeyData.metadata?.disable_global_guardrails === "boolean" &&
currentKeyData.metadata.disable_global_guardrails === true && (
<div className="mt-3 pt-3 border-t border-gray-200">
<Badge color="yellow">Global Guardrails Disabled</Badge>
<Badge color="yellow">{t("templates.keyInfoView.globalGuardrailsDisabled")}</Badge>
</div>
)}
</Card>
<Card>
<Text className="font-medium mb-3">Policies</Text>
<Text className="font-medium mb-3">{t("templates.keyInfoView.policies")}</Text>
{Array.isArray(currentKeyData.metadata?.policies) && currentKeyData.metadata.policies.length > 0 ? (
<div className="space-y-4">
{currentKeyData.metadata.policies.map((policy: string, index: number) => (
<div key={index} className="space-y-2">
<div className="flex items-center gap-2">
<Badge color="purple">{policy}</Badge>
{loadingPolicies && <Text className="text-xs text-gray-400">Loading guardrails...</Text>}
{loadingPolicies && (
<Text className="text-xs text-gray-400">
{t("templates.keyInfoView.loadingGuardrails")}
</Text>
)}
</div>
{!loadingPolicies && policyGuardrails[policy] && policyGuardrails[policy].length > 0 && (
<div className="ml-4 pl-3 border-l-2 border-gray-200">
<Text className="text-xs text-gray-500 mb-1">Resolved Guardrails:</Text>
<Text className="text-xs text-gray-500 mb-1">
{t("templates.keyInfoView.resolvedGuardrails")}
</Text>
<div className="flex flex-wrap gap-1">
{policyGuardrails[policy].map((guardrail: string, gIndex: number) => (
<Badge key={gIndex} color="blue" size="xs">
@ -607,7 +635,7 @@ export default function KeyInfoView({
))}
</div>
) : (
<Text className="text-gray-500">No policies configured</Text>
<Text className="text-gray-500">{t("templates.keyInfoView.noPoliciesConfigured")}</Text>
)}
</Card>
@ -636,8 +664,10 @@ export default function KeyInfoView({
<TabPanel>
<Card>
<div className="flex justify-between items-center mb-4">
<Title>Key Settings</Title>
{!isEditing && canModifyKey && <Button onClick={() => setIsEditing(true)}>Edit Settings</Button>}
<Title>{t("templates.keyInfoView.keySettingsTitle")}</Title>
{!isEditing && canModifyKey && (
<Button onClick={() => setIsEditing(true)}>{t("templates.keyInfoView.editSettings")}</Button>
)}
</div>
{isEditing ? (
@ -654,28 +684,28 @@ export default function KeyInfoView({
) : (
<div className="space-y-4">
<div>
<Text className="font-medium">Key ID</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldKeyId")}</Text>
<Text className="font-mono">{currentKeyData.token_id || currentKeyData.token}</Text>
</div>
<div>
<Text className="font-medium">Key Alias</Text>
<Text>{currentKeyData.key_alias || "Not Set"}</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldKeyAlias")}</Text>
<Text>{currentKeyData.key_alias || t("templates.keyInfoView.notSet")}</Text>
</div>
<div>
<Text className="font-medium">Secret Key</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldSecretKey")}</Text>
<Text className="font-mono">{currentKeyData.key_name}</Text>
</div>
<div>
<Text className="font-medium">Team ID</Text>
<Text>{currentKeyData.team_id || "Not Set"}</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldTeamId")}</Text>
<Text>{currentKeyData.team_id || t("templates.keyInfoView.notSet")}</Text>
</div>
{enableProjectsUI && (
<div>
<Text className="font-medium">Project</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldProject")}</Text>
<Text>
{currentKeyData.project_id
? (() => {
@ -684,36 +714,38 @@ export default function KeyInfoView({
? `${project.project_alias} (${currentKeyData.project_id})`
: currentKeyData.project_id;
})()
: "Not Set"}
: t("templates.keyInfoView.notSet")}
</Text>
</div>
)}
<div>
<Text className="font-medium">Organization</Text>
<Text>{(currentKeyData.organization_id ?? currentKeyData.org_id) || "Not Set"}</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldOrganization")}</Text>
<Text>
{(currentKeyData.organization_id ?? currentKeyData.org_id) || t("templates.keyInfoView.notSet")}
</Text>
</div>
<div>
<Text className="font-medium">Created</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldCreated")}</Text>
<Text>{formatTimestamp(currentKeyData.created_at)}</Text>
</div>
{lastRegeneratedAt && (
<div>
<Text className="font-medium">Last Regenerated</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldLastRegenerated")}</Text>
<div className="flex items-center gap-2">
<Text>{formatTimestamp(lastRegeneratedAt)}</Text>
<Badge color="green" size="xs">
Recent
{t("templates.keyInfoView.recentBadge")}
</Badge>
</div>
</div>
)}
<div>
<Text className="font-medium">Expires</Text>
<Text>{currentKeyData.expires ? formatTimestamp(currentKeyData.expires) : "Never"}</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldExpires")}</Text>
<Text>{currentKeyData.expires ? formatTimestamp(currentKeyData.expires) : t("common.never")}</Text>
</div>
<AutoRotationView
@ -727,21 +759,25 @@ export default function KeyInfoView({
/>
<div>
<Text className="font-medium">Spend</Text>
<Text>${formatNumberWithCommas(currentKeyData.spend, 4)} USD</Text>
</div>
<div>
<Text className="font-medium">Budget</Text>
<Text className="font-medium">{t("templates.keyInfoView.spend")}</Text>
<Text>
{currentKeyData.max_budget !== null
? `$${formatNumberWithCommas(currentKeyData.max_budget, 2)}`
: "Unlimited"}
{t("templates.keyInfoView.spendValue", {
amount: formatNumberWithCommas(currentKeyData.spend, 4),
})}
</Text>
</div>
<div>
<Text className="font-medium">Tags</Text>
<Text className="font-medium">{t("templates.keyInfoView.budget")}</Text>
<Text>
{currentKeyData.max_budget !== null
? `$${formatNumberWithCommas(currentKeyData.max_budget, 2)}`
: t("templates.keyInfoView.unlimited")}
</Text>
</div>
<div>
<Text className="font-medium">{t("templates.keyInfoView.fieldTags")}</Text>
<div className="flex flex-wrap gap-2 mt-1">
{Array.isArray(currentKeyData.metadata?.tags) && currentKeyData.metadata.tags.length > 0
? currentKeyData.metadata.tags.map((tag, index) => (
@ -749,12 +785,12 @@ export default function KeyInfoView({
{tag}
</span>
))
: "No tags specified"}
: t("templates.keyInfoView.noTagsSpecified")}
</div>
</div>
<div>
<Text className="font-medium">Prompts</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldPrompts")}</Text>
<Text>
{Array.isArray(currentKeyData.metadata?.prompts) && currentKeyData.metadata.prompts.length > 0
? currentKeyData.metadata.prompts.map((prompt, index) => (
@ -762,12 +798,12 @@ export default function KeyInfoView({
{prompt}
</span>
))
: "No prompts specified"}
: t("templates.keyInfoView.noPromptsSpecified")}
</Text>
</div>
<div>
<Text className="font-medium">Allowed Routes</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldAllowedRoutes")}</Text>
<div className="flex flex-wrap gap-2 mt-1">
{Array.isArray(currentKeyData.allowed_routes) && currentKeyData.allowed_routes.length > 0 ? (
currentKeyData.allowed_routes.map((route, index) => (
@ -776,13 +812,13 @@ export default function KeyInfoView({
</span>
))
) : (
<Tag color="green">All routes allowed</Tag>
<Tag color="green">{t("templates.keyInfoView.allRoutesAllowed")}</Tag>
)}
</div>
</div>
<div>
<Text className="font-medium">Allowed Pass Through Routes</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldAllowedPassThroughRoutes")}</Text>
<Text>
{Array.isArray(currentKeyData.metadata?.allowed_passthrough_routes) &&
currentKeyData.metadata.allowed_passthrough_routes.length > 0
@ -791,23 +827,23 @@ export default function KeyInfoView({
{route}
</span>
))
: "No pass through routes specified"}
: t("templates.keyInfoView.noPassThroughRoutesSpecified")}
</Text>
</div>
<div>
<Text className="font-medium">Disable Global Guardrails</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldDisableGlobalGuardrails")}</Text>
<Text>
{currentKeyData.metadata?.disable_global_guardrails === true ? (
<Badge color="yellow">Enabled - Global guardrails bypassed</Badge>
<Badge color="yellow">{t("templates.keyInfoView.globalGuardrailsBypassed")}</Badge>
) : (
<Badge color="green">Disabled - Global guardrails active</Badge>
<Badge color="green">{t("templates.keyInfoView.globalGuardrailsActive")}</Badge>
)}
</Text>
</div>
<div>
<Text className="font-medium">Models</Text>
<Text className="font-medium">{t("templates.keyInfoView.models")}</Text>
<div className="flex flex-wrap gap-2 mt-1">
{currentKeyData.models && currentKeyData.models.length > 0 ? (
currentKeyData.models.map((model, index) => (
@ -816,37 +852,55 @@ export default function KeyInfoView({
</span>
))
) : (
<Text>No models specified</Text>
<Text>{t("templates.keyInfoView.noModelsSpecified")}</Text>
)}
</div>
</div>
<div>
<Text className="font-medium">Rate Limits</Text>
<Text>TPM: {currentKeyData.tpm_limit !== null ? currentKeyData.tpm_limit : "Unlimited"}</Text>
<Text>RPM: {currentKeyData.rpm_limit !== null ? currentKeyData.rpm_limit : "Unlimited"}</Text>
<Text className="font-medium">{t("templates.keyInfoView.rateLimits")}</Text>
<Text>
Max Parallel Requests:{" "}
{currentKeyData.max_parallel_requests !== null
? currentKeyData.max_parallel_requests
: "Unlimited"}
{t("templates.keyInfoView.tpmValue", {
value:
currentKeyData.tpm_limit !== null
? currentKeyData.tpm_limit
: t("templates.keyInfoView.unlimited"),
})}
</Text>
<Text>
Model TPM Limits:{" "}
{currentKeyData.metadata?.model_tpm_limit
? JSON.stringify(currentKeyData.metadata.model_tpm_limit)
: "Unlimited"}
{t("templates.keyInfoView.rpmValue", {
value:
currentKeyData.rpm_limit !== null
? currentKeyData.rpm_limit
: t("templates.keyInfoView.unlimited"),
})}
</Text>
<Text>
Model RPM Limits:{" "}
{currentKeyData.metadata?.model_rpm_limit
? JSON.stringify(currentKeyData.metadata.model_rpm_limit)
: "Unlimited"}
{t("templates.keyInfoView.maxParallelRequests", {
value:
currentKeyData.max_parallel_requests !== null
? currentKeyData.max_parallel_requests
: t("templates.keyInfoView.unlimited"),
})}
</Text>
<Text>
{t("templates.keyInfoView.modelTpmLimits", {
value: currentKeyData.metadata?.model_tpm_limit
? JSON.stringify(currentKeyData.metadata.model_tpm_limit)
: t("templates.keyInfoView.unlimited"),
})}
</Text>
<Text>
{t("templates.keyInfoView.modelRpmLimits", {
value: currentKeyData.metadata?.model_rpm_limit
? JSON.stringify(currentKeyData.metadata.model_rpm_limit)
: t("templates.keyInfoView.unlimited"),
})}
</Text>
</div>
<div>
<Text className="font-medium">Metadata</Text>
<Text className="font-medium">{t("templates.keyInfoView.fieldMetadata")}</Text>
<pre className="bg-gray-100 p-2 rounded text-xs overflow-auto mt-1">
{formatMetadataForDisplay(stripTagsFromMetadata(currentKeyData.metadata))}
</pre>

View file

@ -7870,6 +7870,73 @@
"metadata": "Metadata",
"saveChanges": "Save Changes",
"errorFetchingTags": "Error fetching tags: {{error}}"
},
"keyInfoView": {
"backToKeys": "Back to Keys",
"keyNotFound": "Key not found",
"invalidMetadataJson": "Invalid metadata JSON",
"keyUpdatedSuccess": "Key updated successfully",
"keyDeletedSuccess": "Key deleted successfully",
"keySpendResetSuccess": "Key spend reset to $0",
"virtualKey": "Virtual Key",
"enterpriseFeatureTooltip": "This is a LiteLLM Enterprise feature, and requires a valid key to use.",
"deleteKeyTitle": "Delete Key",
"deleteKeyAlertMessage": "This action is irreversible and will immediately revoke access for any applications using this key.",
"deleteKeyMessage": "Are you sure you want to delete this Virtual Key?",
"keyInformation": "Key Information",
"keyAlias": "Key Alias",
"keyId": "Key ID",
"teamId": "Team ID",
"spend": "Spend",
"resetKeySpendTitle": "Reset Key Spend",
"thisKey": "this key",
"resetSpendConfirm": "Reset spend for <strong>{{keyName}}</strong> to <strong>$0</strong>?",
"resetSpendCurrentSpend": "Current spend: <strong>${{amount}}</strong>. Spend history is preserved in logs. This resets the current period spend counter, the same as an automatic budget reset.",
"tabOverview": "Overview",
"of": "of",
"unlimited": "Unlimited",
"rateLimits": "Rate Limits",
"tpmValue": "TPM: {{value}}",
"rpmValue": "RPM: {{value}}",
"models": "Models",
"noModelsSpecified": "No models specified",
"guardrails": "Guardrails",
"noGuardrailsConfigured": "No guardrails configured",
"globalGuardrailsDisabled": "Global Guardrails Disabled",
"policies": "Policies",
"loadingGuardrails": "Loading guardrails...",
"resolvedGuardrails": "Resolved Guardrails:",
"noPoliciesConfigured": "No policies configured",
"keySettingsTitle": "Key Settings",
"editSettings": "Edit Settings",
"fieldKeyId": "Key ID",
"fieldKeyAlias": "Key Alias",
"notSet": "Not Set",
"fieldSecretKey": "Secret Key",
"fieldTeamId": "Team ID",
"fieldProject": "Project",
"fieldOrganization": "Organization",
"fieldCreated": "Created",
"fieldLastRegenerated": "Last Regenerated",
"recentBadge": "Recent",
"fieldExpires": "Expires",
"spendValue": "${{amount}} USD",
"budget": "Budget",
"fieldTags": "Tags",
"noTagsSpecified": "No tags specified",
"fieldPrompts": "Prompts",
"noPromptsSpecified": "No prompts specified",
"fieldAllowedRoutes": "Allowed Routes",
"allRoutesAllowed": "All routes allowed",
"fieldAllowedPassThroughRoutes": "Allowed Pass Through Routes",
"noPassThroughRoutesSpecified": "No pass through routes specified",
"fieldDisableGlobalGuardrails": "Disable Global Guardrails",
"globalGuardrailsBypassed": "Enabled - Global guardrails bypassed",
"globalGuardrailsActive": "Disabled - Global guardrails active",
"maxParallelRequests": "Max Parallel Requests: {{value}}",
"modelTpmLimits": "Model TPM Limits: {{value}}",
"modelRpmLimits": "Model RPM Limits: {{value}}",
"fieldMetadata": "Metadata"
}
}
}

View file

@ -7870,6 +7870,73 @@
"metadata": "元数据",
"saveChanges": "保存更改",
"errorFetchingTags": "获取标签时出错:{{error}}"
},
"keyInfoView": {
"backToKeys": "返回密钥列表",
"keyNotFound": "密钥未找到",
"invalidMetadataJson": "无效的元数据 JSON",
"keyUpdatedSuccess": "密钥更新成功",
"keyDeletedSuccess": "密钥删除成功",
"keySpendResetSuccess": "密钥花费已重置为 $0",
"virtualKey": "虚拟密钥",
"enterpriseFeatureTooltip": "这是 LiteLLM 企业版功能,需要有效密钥才能使用。",
"deleteKeyTitle": "删除密钥",
"deleteKeyAlertMessage": "此操作不可撤销,将立即撤销所有使用此密钥的应用程序的访问权限。",
"deleteKeyMessage": "确定要删除此虚拟密钥吗?",
"keyInformation": "密钥信息",
"keyAlias": "密钥别名",
"keyId": "密钥 ID",
"teamId": "团队 ID",
"spend": "花费",
"resetKeySpendTitle": "重置密钥花费",
"thisKey": "此密钥",
"resetSpendConfirm": "将 <strong>{{keyName}}</strong> 的花费重置为 <strong>$0</strong>?",
"resetSpendCurrentSpend": "当前花费:<strong>${{amount}}</strong>。花费历史记录保留在日志中。这将重置当前周期的花费计数器,与自动预算重置相同。",
"tabOverview": "概览",
"of": ",共",
"unlimited": "无限制",
"rateLimits": "速率限制",
"tpmValue": "TPM:{{value}}",
"rpmValue": "RPM:{{value}}",
"models": "模型",
"noModelsSpecified": "未指定模型",
"guardrails": "护栏",
"noGuardrailsConfigured": "未配置护栏",
"globalGuardrailsDisabled": "全局护栏已禁用",
"policies": "策略",
"loadingGuardrails": "正在加载护栏...",
"resolvedGuardrails": "已解析护栏:",
"noPoliciesConfigured": "未配置策略",
"keySettingsTitle": "密钥设置",
"editSettings": "编辑设置",
"fieldKeyId": "密钥 ID",
"fieldKeyAlias": "密钥别名",
"notSet": "未设置",
"fieldSecretKey": "密钥值",
"fieldTeamId": "团队 ID",
"fieldProject": "项目",
"fieldOrganization": "组织",
"fieldCreated": "创建时间",
"fieldLastRegenerated": "最近重新生成",
"recentBadge": "最近",
"fieldExpires": "过期时间",
"spendValue": "${{amount}} USD",
"budget": "预算",
"fieldTags": "标签",
"noTagsSpecified": "未指定标签",
"fieldPrompts": "提示词",
"noPromptsSpecified": "未指定提示词",
"fieldAllowedRoutes": "允许的路由",
"allRoutesAllowed": "所有路由均允许",
"fieldAllowedPassThroughRoutes": "允许的透传路由",
"noPassThroughRoutesSpecified": "未指定透传路由",
"fieldDisableGlobalGuardrails": "禁用全局护栏",
"globalGuardrailsBypassed": "已启用 - 全局护栏已绕过",
"globalGuardrailsActive": "已禁用 - 全局护栏已生效",
"maxParallelRequests": "最大并发请求数:{{value}}",
"modelTpmLimits": "模型 TPM 限制:{{value}}",
"modelRpmLimits": "模型 RPM 限制:{{value}}",
"fieldMetadata": "元数据"
}
}
}