新增功能:
- Antigravity OAuth 授权流程支持
- Claude → Gemini 协议转换(Claude API 请求自动转换为 Gemini 格式)
- 配额刷新和状态显示
- 混合调度功能,支持 Anthropic 和 Antigravity 账户混合使用
- /antigravity 专用路由,支持仅使用 Antigravity 账户
- 前端 Antigravity 服务商标识和账户管理功能
冲突解决:
- CreateAccountModal.vue: 合并 data-tour 属性和 mixed-scheduling 属性
- EditAccountModal.vue: 合并 data-tour 属性和 mixed-scheduling 属性
代码质量改进:
- 修复 antigravity 类型文件的 gofmt 格式问题(struct 字段对齐、interface{} → any)
- 移除 .golangci.yml 中的 gofmt 排除规则
- 修复测试文件的格式问题
1691 lines
79 KiB
TypeScript
1691 lines
79 KiB
TypeScript
export default {
|
||
// Home Page
|
||
home: {
|
||
viewOnGithub: 'View on GitHub',
|
||
viewDocs: 'View Documentation',
|
||
docs: 'Docs',
|
||
switchToLight: 'Switch to Light Mode',
|
||
switchToDark: 'Switch to Dark Mode',
|
||
dashboard: 'Dashboard',
|
||
login: 'Login',
|
||
getStarted: 'Get Started',
|
||
goToDashboard: 'Go to Dashboard',
|
||
tags: {
|
||
subscriptionToApi: 'Subscription to API',
|
||
stickySession: 'Sticky Session',
|
||
realtimeBilling: 'Real-time Billing'
|
||
},
|
||
features: {
|
||
unifiedGateway: 'Unified API Gateway',
|
||
unifiedGatewayDesc:
|
||
'Convert Claude subscriptions to API endpoints. Access AI capabilities through standard /v1/messages interface.',
|
||
multiAccount: 'Multi-Account Pool',
|
||
multiAccountDesc:
|
||
'Manage multiple upstream accounts with smart load balancing. Support OAuth and API Key authentication.',
|
||
balanceQuota: 'Balance & Quota',
|
||
balanceQuotaDesc:
|
||
'Token-based billing with precise usage tracking. Manage quotas and recharge with redeem codes.'
|
||
},
|
||
providers: {
|
||
title: 'Supported Providers',
|
||
description: 'Unified API interface for AI services',
|
||
supported: 'Supported',
|
||
soon: 'Soon',
|
||
claude: 'Claude',
|
||
gemini: 'Gemini',
|
||
antigravity: 'Antigravity',
|
||
more: 'More'
|
||
},
|
||
footer: {
|
||
allRightsReserved: 'All rights reserved.'
|
||
}
|
||
},
|
||
|
||
// Setup Wizard
|
||
setup: {
|
||
title: 'Sub2API Setup',
|
||
description: 'Configure your Sub2API instance',
|
||
database: {
|
||
title: 'Database Configuration',
|
||
host: 'Host',
|
||
port: 'Port',
|
||
username: 'Username',
|
||
password: 'Password',
|
||
databaseName: 'Database Name',
|
||
sslMode: 'SSL Mode',
|
||
passwordPlaceholder: 'Password',
|
||
ssl: {
|
||
disable: 'Disable',
|
||
require: 'Require',
|
||
verifyCa: 'Verify CA',
|
||
verifyFull: 'Verify Full'
|
||
}
|
||
},
|
||
redis: {
|
||
title: 'Redis Configuration',
|
||
host: 'Host',
|
||
port: 'Port',
|
||
password: 'Password (optional)',
|
||
database: 'Database',
|
||
passwordPlaceholder: 'Password'
|
||
},
|
||
admin: {
|
||
title: 'Admin Account',
|
||
email: 'Email',
|
||
password: 'Password',
|
||
confirmPassword: 'Confirm Password',
|
||
passwordPlaceholder: 'Min 6 characters',
|
||
confirmPasswordPlaceholder: 'Confirm password',
|
||
passwordMismatch: 'Passwords do not match'
|
||
},
|
||
ready: {
|
||
title: 'Ready to Install',
|
||
database: 'Database',
|
||
redis: 'Redis',
|
||
adminEmail: 'Admin Email'
|
||
}
|
||
},
|
||
|
||
// Common
|
||
common: {
|
||
loading: 'Loading...',
|
||
save: 'Save',
|
||
cancel: 'Cancel',
|
||
delete: 'Delete',
|
||
edit: 'Edit',
|
||
create: 'Create',
|
||
update: 'Update',
|
||
confirm: 'Confirm',
|
||
reset: 'Reset',
|
||
search: 'Search',
|
||
filter: 'Filter',
|
||
export: 'Export',
|
||
import: 'Import',
|
||
actions: 'Actions',
|
||
status: 'Status',
|
||
name: 'Name',
|
||
email: 'Email',
|
||
password: 'Password',
|
||
submit: 'Submit',
|
||
back: 'Back',
|
||
next: 'Next',
|
||
yes: 'Yes',
|
||
no: 'No',
|
||
all: 'All',
|
||
none: 'None',
|
||
noData: 'No data',
|
||
success: 'Success',
|
||
error: 'Error',
|
||
warning: 'Warning',
|
||
info: 'Info',
|
||
active: 'Active',
|
||
inactive: 'Inactive',
|
||
more: 'More',
|
||
close: 'Close',
|
||
enabled: 'Enabled',
|
||
disabled: 'Disabled',
|
||
total: 'Total',
|
||
balance: 'Balance',
|
||
available: 'Available',
|
||
copiedToClipboard: 'Copied to clipboard',
|
||
copyFailed: 'Failed to copy',
|
||
contactSupport: 'Contact Support',
|
||
selectOption: 'Select an option',
|
||
searchPlaceholder: 'Search...',
|
||
noOptionsFound: 'No options found',
|
||
saving: 'Saving...',
|
||
refresh: 'Refresh',
|
||
time: {
|
||
never: 'Never',
|
||
justNow: 'Just now',
|
||
minutesAgo: '{n}m ago',
|
||
hoursAgo: '{n}h ago',
|
||
daysAgo: '{n}d ago'
|
||
}
|
||
},
|
||
|
||
// Navigation
|
||
nav: {
|
||
dashboard: 'Dashboard',
|
||
apiKeys: 'API Keys',
|
||
usage: 'Usage',
|
||
redeem: 'Redeem',
|
||
profile: 'Profile',
|
||
users: 'Users',
|
||
groups: 'Groups',
|
||
subscriptions: 'Subscriptions',
|
||
accounts: 'Accounts',
|
||
proxies: 'Proxies',
|
||
redeemCodes: 'Redeem Codes',
|
||
settings: 'Settings',
|
||
myAccount: 'My Account',
|
||
lightMode: 'Light Mode',
|
||
darkMode: 'Dark Mode',
|
||
collapse: 'Collapse',
|
||
expand: 'Expand',
|
||
logout: 'Logout',
|
||
github: 'GitHub',
|
||
mySubscriptions: 'My Subscriptions'
|
||
},
|
||
|
||
// Auth
|
||
auth: {
|
||
welcomeBack: 'Welcome Back',
|
||
signInToAccount: 'Sign in to your account to continue',
|
||
signIn: 'Sign In',
|
||
signingIn: 'Signing in...',
|
||
createAccount: 'Create Account',
|
||
signUpToStart: 'Sign up to start using {siteName}',
|
||
signUp: 'Sign up',
|
||
processing: 'Processing...',
|
||
continue: 'Continue',
|
||
rememberMe: 'Remember me',
|
||
dontHaveAccount: "Don't have an account?",
|
||
alreadyHaveAccount: 'Already have an account?',
|
||
registrationDisabled: 'Registration is currently disabled. Please contact the administrator.',
|
||
emailLabel: 'Email',
|
||
emailPlaceholder: 'Enter your email',
|
||
passwordLabel: 'Password',
|
||
passwordPlaceholder: 'Enter your password',
|
||
createPasswordPlaceholder: 'Create a strong password',
|
||
passwordHint: 'At least 6 characters',
|
||
emailRequired: 'Email is required',
|
||
invalidEmail: 'Please enter a valid email address',
|
||
passwordRequired: 'Password is required',
|
||
passwordMinLength: 'Password must be at least 6 characters',
|
||
loginFailed: 'Login failed. Please check your credentials and try again.',
|
||
registrationFailed: 'Registration failed. Please try again.',
|
||
loginSuccess: 'Login successful! Welcome back.',
|
||
accountCreatedSuccess: 'Account created successfully! Welcome to {siteName}.',
|
||
turnstileExpired: 'Verification expired, please try again',
|
||
turnstileFailed: 'Verification failed, please try again',
|
||
completeVerification: 'Please complete the verification',
|
||
verifyYourEmail: 'Verify Your Email',
|
||
sessionExpired: 'Session expired',
|
||
sessionExpiredDesc: 'Please go back to the registration page and start again.',
|
||
verificationCode: 'Verification Code',
|
||
verificationCodeHint: 'Enter the 6-digit code sent to your email',
|
||
sendingCode: 'Sending...',
|
||
clickToResend: 'Click to resend code',
|
||
resendCode: 'Resend verification code',
|
||
oauth: {
|
||
code: 'Code',
|
||
state: 'State',
|
||
fullUrl: 'Full URL'
|
||
}
|
||
},
|
||
|
||
// Dashboard
|
||
dashboard: {
|
||
title: 'Dashboard',
|
||
welcomeMessage: "Welcome back! Here's an overview of your account.",
|
||
balance: 'Balance',
|
||
apiKeys: 'API Keys',
|
||
todayRequests: 'Today Requests',
|
||
todayCost: 'Today Cost',
|
||
todayTokens: 'Today Tokens',
|
||
totalTokens: 'Total Tokens',
|
||
cacheToday: 'Cache (Today)',
|
||
performance: 'Performance',
|
||
avgResponse: 'Avg Response',
|
||
averageTime: 'Average time',
|
||
timeRange: 'Time Range',
|
||
granularity: 'Granularity',
|
||
day: 'Day',
|
||
hour: 'Hour',
|
||
modelDistribution: 'Model Distribution',
|
||
tokenUsageTrend: 'Token Usage Trend',
|
||
noDataAvailable: 'No data available',
|
||
model: 'Model',
|
||
requests: 'Requests',
|
||
tokens: 'Tokens',
|
||
actual: 'Actual',
|
||
standard: 'Standard',
|
||
input: 'Input',
|
||
output: 'Output',
|
||
cache: 'Cache',
|
||
recentUsage: 'Recent Usage',
|
||
last7Days: 'Last 7 days',
|
||
noUsageRecords: 'No usage records',
|
||
startUsingApi: 'Start using the API to see your usage history here.',
|
||
viewAllUsage: 'View all usage',
|
||
quickActions: 'Quick Actions',
|
||
createApiKey: 'Create API Key',
|
||
generateNewKey: 'Generate a new API key',
|
||
viewUsage: 'View Usage',
|
||
checkDetailedLogs: 'Check detailed usage logs',
|
||
redeemCode: 'Redeem Code',
|
||
addBalanceWithCode: 'Add balance with a code'
|
||
},
|
||
|
||
// Groups (shared)
|
||
groups: {
|
||
subscription: 'Sub'
|
||
},
|
||
|
||
// API Keys
|
||
keys: {
|
||
title: 'API Keys',
|
||
description: 'Manage your API keys and access tokens',
|
||
createKey: 'Create API Key',
|
||
editKey: 'Edit API Key',
|
||
deleteKey: 'Delete API Key',
|
||
deleteConfirmMessage: "Are you sure you want to delete '{name}'? This action cannot be undone.",
|
||
apiKey: 'API Key',
|
||
group: 'Group',
|
||
noGroup: 'No group',
|
||
created: 'Created',
|
||
copyToClipboard: 'Copy to clipboard',
|
||
copied: 'Copied!',
|
||
importToCcSwitch: 'Import to CCS',
|
||
enable: 'Enable',
|
||
disable: 'Disable',
|
||
nameLabel: 'Name',
|
||
namePlaceholder: 'My API Key',
|
||
groupLabel: 'Group',
|
||
selectGroup: 'Select a group',
|
||
statusLabel: 'Status',
|
||
selectStatus: 'Select status',
|
||
saving: 'Saving...',
|
||
noKeysYet: 'No API keys yet',
|
||
createFirstKey: 'Create your first API key to get started with the API.',
|
||
keyCreatedSuccess: 'API key created successfully',
|
||
keyUpdatedSuccess: 'API key updated successfully',
|
||
keyDeletedSuccess: 'API key deleted successfully',
|
||
keyEnabledSuccess: 'API key enabled successfully',
|
||
keyDisabledSuccess: 'API key disabled successfully',
|
||
failedToLoad: 'Failed to load API keys',
|
||
failedToSave: 'Failed to save API key',
|
||
failedToDelete: 'Failed to delete API key',
|
||
failedToUpdateStatus: 'Failed to update API key status',
|
||
clickToChangeGroup: 'Click to change group',
|
||
groupChangedSuccess: 'Group changed successfully',
|
||
failedToChangeGroup: 'Failed to change group',
|
||
groupRequired: 'Please select a group',
|
||
usage: 'Usage',
|
||
today: 'Today',
|
||
total: 'Total',
|
||
useKey: 'Use Key',
|
||
useKeyModal: {
|
||
title: 'Use API Key',
|
||
description:
|
||
'Add the following environment variables to your terminal profile or run directly in terminal to configure API access.',
|
||
copy: 'Copy',
|
||
copied: 'Copied',
|
||
note: 'These environment variables will be active in the current terminal session. For permanent configuration, add them to ~/.bashrc, ~/.zshrc, or the appropriate configuration file.',
|
||
noGroupTitle: 'Please assign a group first',
|
||
noGroupDescription: 'This API key has not been assigned to a group. Please click the group column in the key list to assign one before viewing the configuration.',
|
||
openai: {
|
||
description: 'Add the following configuration files to your Codex CLI config directory.',
|
||
configTomlHint: 'Make sure the following content is at the beginning of the config.toml file',
|
||
note: 'Make sure the config directory exists. macOS/Linux users can run mkdir -p ~/.codex to create it.',
|
||
noteWindows: 'Press Win+R and enter %userprofile%\\.codex to open the config directory. Create it manually if it does not exist.',
|
||
},
|
||
},
|
||
customKeyLabel: 'Custom Key',
|
||
customKeyPlaceholder: 'Enter your custom key (min 16 chars)',
|
||
customKeyHint: 'Only letters, numbers, underscores and hyphens allowed. Minimum 16 characters.',
|
||
customKeyTooShort: 'Custom key must be at least 16 characters',
|
||
customKeyInvalidChars: 'Custom key can only contain letters, numbers, underscores, and hyphens',
|
||
customKeyRequired: 'Please enter a custom key',
|
||
ccSwitchNotInstalled: 'CC-Switch is not installed or the protocol handler is not registered. Please install CC-Switch first or manually copy the API key.'
|
||
},
|
||
|
||
// Usage
|
||
usage: {
|
||
title: 'Usage Records',
|
||
description: 'View and analyze your API usage history',
|
||
totalRequests: 'Total Requests',
|
||
totalTokens: 'Total Tokens',
|
||
totalCost: 'Total Cost',
|
||
standardCost: 'Standard',
|
||
actualCost: 'Actual',
|
||
avgDuration: 'Avg Duration',
|
||
inSelectedRange: 'in selected range',
|
||
perRequest: 'per request',
|
||
apiKeyFilter: 'API Key',
|
||
allApiKeys: 'All API Keys',
|
||
timeRange: 'Time Range',
|
||
exportCsv: 'Export CSV',
|
||
exportExcel: 'Export Excel',
|
||
exportingProgress: 'Exporting data...',
|
||
exportedCount: 'Exported {current}/{total} records',
|
||
estimatedTime: 'Estimated time remaining: {time}',
|
||
cancelExport: 'Cancel Export',
|
||
exportCancelled: 'Export cancelled',
|
||
exporting: 'Exporting...',
|
||
preparingExport: 'Preparing export...',
|
||
model: 'Model',
|
||
type: 'Type',
|
||
tokens: 'Tokens',
|
||
cost: 'Cost',
|
||
firstToken: 'First Token',
|
||
duration: 'Duration',
|
||
time: 'Time',
|
||
stream: 'Stream',
|
||
sync: 'Sync',
|
||
in: 'In',
|
||
out: 'Out',
|
||
cacheRead: 'Read',
|
||
cacheWrite: 'Write',
|
||
rate: 'Rate',
|
||
original: 'Original',
|
||
billed: 'Billed',
|
||
noRecords: 'No usage records found. Try adjusting your filters.',
|
||
failedToLoad: 'Failed to load usage logs',
|
||
noDataToExport: 'No data to export',
|
||
exportSuccess: 'Usage data exported successfully',
|
||
exportFailed: 'Failed to export usage data',
|
||
exportExcelSuccess: 'Usage data exported successfully (Excel format)',
|
||
exportExcelFailed: 'Failed to export usage data',
|
||
billingType: 'Billing',
|
||
balance: 'Balance',
|
||
subscription: 'Subscription'
|
||
},
|
||
|
||
// Redeem
|
||
redeem: {
|
||
title: 'Redeem Code',
|
||
description: 'Enter your redeem code to add balance or increase concurrency',
|
||
currentBalance: 'Current Balance',
|
||
concurrency: 'Concurrency',
|
||
requests: 'requests',
|
||
redeemCodeLabel: 'Redeem Code',
|
||
redeemCodePlaceholder: 'Enter your redeem code',
|
||
redeemCodeHint: 'Redeem codes are case-sensitive',
|
||
redeeming: 'Redeeming...',
|
||
redeemButton: 'Redeem Code',
|
||
redeemSuccess: 'Code Redeemed Successfully!',
|
||
redeemFailed: 'Redemption Failed',
|
||
added: 'Added',
|
||
concurrentRequests: 'concurrent requests',
|
||
newBalance: 'New Balance',
|
||
newConcurrency: 'New Concurrency',
|
||
aboutCodes: 'About Redeem Codes',
|
||
codeRule1: 'Each code can only be used once',
|
||
codeRule2: 'Codes may add balance, increase concurrency, or grant trial access',
|
||
codeRule3: 'Contact support if you have issues redeeming a code',
|
||
codeRule4: 'Balance and concurrency updates are immediate',
|
||
recentActivity: 'Recent Activity',
|
||
historyWillAppear: 'Your redemption history will appear here',
|
||
balanceAddedRedeem: 'Balance Added (Redeem)',
|
||
balanceAddedAdmin: 'Balance Added (Admin)',
|
||
balanceDeductedAdmin: 'Balance Deducted (Admin)',
|
||
concurrencyAddedRedeem: 'Concurrency Added (Redeem)',
|
||
concurrencyAddedAdmin: 'Concurrency Added (Admin)',
|
||
concurrencyReducedAdmin: 'Concurrency Reduced (Admin)',
|
||
adminAdjustment: 'Admin Adjustment',
|
||
subscriptionAssigned: 'Subscription Assigned',
|
||
subscriptionAssignedDesc: 'You have been granted access to {groupName}',
|
||
subscriptionDays: '{days} days',
|
||
days: ' days',
|
||
codeRedeemSuccess: 'Code redeemed successfully!',
|
||
failedToRedeem: 'Failed to redeem code. Please check the code and try again.',
|
||
subscriptionRefreshFailed: 'Redeemed successfully, but failed to refresh subscription status.'
|
||
},
|
||
|
||
// Profile
|
||
profile: {
|
||
title: 'Profile Settings',
|
||
description: 'Manage your account information and settings',
|
||
accountBalance: 'Account Balance',
|
||
concurrencyLimit: 'Concurrency Limit',
|
||
memberSince: 'Member Since',
|
||
administrator: 'Administrator',
|
||
user: 'User',
|
||
username: 'Username',
|
||
wechat: 'WeChat ID',
|
||
enterUsername: 'Enter username',
|
||
enterWechat: 'Enter WeChat ID',
|
||
editProfile: 'Edit Profile',
|
||
updateProfile: 'Update Profile',
|
||
updating: 'Updating...',
|
||
updateSuccess: 'Profile updated successfully',
|
||
updateFailed: 'Failed to update profile',
|
||
usernameRequired: 'Username is required',
|
||
changePassword: 'Change Password',
|
||
currentPassword: 'Current Password',
|
||
newPassword: 'New Password',
|
||
confirmNewPassword: 'Confirm New Password',
|
||
passwordHint: 'Password must be at least 8 characters long',
|
||
changingPassword: 'Changing...',
|
||
changePasswordButton: 'Change Password',
|
||
passwordsNotMatch: 'New passwords do not match',
|
||
passwordTooShort: 'Password must be at least 8 characters long',
|
||
passwordChangeSuccess: 'Password changed successfully',
|
||
passwordChangeFailed: 'Failed to change password'
|
||
},
|
||
|
||
// Empty States
|
||
empty: {
|
||
noData: 'No data found'
|
||
},
|
||
|
||
// Table
|
||
table: {
|
||
expandActions: 'Expand More Actions',
|
||
collapseActions: 'Collapse Actions'
|
||
},
|
||
|
||
// Pagination
|
||
pagination: {
|
||
showing: 'Showing',
|
||
to: 'to',
|
||
of: 'of',
|
||
results: 'results',
|
||
page: 'Page',
|
||
pageOf: 'Page {page} of {total}',
|
||
previous: 'Previous',
|
||
next: 'Next',
|
||
perPage: 'Per page',
|
||
goToPage: 'Go to page {page}'
|
||
},
|
||
|
||
// Errors
|
||
errors: {
|
||
somethingWentWrong: 'Something went wrong',
|
||
pageNotFound: 'Page not found',
|
||
unauthorized: 'Unauthorized',
|
||
forbidden: 'Forbidden',
|
||
serverError: 'Server error',
|
||
networkError: 'Network error',
|
||
timeout: 'Request timeout',
|
||
tryAgain: 'Please try again'
|
||
},
|
||
|
||
// Dates
|
||
dates: {
|
||
today: 'Today',
|
||
yesterday: 'Yesterday',
|
||
thisWeek: 'This Week',
|
||
lastWeek: 'Last Week',
|
||
thisMonth: 'This Month',
|
||
lastMonth: 'Last Month',
|
||
last7Days: 'Last 7 Days',
|
||
last14Days: 'Last 14 Days',
|
||
last30Days: 'Last 30 Days',
|
||
custom: 'Custom',
|
||
startDate: 'Start Date',
|
||
endDate: 'End Date',
|
||
apply: 'Apply',
|
||
selectDateRange: 'Select date range'
|
||
},
|
||
|
||
// Admin
|
||
admin: {
|
||
// Dashboard
|
||
dashboard: {
|
||
title: 'Admin Dashboard',
|
||
description: 'System overview and real-time statistics',
|
||
apiKeys: 'API Keys',
|
||
accounts: 'Accounts',
|
||
users: 'Users',
|
||
todayRequests: 'Today Requests',
|
||
newUsersToday: 'New Users Today',
|
||
todayTokens: 'Today Tokens',
|
||
totalTokens: 'Total Tokens',
|
||
cacheToday: 'Cache (Today)',
|
||
performance: 'Performance',
|
||
avgResponse: 'Avg Response',
|
||
active: 'active',
|
||
ok: 'ok',
|
||
err: 'err',
|
||
activeUsers: 'active users',
|
||
create: 'Create',
|
||
timeRange: 'Time Range',
|
||
granularity: 'Granularity',
|
||
day: 'Day',
|
||
hour: 'Hour',
|
||
modelDistribution: 'Model Distribution',
|
||
tokenUsageTrend: 'Token Usage Trend',
|
||
userUsageTrend: 'User Usage Trend (Top 12)',
|
||
model: 'Model',
|
||
requests: 'Requests',
|
||
tokens: 'Tokens',
|
||
actual: 'Actual',
|
||
standard: 'Standard',
|
||
noDataAvailable: 'No data available',
|
||
recentUsage: 'Recent Usage',
|
||
failedToLoad: 'Failed to load dashboard statistics'
|
||
},
|
||
|
||
// Users
|
||
users: {
|
||
title: 'User Management',
|
||
description: 'Manage users and their permissions',
|
||
createUser: 'Create User',
|
||
editUser: 'Edit User',
|
||
deleteUser: 'Delete User',
|
||
searchUsers: 'Search users...',
|
||
allRoles: 'All Roles',
|
||
allStatus: 'All Status',
|
||
admin: 'Admin',
|
||
user: 'User',
|
||
disabled: 'Disabled',
|
||
email: 'Email',
|
||
password: 'Password',
|
||
username: 'Username',
|
||
wechat: 'WeChat ID',
|
||
notes: 'Notes',
|
||
enterEmail: 'Enter email',
|
||
enterPassword: 'Enter password',
|
||
enterUsername: 'Enter username (optional)',
|
||
enterWechat: 'Enter WeChat ID (optional)',
|
||
enterNotes: 'Enter notes (admin only)',
|
||
notesHint: 'This note is only visible to administrators',
|
||
enterNewPassword: 'Enter new password (optional)',
|
||
leaveEmptyToKeep: 'Leave empty to keep current password',
|
||
generatePassword: 'Generate random password',
|
||
copyPassword: 'Copy password',
|
||
creating: 'Creating...',
|
||
updating: 'Updating...',
|
||
columns: {
|
||
user: 'User',
|
||
username: 'Username',
|
||
wechat: 'WeChat ID',
|
||
notes: 'Notes',
|
||
role: 'Role',
|
||
subscriptions: 'Subscriptions',
|
||
balance: 'Balance',
|
||
usage: 'Usage',
|
||
concurrency: 'Concurrency',
|
||
status: 'Status',
|
||
created: 'Created',
|
||
actions: 'Actions'
|
||
},
|
||
today: 'Today',
|
||
total: 'Total',
|
||
noSubscription: 'No subscription',
|
||
daysRemaining: '{days}d',
|
||
expired: 'Expired',
|
||
disable: 'Disable',
|
||
enable: 'Enable',
|
||
disableUser: 'Disable User',
|
||
enableUser: 'Enable User',
|
||
viewApiKeys: 'View API Keys',
|
||
groups: 'Groups',
|
||
apiKeys: 'API Keys',
|
||
userApiKeys: 'User API Keys',
|
||
noApiKeys: 'This user has no API keys',
|
||
group: 'Group',
|
||
none: 'None',
|
||
noUsersYet: 'No users yet',
|
||
createFirstUser: 'Create your first user to get started.',
|
||
userCreated: 'User created successfully',
|
||
userUpdated: 'User updated successfully',
|
||
userDeleted: 'User deleted successfully',
|
||
userEnabled: 'User enabled successfully',
|
||
userDisabled: 'User disabled successfully',
|
||
failedToLoad: 'Failed to load users',
|
||
failedToCreate: 'Failed to create user',
|
||
failedToUpdate: 'Failed to update user',
|
||
failedToDelete: 'Failed to delete user',
|
||
failedToToggle: 'Failed to update user status',
|
||
failedToLoadApiKeys: 'Failed to load user API keys',
|
||
deleteConfirm: "Are you sure you want to delete '{email}'? This action cannot be undone.",
|
||
setAllowedGroups: 'Set Allowed Groups',
|
||
allowedGroupsHint:
|
||
'Select which standard groups this user can use. Subscription groups are managed separately.',
|
||
noStandardGroups: 'No standard groups available',
|
||
allowAllGroups: 'Allow All Groups',
|
||
allowAllGroupsHint: 'User can use any non-exclusive group',
|
||
allowedGroupsUpdated: 'Allowed groups updated successfully',
|
||
failedToLoadGroups: 'Failed to load groups',
|
||
failedToUpdateAllowedGroups: 'Failed to update allowed groups',
|
||
deposit: 'Deposit',
|
||
withdraw: 'Withdraw',
|
||
depositAmount: 'Deposit Amount',
|
||
withdrawAmount: 'Withdraw Amount',
|
||
currentBalance: 'Current Balance',
|
||
depositNotesPlaceholder:
|
||
'e.g., New user registration bonus, promotional credit, compensation, etc.',
|
||
withdrawNotesPlaceholder:
|
||
'e.g., Service issue refund, incorrect charge reversal, account closure refund, etc.',
|
||
notesOptional: 'Notes are optional but helpful for record keeping',
|
||
amountHint: 'Please enter a positive amount',
|
||
newBalance: 'New Balance',
|
||
depositing: 'Depositing...',
|
||
withdrawing: 'Withdrawing...',
|
||
confirmDeposit: 'Confirm Deposit',
|
||
confirmWithdraw: 'Confirm Withdraw',
|
||
depositSuccess: 'Deposit successful',
|
||
withdrawSuccess: 'Withdraw successful',
|
||
failedToDeposit: 'Failed to deposit',
|
||
failedToWithdraw: 'Failed to withdraw',
|
||
useDepositWithdrawButtons: 'Please use deposit/withdraw buttons to adjust balance',
|
||
insufficientBalance: 'Insufficient balance, balance cannot be negative after withdrawal'
|
||
},
|
||
|
||
// Groups
|
||
groups: {
|
||
title: 'Group Management',
|
||
description: 'Manage API key groups and rate multipliers',
|
||
createGroup: 'Create Group',
|
||
editGroup: 'Edit Group',
|
||
deleteGroup: 'Delete Group',
|
||
allPlatforms: 'All Platforms',
|
||
allStatus: 'All Status',
|
||
allGroups: 'All Groups',
|
||
exclusive: 'Exclusive',
|
||
nonExclusive: 'Non-Exclusive',
|
||
public: 'Public',
|
||
columns: {
|
||
name: 'Name',
|
||
platform: 'Platform',
|
||
rateMultiplier: 'Rate Multiplier',
|
||
type: 'Type',
|
||
accounts: 'Accounts',
|
||
status: 'Status',
|
||
actions: 'Actions',
|
||
billingType: 'Billing Type'
|
||
},
|
||
rateAndAccounts: '{rate}x rate · {count} accounts',
|
||
accountsCount: '{count} accounts',
|
||
form: {
|
||
name: 'Name',
|
||
description: 'Description',
|
||
platform: 'Platform',
|
||
rateMultiplier: 'Rate Multiplier',
|
||
status: 'Status',
|
||
exclusive: 'Exclusive Group'
|
||
},
|
||
enterGroupName: 'Enter group name',
|
||
optionalDescription: 'Optional description',
|
||
platformHint: 'Select the platform this group is associated with',
|
||
platformNotEditable: 'Platform cannot be changed after creation',
|
||
rateMultiplierHint: 'Cost multiplier for this group (e.g., 1.5 = 150% of base cost)',
|
||
exclusiveHint: 'Exclusive group, manually assign to specific users',
|
||
exclusiveTooltip: {
|
||
title: 'What is an exclusive group?',
|
||
description: 'When enabled, users cannot see this group when creating API Keys. Only after an admin manually assigns a user to this group can they use it.',
|
||
example: 'Use case:',
|
||
exampleContent: 'Public group rate is 0.8. Create an exclusive group with 0.7 rate, manually assign VIP users to give them better pricing.'
|
||
},
|
||
noGroupsYet: 'No groups yet',
|
||
createFirstGroup: 'Create your first group to organize API keys.',
|
||
creating: 'Creating...',
|
||
updating: 'Updating...',
|
||
limitDay: 'd',
|
||
limitWeek: 'w',
|
||
limitMonth: 'mo',
|
||
groupCreated: 'Group created successfully',
|
||
groupUpdated: 'Group updated successfully',
|
||
groupDeleted: 'Group deleted successfully',
|
||
failedToLoad: 'Failed to load groups',
|
||
failedToCreate: 'Failed to create group',
|
||
failedToUpdate: 'Failed to update group',
|
||
failedToDelete: 'Failed to delete group',
|
||
deleteConfirm:
|
||
"Are you sure you want to delete '{name}'? All associated API keys will no longer belong to any group.",
|
||
deleteConfirmSubscription:
|
||
"Are you sure you want to delete subscription group '{name}'? This will invalidate all API keys bound to this subscription and delete all related subscription records. This action cannot be undone.",
|
||
subscription: {
|
||
title: 'Subscription Settings',
|
||
type: 'Billing Type',
|
||
typeHint:
|
||
'Standard billing deducts from user balance. Subscription mode uses quota limits instead.',
|
||
typeNotEditable: 'Billing type cannot be changed after group creation.',
|
||
standard: 'Standard (Balance)',
|
||
subscription: 'Subscription (Quota)',
|
||
dailyLimit: 'Daily Limit (USD)',
|
||
weeklyLimit: 'Weekly Limit (USD)',
|
||
monthlyLimit: 'Monthly Limit (USD)',
|
||
defaultValidityDays: 'Default Validity (Days)',
|
||
validityHint: 'Number of days the subscription is valid when assigned to a user',
|
||
noLimit: 'No limit'
|
||
}
|
||
},
|
||
|
||
// Subscriptions
|
||
subscriptions: {
|
||
title: 'Subscription Management',
|
||
description: 'Manage user subscriptions and quota limits',
|
||
assignSubscription: 'Assign Subscription',
|
||
extendSubscription: 'Extend Subscription',
|
||
revokeSubscription: 'Revoke Subscription',
|
||
allStatus: 'All Status',
|
||
allGroups: 'All Groups',
|
||
daily: 'Daily',
|
||
weekly: 'Weekly',
|
||
monthly: 'Monthly',
|
||
noLimits: 'No limits configured',
|
||
resetNow: 'Resetting soon',
|
||
windowNotActive: 'Window not active',
|
||
resetInMinutes: 'Resets in {minutes}m',
|
||
resetInHoursMinutes: 'Resets in {hours}h {minutes}m',
|
||
resetInDaysHours: 'Resets in {days}d {hours}h',
|
||
daysRemaining: 'days remaining',
|
||
noExpiration: 'No expiration',
|
||
status: {
|
||
active: 'Active',
|
||
expired: 'Expired',
|
||
revoked: 'Revoked'
|
||
},
|
||
columns: {
|
||
user: 'User',
|
||
group: 'Group',
|
||
usage: 'Usage',
|
||
expires: 'Expires',
|
||
status: 'Status',
|
||
actions: 'Actions'
|
||
},
|
||
form: {
|
||
user: 'User',
|
||
group: 'Subscription Group',
|
||
validityDays: 'Validity (Days)',
|
||
extendDays: 'Extend by (Days)'
|
||
},
|
||
selectUser: 'Select a user',
|
||
selectGroup: 'Select a subscription group',
|
||
groupHint: 'Only groups with subscription billing type are shown',
|
||
validityHint: 'Number of days the subscription will be valid',
|
||
extendingFor: 'Extending subscription for',
|
||
currentExpiration: 'Current expiration',
|
||
assign: 'Assign',
|
||
assigning: 'Assigning...',
|
||
extend: 'Extend',
|
||
extending: 'Extending...',
|
||
revoke: 'Revoke',
|
||
noSubscriptionsYet: 'No subscriptions yet',
|
||
assignFirstSubscription: 'Assign a subscription to get started.',
|
||
subscriptionAssigned: 'Subscription assigned successfully',
|
||
subscriptionExtended: 'Subscription extended successfully',
|
||
subscriptionRevoked: 'Subscription revoked successfully',
|
||
failedToLoad: 'Failed to load subscriptions',
|
||
failedToAssign: 'Failed to assign subscription',
|
||
failedToExtend: 'Failed to extend subscription',
|
||
failedToRevoke: 'Failed to revoke subscription',
|
||
revokeConfirm:
|
||
"Are you sure you want to revoke the subscription for '{user}'? This action cannot be undone."
|
||
},
|
||
|
||
// Accounts
|
||
accounts: {
|
||
title: 'Account Management',
|
||
description: 'Manage AI platform accounts and credentials',
|
||
createAccount: 'Create Account',
|
||
syncFromCrs: 'Sync from CRS',
|
||
syncFromCrsTitle: 'Sync Accounts from CRS',
|
||
syncFromCrsDesc:
|
||
'Sync accounts from claude-relay-service (CRS) into this system (CRS is called server-to-server).',
|
||
crsVersionRequirement: '⚠️ Note: CRS version must be ≥ v1.1.240 to support this feature',
|
||
crsBaseUrl: 'CRS Base URL',
|
||
crsBaseUrlPlaceholder: 'e.g. http://127.0.0.1:3000',
|
||
crsUsername: 'Username',
|
||
crsPassword: 'Password',
|
||
syncProxies: 'Also sync proxies (match by host/port/auth or create)',
|
||
syncNow: 'Sync Now',
|
||
syncing: 'Syncing...',
|
||
syncMissingFields: 'Please fill base URL, username and password',
|
||
syncResult: 'Sync Result',
|
||
syncResultSummary: 'Created {created}, updated {updated}, skipped {skipped}, failed {failed}',
|
||
syncErrors: 'Errors / Skipped Details',
|
||
syncCompleted: 'Sync completed: created {created}, updated {updated}',
|
||
syncCompletedWithErrors:
|
||
'Sync completed with errors: failed {failed} (created {created}, updated {updated})',
|
||
syncFailed: 'Sync failed',
|
||
editAccount: 'Edit Account',
|
||
deleteAccount: 'Delete Account',
|
||
searchAccounts: 'Search accounts...',
|
||
allPlatforms: 'All Platforms',
|
||
allTypes: 'All Types',
|
||
allStatus: 'All Status',
|
||
oauthType: 'OAuth',
|
||
setupToken: 'Setup Token',
|
||
apiKey: 'API Key',
|
||
// Schedulable toggle
|
||
schedulable: 'Schedulable',
|
||
schedulableHint: 'Enable to include this account in API request scheduling',
|
||
schedulableEnabled: 'Scheduling enabled',
|
||
schedulableDisabled: 'Scheduling disabled',
|
||
failedToToggleSchedulable: 'Failed to toggle scheduling status',
|
||
platforms: {
|
||
anthropic: 'Anthropic',
|
||
claude: 'Claude',
|
||
openai: 'OpenAI',
|
||
gemini: 'Gemini',
|
||
antigravity: 'Antigravity'
|
||
},
|
||
types: {
|
||
oauth: 'OAuth',
|
||
chatgptOauth: 'ChatGPT OAuth',
|
||
responsesApi: 'Responses API',
|
||
googleOauth: 'Google OAuth',
|
||
codeAssist: 'Code Assist',
|
||
antigravityOauth: 'Antigravity OAuth'
|
||
},
|
||
columns: {
|
||
name: 'Name',
|
||
platformType: 'Platform/Type',
|
||
platform: 'Platform',
|
||
type: 'Type',
|
||
concurrencyStatus: 'Concurrency',
|
||
status: 'Status',
|
||
schedulable: 'Schedule',
|
||
todayStats: "Today's Stats",
|
||
groups: 'Groups',
|
||
usageWindows: 'Usage Windows',
|
||
priority: 'Priority',
|
||
lastUsed: 'Last Used',
|
||
actions: 'Actions'
|
||
},
|
||
clearRateLimit: 'Clear Rate Limit',
|
||
testConnection: 'Test Connection',
|
||
reAuthorize: 'Re-Authorize',
|
||
refreshToken: 'Refresh Token',
|
||
noAccountsYet: 'No accounts yet',
|
||
createFirstAccount: 'Create your first account to start using AI services.',
|
||
tokenRefreshed: 'Token refreshed successfully',
|
||
accountDeleted: 'Account deleted successfully',
|
||
rateLimitCleared: 'Rate limit cleared successfully',
|
||
bulkActions: {
|
||
selected: '{count} account(s) selected',
|
||
selectCurrentPage: 'Select this page',
|
||
clear: 'Clear selection',
|
||
edit: 'Bulk Edit',
|
||
delete: 'Bulk Delete'
|
||
},
|
||
bulkEdit: {
|
||
title: 'Bulk Edit Accounts',
|
||
selectionInfo:
|
||
'{count} account(s) selected. Only checked or filled fields will be updated; others stay unchanged.',
|
||
baseUrlPlaceholder: 'https://api.anthropic.com or https://api.openai.com',
|
||
baseUrlNotice: 'Applies to API Key accounts only; leave empty to keep existing value',
|
||
submit: 'Update Accounts',
|
||
updating: 'Updating...',
|
||
success: 'Updated {count} account(s)',
|
||
partialSuccess: 'Partially updated: {success} succeeded, {failed} failed',
|
||
failed: 'Bulk update failed',
|
||
noSelection: 'Please select accounts to edit',
|
||
noFieldsSelected: 'Select at least one field to update'
|
||
},
|
||
bulkDeleteTitle: 'Bulk Delete Accounts',
|
||
bulkDeleteConfirm: 'Delete the selected {count} account(s)? This action cannot be undone.',
|
||
bulkDeleteSuccess: 'Deleted {count} account(s)',
|
||
bulkDeletePartial: 'Partially deleted: {success} succeeded, {failed} failed',
|
||
bulkDeleteFailed: 'Bulk delete failed',
|
||
resetStatus: 'Reset Status',
|
||
statusReset: 'Account status reset successfully',
|
||
failedToResetStatus: 'Failed to reset account status',
|
||
failedToLoad: 'Failed to load accounts',
|
||
failedToRefresh: 'Failed to refresh token',
|
||
failedToDelete: 'Failed to delete account',
|
||
failedToClearRateLimit: 'Failed to clear rate limit',
|
||
deleteConfirm: "Are you sure you want to delete '{name}'? This action cannot be undone.",
|
||
// Create/Edit Account Modal
|
||
platform: 'Platform',
|
||
accountName: 'Account Name',
|
||
enterAccountName: 'Enter account name',
|
||
accountType: 'Account Type',
|
||
claudeCode: 'Claude Code',
|
||
claudeConsole: 'Claude Console',
|
||
oauthSetupToken: 'OAuth / Setup Token',
|
||
addMethod: 'Add Method',
|
||
setupTokenLongLived: 'Setup Token (Long-lived)',
|
||
baseUrl: 'Base URL',
|
||
baseUrlHint: 'Leave default for official Anthropic API',
|
||
apiKeyRequired: 'API Key *',
|
||
apiKeyPlaceholder: 'sk-ant-api03-...',
|
||
apiKeyHint: 'Your Claude Console API Key',
|
||
// OpenAI specific hints
|
||
openai: {
|
||
baseUrlHint: 'Leave default for official OpenAI API',
|
||
apiKeyHint: 'Your OpenAI API Key'
|
||
},
|
||
modelRestriction: 'Model Restriction (Optional)',
|
||
modelWhitelist: 'Model Whitelist',
|
||
modelMapping: 'Model Mapping',
|
||
selectAllowedModels: 'Select allowed models. Leave empty to support all models.',
|
||
mapRequestModels:
|
||
'Map request models to actual models. Left is the requested model, right is the actual model sent to API.',
|
||
selectedModels: 'Selected {count} model(s)',
|
||
supportsAllModels: '(supports all models)',
|
||
requestModel: 'Request model',
|
||
actualModel: 'Actual model',
|
||
addMapping: 'Add Mapping',
|
||
mappingExists: 'Mapping for {model} already exists',
|
||
customErrorCodes: 'Custom Error Codes',
|
||
customErrorCodesHint: 'Only stop scheduling for selected error codes',
|
||
customErrorCodesWarning:
|
||
'Only selected error codes will stop scheduling. Other errors will return 500.',
|
||
selectedErrorCodes: 'Selected',
|
||
noneSelectedUsesDefault: 'None selected (uses default policy)',
|
||
enterErrorCode: 'Enter error code (100-599)',
|
||
invalidErrorCode: 'Please enter a valid HTTP error code (100-599)',
|
||
errorCodeExists: 'This error code is already selected',
|
||
interceptWarmupRequests: 'Intercept Warmup Requests',
|
||
interceptWarmupRequestsDesc:
|
||
'When enabled, warmup requests like title generation will return mock responses without consuming upstream tokens',
|
||
proxy: 'Proxy',
|
||
noProxy: 'No Proxy',
|
||
concurrency: 'Concurrency',
|
||
priority: 'Priority',
|
||
priorityHint: 'Higher priority accounts are used first',
|
||
higherPriorityFirst: 'Higher value means higher priority',
|
||
mixedScheduling: 'Mixed Scheduling',
|
||
mixedSchedulingHint: 'Enable to participate in Anthropic/Gemini group scheduling',
|
||
mixedSchedulingTooltip:
|
||
'When enabled, this account can be scheduled by /v1/messages and /v1beta endpoints. Otherwise, it will only be scheduled by /antigravity. Note: Anthropic Claude and Antigravity Claude cannot be mixed in the same context. Please manage groups carefully when enabled.',
|
||
creating: 'Creating...',
|
||
updating: 'Updating...',
|
||
accountCreated: 'Account created successfully',
|
||
accountUpdated: 'Account updated successfully',
|
||
failedToCreate: 'Failed to create account',
|
||
failedToUpdate: 'Failed to update account',
|
||
pleaseEnterAccountName: 'Please enter account name',
|
||
pleaseEnterApiKey: 'Please enter API Key',
|
||
apiKeyIsRequired: 'API Key is required',
|
||
leaveEmptyToKeep: 'Leave empty to keep current key',
|
||
// OAuth flow
|
||
oauth: {
|
||
title: 'Claude Account Authorization',
|
||
authMethod: 'Authorization Method',
|
||
manualAuth: 'Manual Authorization',
|
||
cookieAutoAuth: 'Cookie Auto-Auth',
|
||
cookieAutoAuthDesc:
|
||
'Use claude.ai sessionKey to automatically complete OAuth authorization without manually opening browser.',
|
||
sessionKey: 'sessionKey',
|
||
keysCount: '{count} keys',
|
||
batchCreateAccounts: 'Will batch create {count} accounts',
|
||
sessionKeyPlaceholder:
|
||
'One sessionKey per line, e.g.:\nsk-ant-sid01-xxxxx...\nsk-ant-sid01-yyyyy...',
|
||
sessionKeyPlaceholderSingle: 'sk-ant-sid01-xxxxx...',
|
||
howToGetSessionKey: 'How to get sessionKey',
|
||
step1: 'Login to <strong>claude.ai</strong> in your browser',
|
||
step2: 'Press <kbd>F12</kbd> to open Developer Tools',
|
||
step3: 'Go to <strong>Application</strong> tab',
|
||
step4: 'Find <strong>Cookies</strong> → <strong>https://claude.ai</strong>',
|
||
step5: 'Find the row with key <strong>sessionKey</strong>',
|
||
step6: 'Copy the <strong>Value</strong>',
|
||
sessionKeyFormat: 'sessionKey usually starts with <code>sk-ant-sid01-</code>',
|
||
startAutoAuth: 'Start Auto-Auth',
|
||
authorizing: 'Authorizing...',
|
||
followSteps: 'Follow these steps to authorize your Claude account:',
|
||
step1GenerateUrl: 'Click the button below to generate the authorization URL',
|
||
generateAuthUrl: 'Generate Auth URL',
|
||
generating: 'Generating...',
|
||
regenerate: 'Regenerate',
|
||
step2OpenUrl: 'Open the URL in your browser and complete authorization',
|
||
openUrlDesc:
|
||
'Open the authorization URL in a new tab, log in to your Claude account and authorize.',
|
||
proxyWarning:
|
||
'<strong>Note:</strong> If you configured a proxy, make sure your browser uses the same proxy to access the authorization page.',
|
||
step3EnterCode: 'Enter the Authorization Code',
|
||
authCodeDesc:
|
||
'After authorization is complete, the page will display an <strong>Authorization Code</strong>. Copy and paste it below:',
|
||
authCode: 'Authorization Code',
|
||
authCodePlaceholder: 'Paste the Authorization Code from Claude page...',
|
||
authCodeHint: 'Paste the Authorization Code copied from the Claude page',
|
||
completeAuth: 'Complete Authorization',
|
||
verifying: 'Verifying...',
|
||
pleaseEnterSessionKey: 'Please enter at least one valid sessionKey',
|
||
authFailed: 'Authorization failed',
|
||
cookieAuthFailed: 'Cookie authorization failed',
|
||
keyAuthFailed: 'Key {index}: {error}',
|
||
successCreated: 'Successfully created {count} account(s)',
|
||
// OpenAI specific
|
||
openai: {
|
||
title: 'OpenAI Account Authorization',
|
||
followSteps: 'Follow these steps to complete OpenAI account authorization:',
|
||
step1GenerateUrl: 'Click the button below to generate the authorization URL',
|
||
generateAuthUrl: 'Generate Auth URL',
|
||
step2OpenUrl: 'Open the URL in your browser and complete authorization',
|
||
openUrlDesc:
|
||
'Open the authorization URL in a new tab, log in to your OpenAI account and authorize.',
|
||
importantNotice:
|
||
'<strong>Important:</strong> The page may take a while to load after authorization. Please wait patiently. When the browser address bar changes to <code>http://localhost...</code>, the authorization is complete.',
|
||
step3EnterCode: 'Enter Authorization URL or Code',
|
||
authCodeDesc:
|
||
'After authorization is complete, when the page URL becomes <code>http://localhost:xxx/auth/callback?code=...</code>:',
|
||
authCode: 'Authorization URL or Code',
|
||
authCodePlaceholder:
|
||
'Option 1: Copy the complete URL\n(http://localhost:xxx/auth/callback?code=...)\nOption 2: Copy only the code parameter value',
|
||
authCodeHint:
|
||
'You can copy the entire URL or just the code parameter value, the system will auto-detect'
|
||
},
|
||
// Gemini specific
|
||
gemini: {
|
||
title: 'Gemini Account Authorization',
|
||
followSteps: 'Follow these steps to authorize your Gemini account:',
|
||
step1GenerateUrl: 'Generate the authorization URL',
|
||
generateAuthUrl: 'Generate Auth URL',
|
||
projectIdLabel: 'Project ID (optional)',
|
||
projectIdPlaceholder: 'e.g. my-gcp-project or cloud-ai-companion-xxxxx',
|
||
projectIdHint:
|
||
'Leave empty to auto-detect after code exchange. If auto-detection fails, fill it in and re-generate the auth URL to try again.',
|
||
howToGetProjectId: 'How to get',
|
||
step2OpenUrl: 'Open the URL in your browser and complete authorization',
|
||
openUrlDesc:
|
||
'Open the authorization URL in a new tab, log in to your Google account and authorize.',
|
||
step3EnterCode: 'Enter Authorization URL or Code',
|
||
authCodeDesc:
|
||
'After authorization, copy the callback URL (recommended) or just the <code>code</code> and paste it below.',
|
||
authCode: 'Callback URL or Code',
|
||
authCodePlaceholder:
|
||
'Option 1 (recommended): Paste the callback URL\nOption 2: Paste only the code value',
|
||
authCodeHint: 'The system will auto-extract code/state from the URL.',
|
||
redirectUri: 'Redirect URI',
|
||
redirectUriHint:
|
||
'This must be configured in your Google OAuth client and must match exactly.',
|
||
confirmRedirectUri:
|
||
'I have configured this Redirect URI in the Google OAuth client (must match exactly)',
|
||
invalidRedirectUri: 'Redirect URI must be a valid http(s) URL',
|
||
redirectUriNotConfirmed: 'Please confirm the Redirect URI is configured correctly',
|
||
missingRedirectUri: 'Missing redirect URI',
|
||
failedToGenerateUrl: 'Failed to generate Gemini auth URL',
|
||
missingExchangeParams: 'Missing auth code, session ID, or state',
|
||
failedToExchangeCode: 'Failed to exchange Gemini auth code',
|
||
modelPassthrough: 'Gemini Model Passthrough',
|
||
modelPassthroughDesc:
|
||
'All model requests are forwarded directly to the Gemini API without model restrictions or mappings.',
|
||
stateWarningTitle: 'Note',
|
||
stateWarningDesc: 'Recommended: paste the full callback URL (includes code & state).',
|
||
oauthTypeLabel: 'OAuth Type',
|
||
needsProjectId: 'For GCP Developers',
|
||
needsProjectIdDesc: 'Requires GCP project',
|
||
noProjectIdNeeded: 'For Regular Users',
|
||
noProjectIdNeededDesc: 'Requires admin-configured OAuth client',
|
||
aiStudioNotConfiguredShort: 'Not configured',
|
||
aiStudioNotConfiguredTip:
|
||
'AI Studio OAuth is not configured: set GEMINI_OAUTH_CLIENT_ID / GEMINI_OAUTH_CLIENT_SECRET and add Redirect URI: http://localhost:1455/auth/callback (Consent screen scopes must include https://www.googleapis.com/auth/generative-language.retriever)',
|
||
aiStudioNotConfigured:
|
||
'AI Studio OAuth is not configured: set GEMINI_OAUTH_CLIENT_ID / GEMINI_OAUTH_CLIENT_SECRET and add Redirect URI: http://localhost:1455/auth/callback'
|
||
},
|
||
// Antigravity specific
|
||
antigravity: {
|
||
title: 'Antigravity Account Authorization',
|
||
followSteps: 'Follow these steps to authorize your Antigravity account:',
|
||
step1GenerateUrl: 'Generate the authorization URL',
|
||
generateAuthUrl: 'Generate Auth URL',
|
||
step2OpenUrl: 'Open the URL in your browser and complete authorization',
|
||
openUrlDesc: 'Open the authorization URL in a new tab, log in to your Google account and authorize.',
|
||
importantNotice:
|
||
'<strong>Important:</strong> The page may take a while to load after authorization. Please wait patiently. When the browser address bar shows <code>http://localhost...</code>, authorization is complete.',
|
||
step3EnterCode: 'Enter Authorization URL or Code',
|
||
authCodeDesc:
|
||
'After authorization, when the page URL becomes <code>http://localhost:xxx/auth/callback?code=...</code>:',
|
||
authCode: 'Authorization URL or Code',
|
||
authCodePlaceholder:
|
||
'Option 1: Copy the complete URL\n(http://localhost:xxx/auth/callback?code=...)\nOption 2: Copy only the code parameter value',
|
||
authCodeHint: 'You can copy the entire URL or just the code parameter value, the system will auto-detect',
|
||
failedToGenerateUrl: 'Failed to generate Antigravity auth URL',
|
||
missingExchangeParams: 'Missing code, session ID, or state',
|
||
failedToExchangeCode: 'Failed to exchange Antigravity auth code'
|
||
}
|
||
},
|
||
// Gemini specific (platform-wide)
|
||
gemini: {
|
||
modelPassthrough: 'Gemini Model Passthrough',
|
||
modelPassthroughDesc:
|
||
'All model requests are forwarded directly to the Gemini API without model restrictions or mappings.',
|
||
baseUrlHint: 'Leave default for official Gemini API',
|
||
apiKeyHint: 'Your Gemini API Key (starts with AIza)'
|
||
},
|
||
// Re-Auth Modal
|
||
reAuthorizeAccount: 'Re-Authorize Account',
|
||
claudeCodeAccount: 'Claude Code Account',
|
||
openaiAccount: 'OpenAI Account',
|
||
geminiAccount: 'Gemini Account',
|
||
antigravityAccount: 'Antigravity Account',
|
||
inputMethod: 'Input Method',
|
||
reAuthorizedSuccess: 'Account re-authorized successfully',
|
||
// Test Modal
|
||
testAccountConnection: 'Test Account Connection',
|
||
account: 'Account',
|
||
readyToTest: 'Ready to test. Click "Start Test" to begin...',
|
||
connectingToApi: 'Connecting to API...',
|
||
testCompleted: 'Test completed successfully!',
|
||
testFailed: 'Test failed',
|
||
connectedToApi: 'Connected to API',
|
||
usingModel: 'Using model: {model}',
|
||
sendingTestMessage: 'Sending test message: "hi"',
|
||
response: 'Response:',
|
||
startTest: 'Start Test',
|
||
testing: 'Testing...',
|
||
retry: 'Retry',
|
||
copyOutput: 'Copy output',
|
||
startingTestForAccount: 'Starting test for account: {name}',
|
||
testAccountTypeLabel: 'Account type: {type}',
|
||
selectTestModel: 'Select Test Model',
|
||
testModel: 'Test model',
|
||
testPrompt: 'Prompt: "hi"',
|
||
// Stats Modal
|
||
viewStats: 'View Stats',
|
||
usageStatistics: 'Usage Statistics',
|
||
last30DaysUsage: 'Last 30 days usage statistics (based on actual usage days)',
|
||
stats: {
|
||
totalCost: '30-Day Total Cost',
|
||
accumulatedCost: 'Accumulated cost',
|
||
standardCost: 'Standard',
|
||
totalRequests: '30-Day Total Requests',
|
||
totalCalls: 'Total API calls',
|
||
avgDailyCost: 'Daily Avg Cost',
|
||
basedOnActualDays: 'Based on {days} actual usage days',
|
||
avgDailyRequests: 'Daily Avg Requests',
|
||
avgDailyUsage: 'Average daily usage',
|
||
todayOverview: 'Today Overview',
|
||
cost: 'Cost',
|
||
requests: 'Requests',
|
||
tokens: 'Tokens',
|
||
highestCostDay: 'Highest Cost Day',
|
||
highestRequestDay: 'Highest Request Day',
|
||
date: 'Date',
|
||
accumulatedTokens: 'Accumulated Tokens',
|
||
totalTokens: '30-Day Total',
|
||
dailyAvgTokens: 'Daily Average',
|
||
performance: 'Performance',
|
||
avgResponseTime: 'Avg Response',
|
||
daysActive: 'Days Active',
|
||
recentActivity: 'Recent Activity',
|
||
todayRequests: 'Today Requests',
|
||
todayTokens: 'Today Tokens',
|
||
todayCost: 'Today Cost',
|
||
usageTrend: '30-Day Cost & Request Trend',
|
||
noData: 'No usage data available for this account'
|
||
},
|
||
usageWindow: {
|
||
statsTitle: '5-Hour Window Usage Statistics',
|
||
gemini3Pro: 'G3P',
|
||
gemini3Flash: 'G3F',
|
||
gemini3Image: 'G3I',
|
||
claude45: 'C4.5'
|
||
},
|
||
tier: {
|
||
free: 'Free',
|
||
pro: 'Pro',
|
||
ultra: 'Ultra'
|
||
}
|
||
},
|
||
|
||
// Proxies
|
||
proxies: {
|
||
title: 'Proxy Management',
|
||
description: 'Manage proxy servers for accounts',
|
||
createProxy: 'Create Proxy',
|
||
editProxy: 'Edit Proxy',
|
||
deleteProxy: 'Delete Proxy',
|
||
searchProxies: 'Search proxies...',
|
||
allProtocols: 'All Protocols',
|
||
allStatus: 'All Status',
|
||
columns: {
|
||
name: 'Name',
|
||
protocol: 'Protocol',
|
||
address: 'Address',
|
||
status: 'Status',
|
||
actions: 'Actions'
|
||
},
|
||
testConnection: 'Test Connection',
|
||
batchTest: 'Test All Proxies',
|
||
testFailed: 'Failed',
|
||
name: 'Name',
|
||
protocol: 'Protocol',
|
||
host: 'Host',
|
||
port: 'Port',
|
||
username: 'Username (Optional)',
|
||
password: 'Password (Optional)',
|
||
status: 'Status',
|
||
enterProxyName: 'Enter proxy name',
|
||
leaveEmptyToKeep: 'Leave empty to keep current',
|
||
optionalAuth: 'Optional authentication',
|
||
form: {
|
||
hostPlaceholder: 'proxy.example.com',
|
||
portPlaceholder: '8080'
|
||
},
|
||
noProxiesYet: 'No proxies yet',
|
||
createFirstProxy: 'Create your first proxy to route traffic through it.',
|
||
// Batch import
|
||
standardAdd: 'Standard Add',
|
||
batchAdd: 'Quick Add',
|
||
batchInput: 'Proxy List',
|
||
batchInputPlaceholder:
|
||
"Enter one proxy per line in the following formats:\nsocks5://user:pass{'@'}192.168.1.1:1080\nhttp://192.168.1.1:8080\nhttps://user:pass{'@'}proxy.example.com:443",
|
||
batchInputHint:
|
||
"Supports http, https, socks5 protocols. Format: protocol://[user:pass{'@'}]host:port",
|
||
parsedCount: '{count} valid',
|
||
invalidCount: '{count} invalid',
|
||
duplicateCount: '{count} duplicate',
|
||
importing: 'Importing...',
|
||
importProxies: 'Import {count} proxies',
|
||
batchImportSuccess: 'Successfully imported {created} proxies, skipped {skipped} duplicates',
|
||
batchImportAllSkipped: 'All {skipped} proxies already exist, skipped import',
|
||
failedToImport: 'Failed to batch import',
|
||
// Other messages
|
||
creating: 'Creating...',
|
||
updating: 'Updating...',
|
||
proxyCreated: 'Proxy created successfully',
|
||
proxyUpdated: 'Proxy updated successfully',
|
||
proxyDeleted: 'Proxy deleted successfully',
|
||
proxyWorking: 'Proxy is working!',
|
||
proxyWorkingWithLatency: 'Proxy is working! Latency: {latency}ms',
|
||
proxyTestFailed: 'Proxy test failed',
|
||
failedToLoad: 'Failed to load proxies',
|
||
failedToCreate: 'Failed to create proxy',
|
||
failedToUpdate: 'Failed to update proxy',
|
||
failedToDelete: 'Failed to delete proxy',
|
||
failedToTest: 'Failed to test proxy',
|
||
deleteConfirm:
|
||
"Are you sure you want to delete '{name}'? Accounts using this proxy will have their proxy removed."
|
||
},
|
||
|
||
// Redeem Codes
|
||
redeem: {
|
||
title: 'Redeem Code Management',
|
||
description: 'Generate and manage redeem codes',
|
||
generateCodes: 'Generate Codes',
|
||
searchCodes: 'Search codes...',
|
||
allTypes: 'All Types',
|
||
allStatus: 'All Status',
|
||
balance: 'Balance',
|
||
concurrency: 'Concurrency',
|
||
subscription: 'Subscription',
|
||
unused: 'Unused',
|
||
used: 'Used',
|
||
columns: {
|
||
code: 'Code',
|
||
type: 'Type',
|
||
value: 'Value',
|
||
status: 'Status',
|
||
usedBy: 'Used By',
|
||
usedAt: 'Used At',
|
||
actions: 'Actions'
|
||
},
|
||
userPrefix: 'User #{id}',
|
||
exportCsv: 'Export CSV',
|
||
deleteAllUnused: 'Delete All Unused Codes',
|
||
deleteCode: 'Delete Redeem Code',
|
||
deleteCodeConfirm:
|
||
'Are you sure you want to delete this redeem code? This action cannot be undone.',
|
||
deleteAllUnusedConfirm:
|
||
'Are you sure you want to delete all unused (active) redeem codes? This action cannot be undone.',
|
||
deleteAll: 'Delete All',
|
||
generateCodesTitle: 'Generate Redeem Codes',
|
||
generatedSuccessfully: 'Generated Successfully',
|
||
codesCreated: '{count} redeem code(s) created',
|
||
codeType: 'Code Type',
|
||
amount: 'Amount ($)',
|
||
value: 'Value',
|
||
count: 'Count',
|
||
generating: 'Generating...',
|
||
generate: 'Generate',
|
||
copyAll: 'Copy All',
|
||
copied: 'Copied!',
|
||
download: 'Download',
|
||
codesExported: 'Codes exported successfully',
|
||
codeDeleted: 'Redeem code deleted successfully',
|
||
codesDeleted: 'Successfully deleted {count} unused code(s)',
|
||
noUnusedCodes: 'No unused codes to delete',
|
||
failedToLoad: 'Failed to load redeem codes',
|
||
failedToGenerate: 'Failed to generate codes',
|
||
failedToExport: 'Failed to export codes',
|
||
failedToDelete: 'Failed to delete code',
|
||
failedToDeleteUnused: 'Failed to delete unused codes',
|
||
failedToCopy: 'Failed to copy codes',
|
||
selectGroup: 'Select Group',
|
||
selectGroupPlaceholder: 'Choose a subscription group',
|
||
validityDays: 'Validity Days',
|
||
groupRequired: 'Please select a subscription group',
|
||
days: ' days'
|
||
},
|
||
|
||
// Usage Records
|
||
usage: {
|
||
title: 'Usage Records',
|
||
description: 'View and manage all user usage records',
|
||
userFilter: 'User',
|
||
searchUserPlaceholder: 'Search user by email...',
|
||
selectedUser: 'Selected',
|
||
user: 'User',
|
||
account: 'Account',
|
||
group: 'Group',
|
||
requestId: 'Request ID',
|
||
requestIdCopied: 'Request ID copied',
|
||
allModels: 'All Models',
|
||
allAccounts: 'All Accounts',
|
||
allGroups: 'All Groups',
|
||
allTypes: 'All Types',
|
||
allBillingTypes: 'All Billing',
|
||
inputCost: 'Input Cost',
|
||
outputCost: 'Output Cost',
|
||
cacheCreationCost: 'Cache Creation Cost',
|
||
cacheReadCost: 'Cache Read Cost',
|
||
inputTokens: 'Input Tokens',
|
||
outputTokens: 'Output Tokens',
|
||
cacheCreationTokens: 'Cache Creation Tokens',
|
||
cacheReadTokens: 'Cache Read Tokens',
|
||
failedToLoad: 'Failed to load usage records'
|
||
},
|
||
|
||
// Settings
|
||
settings: {
|
||
title: 'System Settings',
|
||
description: 'Manage registration, email verification, default values, and SMTP settings',
|
||
registration: {
|
||
title: 'Registration Settings',
|
||
description: 'Control user registration and verification',
|
||
enableRegistration: 'Enable Registration',
|
||
enableRegistrationHint: 'Allow new users to register',
|
||
emailVerification: 'Email Verification',
|
||
emailVerificationHint: 'Require email verification for new registrations'
|
||
},
|
||
turnstile: {
|
||
title: 'Cloudflare Turnstile',
|
||
description: 'Bot protection for login and registration',
|
||
enableTurnstile: 'Enable Turnstile',
|
||
enableTurnstileHint: 'Require Cloudflare Turnstile verification',
|
||
siteKey: 'Site Key',
|
||
secretKey: 'Secret Key',
|
||
siteKeyHint: 'Get this from your Cloudflare Dashboard',
|
||
secretKeyHint: 'Server-side verification key (keep this secret)'
|
||
},
|
||
defaults: {
|
||
title: 'Default User Settings',
|
||
description: 'Default values for new users',
|
||
defaultBalance: 'Default Balance',
|
||
defaultBalanceHint: 'Initial balance for new users',
|
||
defaultConcurrency: 'Default Concurrency',
|
||
defaultConcurrencyHint: 'Maximum concurrent requests for new users'
|
||
},
|
||
site: {
|
||
title: 'Site Settings',
|
||
description: 'Customize site branding',
|
||
siteName: 'Site Name',
|
||
siteNamePlaceholder: 'Sub2API',
|
||
siteNameHint: 'Displayed in emails and page titles',
|
||
siteSubtitle: 'Site Subtitle',
|
||
siteSubtitlePlaceholder: 'Subscription to API Conversion Platform',
|
||
siteSubtitleHint: 'Displayed on login and register pages',
|
||
apiBaseUrl: 'API Base URL',
|
||
apiBaseUrlPlaceholder: 'https://api.example.com',
|
||
apiBaseUrlHint:
|
||
'Used for "Use Key" and "Import to CC Switch" features. Leave empty to use current site URL.',
|
||
contactInfo: 'Contact Info',
|
||
contactInfoPlaceholder: 'e.g., QQ: 123456789',
|
||
contactInfoHint: 'Customer support contact info, displayed on redeem page, profile, etc.',
|
||
docUrl: 'Documentation URL',
|
||
docUrlPlaceholder: 'https://docs.example.com',
|
||
docUrlHint: 'Link to your documentation site. Leave empty to hide the documentation link.',
|
||
siteLogo: 'Site Logo',
|
||
uploadImage: 'Upload Image',
|
||
remove: 'Remove',
|
||
logoHint: 'PNG, JPG, or SVG. Max 300KB. Recommended: 80x80px square image.',
|
||
logoSizeError: 'Image size exceeds 300KB limit ({size}KB)',
|
||
logoTypeError: 'Please select an image file',
|
||
logoReadError: 'Failed to read the image file'
|
||
},
|
||
smtp: {
|
||
title: 'SMTP Settings',
|
||
description: 'Configure email sending for verification codes',
|
||
testConnection: 'Test Connection',
|
||
testing: 'Testing...',
|
||
host: 'SMTP Host',
|
||
hostPlaceholder: 'smtp.gmail.com',
|
||
port: 'SMTP Port',
|
||
portPlaceholder: '587',
|
||
username: 'SMTP Username',
|
||
usernamePlaceholder: "your-email{'@'}gmail.com",
|
||
password: 'SMTP Password',
|
||
passwordPlaceholder: '********',
|
||
passwordHint: 'Leave empty to keep existing password',
|
||
fromEmail: 'From Email',
|
||
fromEmailPlaceholder: "noreply{'@'}example.com",
|
||
fromName: 'From Name',
|
||
fromNamePlaceholder: 'Sub2API',
|
||
useTls: 'Use TLS',
|
||
useTlsHint: 'Enable TLS encryption for SMTP connection'
|
||
},
|
||
testEmail: {
|
||
title: 'Send Test Email',
|
||
description: 'Send a test email to verify your SMTP configuration',
|
||
recipientEmail: 'Recipient Email',
|
||
recipientEmailPlaceholder: "test{'@'}example.com",
|
||
sendTestEmail: 'Send Test Email',
|
||
sending: 'Sending...',
|
||
enterRecipientHint: 'Please enter a recipient email address'
|
||
},
|
||
adminApiKey: {
|
||
title: 'Admin API Key',
|
||
description: 'Global API key for external system integration with full admin access',
|
||
notConfigured: 'Admin API key not configured',
|
||
configured: 'Admin API key is active',
|
||
currentKey: 'Current Key',
|
||
regenerate: 'Regenerate',
|
||
regenerating: 'Regenerating...',
|
||
delete: 'Delete',
|
||
deleting: 'Deleting...',
|
||
create: 'Create Key',
|
||
creating: 'Creating...',
|
||
regenerateConfirm: 'Are you sure? The current key will be immediately invalidated.',
|
||
deleteConfirm:
|
||
'Are you sure you want to delete the admin API key? External integrations will stop working.',
|
||
keyGenerated: 'New admin API key generated',
|
||
keyDeleted: 'Admin API key deleted',
|
||
copyKey: 'Copy Key',
|
||
keyCopied: 'Key copied to clipboard',
|
||
keyWarning: 'This key will only be shown once. Please copy it now.',
|
||
securityWarning: 'Warning: This key provides full admin access. Keep it secure.',
|
||
usage: 'Usage: Add to request header - x-api-key: <your-admin-api-key>'
|
||
},
|
||
saveSettings: 'Save Settings',
|
||
saving: 'Saving...',
|
||
settingsSaved: 'Settings saved successfully',
|
||
smtpConnectionSuccess: 'SMTP connection successful',
|
||
testEmailSent: 'Test email sent successfully',
|
||
failedToLoad: 'Failed to load settings',
|
||
failedToSave: 'Failed to save settings',
|
||
failedToTestSmtp: 'SMTP connection test failed',
|
||
failedToSendTestEmail: 'Failed to send test email'
|
||
}
|
||
},
|
||
|
||
// Subscription Progress (Header component)
|
||
subscriptionProgress: {
|
||
title: 'My Subscriptions',
|
||
viewDetails: 'View subscription details',
|
||
activeCount: '{count} active subscription(s)',
|
||
daily: 'Daily',
|
||
weekly: 'Weekly',
|
||
monthly: 'Monthly',
|
||
daysRemaining: '{days} days left',
|
||
expired: 'Expired',
|
||
expiresToday: 'Expires today',
|
||
expiresTomorrow: 'Expires tomorrow',
|
||
viewAll: 'View all subscriptions',
|
||
noSubscriptions: 'No active subscriptions'
|
||
},
|
||
|
||
// Version Badge
|
||
version: {
|
||
currentVersion: 'Current Version',
|
||
latestVersion: 'Latest Version',
|
||
upToDate: "You're running the latest version.",
|
||
updateAvailable: 'A new version is available!',
|
||
releaseNotes: 'Release Notes',
|
||
noReleaseNotes: 'No release notes',
|
||
viewUpdate: 'View Update',
|
||
viewRelease: 'View Release',
|
||
viewChangelog: 'View Changelog',
|
||
refresh: 'Refresh',
|
||
sourceMode: 'Source Build',
|
||
sourceModeHint: 'Source build, use git pull to update',
|
||
updateNow: 'Update Now',
|
||
updating: 'Updating...',
|
||
updateComplete: 'Update Complete',
|
||
updateFailed: 'Update Failed',
|
||
restartRequired: 'Please restart the service to apply the update',
|
||
restartNow: 'Restart Now',
|
||
restarting: 'Restarting...',
|
||
retry: 'Retry'
|
||
},
|
||
|
||
// User Subscriptions Page
|
||
userSubscriptions: {
|
||
title: 'My Subscriptions',
|
||
description: 'View your subscription plans and usage',
|
||
noActiveSubscriptions: 'No Active Subscriptions',
|
||
noActiveSubscriptionsDesc:
|
||
"You don't have any active subscriptions. Contact administrator to get one.",
|
||
status: {
|
||
active: 'Active',
|
||
expired: 'Expired',
|
||
revoked: 'Revoked'
|
||
},
|
||
usage: 'Usage',
|
||
expires: 'Expires',
|
||
noExpiration: 'No expiration',
|
||
unlimited: 'Unlimited',
|
||
daily: 'Daily',
|
||
weekly: 'Weekly',
|
||
monthly: 'Monthly',
|
||
daysRemaining: '{days} days remaining',
|
||
expiresOn: 'Expires on {date}',
|
||
resetIn: 'Resets in {time}',
|
||
windowNotActive: 'Awaiting first use',
|
||
usageOf: '{used} of {limit}'
|
||
},
|
||
|
||
// Onboarding Tour
|
||
onboarding: {
|
||
restartTour: 'Restart Onboarding Tour',
|
||
dontShowAgain: "Don't show again",
|
||
dontShowAgainTitle: 'Permanently close onboarding guide',
|
||
confirmDontShow: "Are you sure you don't want to see the onboarding guide again?\n\nYou can restart it anytime from the user menu in the top right corner.",
|
||
confirmExit: 'Are you sure you want to exit the onboarding guide? You can restart it anytime from the top right menu.',
|
||
interactiveHint: 'Press Enter or Click to continue',
|
||
navigation: {
|
||
flipPage: 'Flip Page',
|
||
exit: 'Exit'
|
||
},
|
||
// Admin tour steps
|
||
admin: {
|
||
welcome: {
|
||
title: '👋 Welcome to Sub2API',
|
||
description: '<div style="line-height: 1.8;"><p style="margin-bottom: 16px;">Sub2API is a powerful AI service gateway platform that helps you easily manage and distribute AI services.</p><p style="margin-bottom: 12px;"><b>🎯 Core Features:</b></p><ul style="margin-left: 20px; margin-bottom: 16px;"><li>📦 <b>Group Management</b> - Create service tiers (VIP, Free Trial, etc.)</li><li>🔗 <b>Account Pool</b> - Connect multiple upstream AI service accounts</li><li>🔑 <b>Key Distribution</b> - Generate independent API Keys for users</li><li>💰 <b>Billing Control</b> - Flexible rate and quota management</li></ul><p style="color: #10b981; font-weight: 600;">Let\'s complete the initial setup in 3 minutes →</p></div>',
|
||
nextBtn: 'Start Setup 🚀',
|
||
prevBtn: 'Skip'
|
||
},
|
||
groupManage: {
|
||
title: '📦 Step 1: Group Management',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;"><b>What is a Group?</b></p><p style="margin-bottom: 12px;">Groups are the core concept of Sub2API, like a "service package":</p><ul style="margin-left: 20px; margin-bottom: 12px; font-size: 13px;"><li>🎯 Each group can contain multiple upstream accounts</li><li>💰 Each group has independent billing multiplier</li><li>👥 Can be set as public or exclusive</li></ul><p style="margin-top: 12px; padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 Example:</b> You can create "VIP Premium" (high rate) and "Free Trial" (low rate) groups</p><p style="margin-top: 16px; color: #10b981; font-weight: 600;">👉 Click "Group Management" on the left sidebar</p></div>'
|
||
},
|
||
createGroup: {
|
||
title: '➕ Create New Group',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Let\'s create your first group.</p><p style="padding: 8px 12px; background: #eff6ff; border-left: 3px solid #3b82f6; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>📝 Tip:</b> Recommend creating a test group first to familiarize yourself with the process</p><p style="color: #10b981; font-weight: 600;">👉 Click the "Create Group" button</p></div>'
|
||
},
|
||
groupName: {
|
||
title: '✏️ 1. Group Name',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Give your group an easy-to-identify name.</p><div style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>💡 Naming Suggestions:</b><ul style="margin: 8px 0 0 16px;"><li>"Test Group" - For testing</li><li>"VIP Premium" - High-quality service</li><li>"Free Trial" - Trial version</li></ul></div><p style="font-size: 13px; color: #6b7280;">Click "Next" when done</p></div>',
|
||
nextBtn: 'Next'
|
||
},
|
||
groupPlatform: {
|
||
title: '🤖 2. Select Platform',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Choose the AI platform this group supports.</p><div style="padding: 8px 12px; background: #eff6ff; border-left: 3px solid #3b82f6; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>📌 Platform Guide:</b><ul style="margin: 8px 0 0 16px;"><li><b>Anthropic</b> - Claude models</li><li><b>OpenAI</b> - GPT models</li><li><b>Google</b> - Gemini models</li></ul></div><p style="font-size: 13px; color: #6b7280;">One group can only have one platform</p></div>',
|
||
nextBtn: 'Next'
|
||
},
|
||
groupMultiplier: {
|
||
title: '💰 3. Rate Multiplier',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Set the billing multiplier to control user charges.</p><div style="padding: 8px 12px; background: #fef3c7; border-left: 3px solid #f59e0b; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>⚙️ Billing Rules:</b><ul style="margin: 8px 0 0 16px;"><li><b>1.0</b> - Original price (cost price)</li><li><b>1.5</b> - User consumes $1, charged $1.5</li><li><b>2.0</b> - User consumes $1, charged $2</li><li><b>0.8</b> - Subsidy mode (loss-making)</li></ul></div><p style="font-size: 13px; color: #6b7280;">Recommend setting test group to 1.0</p></div>',
|
||
nextBtn: 'Next'
|
||
},
|
||
groupExclusive: {
|
||
title: '🔒 4. Exclusive Group (Optional)',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Control group visibility and access permissions.</p><div style="padding: 8px 12px; background: #eff6ff; border-left: 3px solid #3b82f6; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>🔐 Permission Guide:</b><ul style="margin: 8px 0 0 16px;"><li><b>Off</b> - Public group, visible to all users</li><li><b>On</b> - Exclusive group, only for specified users</li></ul></div><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 Use Cases:</b> VIP exclusive, internal testing, special customers</p></div>',
|
||
nextBtn: 'Next'
|
||
},
|
||
groupSubmit: {
|
||
title: '✅ Save Group',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Confirm the information and click create to save the group.</p><p style="padding: 8px 12px; background: #fef3c7; border-left: 3px solid #f59e0b; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>⚠️ Note:</b> Platform type cannot be changed after creation, but other settings can be edited anytime</p><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>📌 Next Step:</b> After creation, we\'ll add upstream accounts to this group</p><p style="margin-top: 12px; color: #10b981; font-weight: 600;">👉 Click "Create" button</p></div>'
|
||
},
|
||
accountManage: {
|
||
title: '🔗 Step 2: Add Account',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;"><b>Great! Group created successfully 🎉</b></p><p style="margin-bottom: 12px;">Now add upstream AI service accounts to enable actual service delivery.</p><div style="padding: 8px 12px; background: #eff6ff; border-left: 3px solid #3b82f6; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>🔑 Account Purpose:</b><ul style="margin: 8px 0 0 16px;"><li>Connect to upstream AI services (Claude, GPT, etc.)</li><li>One group can contain multiple accounts (load balancing)</li><li>Supports OAuth and Session Key methods</li></ul></div><p style="margin-top: 16px; color: #10b981; font-weight: 600;">👉 Click "Account Management" on the left sidebar</p></div>'
|
||
},
|
||
createAccount: {
|
||
title: '➕ Add New Account',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Click the button to start adding your first upstream account.</p><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 Tip:</b> Recommend using OAuth method - more secure and no manual key extraction needed</p><p style="margin-top: 12px; color: #10b981; font-weight: 600;">👉 Click "Add Account" button</p></div>'
|
||
},
|
||
accountName: {
|
||
title: '✏️ 1. Account Name',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Set an easy-to-identify name for the account.</p><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 Naming Suggestions:</b> "Claude Main", "GPT Backup 1", "Test Account", etc.</p></div>',
|
||
nextBtn: 'Next'
|
||
},
|
||
accountPlatform: {
|
||
title: '🤖 2. Select Platform',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Choose the service provider platform for this account.</p><p style="padding: 8px 12px; background: #fef3c7; border-left: 3px solid #f59e0b; border-radius: 4px; font-size: 13px;"><b>⚠️ Important:</b> Platform must match the group you just created</p></div>',
|
||
nextBtn: 'Next'
|
||
},
|
||
accountType: {
|
||
title: '🔐 3. Authorization Method',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Choose the account authorization method.</p><div style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>✅ Recommended: OAuth Method</b><ul style="margin: 8px 0 0 16px;"><li>No manual key extraction needed</li><li>More secure with auto-refresh support</li><li>Works with Claude Code, ChatGPT OAuth</li></ul></div><div style="padding: 8px 12px; background: #eff6ff; border-left: 3px solid #3b82f6; border-radius: 4px; font-size: 13px;"><b>📌 Session Key Method</b><ul style="margin: 8px 0 0 16px;"><li>Requires manual extraction from browser</li><li>May need periodic updates</li><li>For platforms without OAuth support</li></ul></div></div>',
|
||
nextBtn: 'Next'
|
||
},
|
||
accountPriority: {
|
||
title: '⚖️ 4. Priority (Optional)',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Set the account call priority.</p><div style="padding: 8px 12px; background: #eff6ff; border-left: 3px solid #3b82f6; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>📊 Priority Rules:</b><ul style="margin: 8px 0 0 16px;"><li>Higher number = higher priority</li><li>System uses high-priority accounts first</li><li>Same priority = random selection</li></ul></div><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 Use Case:</b> Set main account to high priority, backup accounts to low priority</p></div>',
|
||
nextBtn: 'Next'
|
||
},
|
||
accountGroups: {
|
||
title: '🎯 5. Assign Groups',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;"><b>Key Step!</b> Assign the account to the group you just created.</p><div style="padding: 8px 12px; background: #fee2e2; border-left: 3px solid #ef4444; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>⚠️ Important Reminder:</b><ul style="margin: 8px 0 0 16px;"><li>Must select at least one group</li><li>Unassigned accounts cannot be used</li><li>One account can be assigned to multiple groups</li></ul></div><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 Tip:</b> Select the test group you just created</p></div>',
|
||
nextBtn: 'Next'
|
||
},
|
||
accountSubmit: {
|
||
title: '✅ Save Account',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Confirm the information and click save.</p><div style="padding: 8px 12px; background: #eff6ff; border-left: 3px solid #3b82f6; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>📌 OAuth Flow:</b><ul style="margin: 8px 0 0 16px;"><li>Will redirect to service provider page after clicking save</li><li>Complete login and authorization on provider page</li><li>Auto-return after successful authorization</li></ul></div><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>📌 Next Step:</b> After adding account, we\'ll create an API key</p><p style="margin-top: 12px; color: #10b981; font-weight: 600;">👉 Click "Save" button</p></div>'
|
||
},
|
||
keyManage: {
|
||
title: '🔑 Step 3: Generate Key',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;"><b>Congratulations! Account setup complete 🎉</b></p><p style="margin-bottom: 12px;">Final step: generate an API Key to test if the service works properly.</p><div style="padding: 8px 12px; background: #eff6ff; border-left: 3px solid #3b82f6; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>🔑 API Key Purpose:</b><ul style="margin: 8px 0 0 16px;"><li>Credential for calling AI services</li><li>Each key is bound to one group</li><li>Can set quota and expiration</li><li>Supports independent usage statistics</li></ul></div><p style="margin-top: 16px; color: #10b981; font-weight: 600;">👉 Click "API Keys" on the left sidebar</p></div>'
|
||
},
|
||
createKey: {
|
||
title: '➕ Create Key',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Click the button to create your first API Key.</p><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 Tip:</b> Copy and save immediately after creation - key is only shown once</p><p style="margin-top: 12px; color: #10b981; font-weight: 600;">👉 Click "Create Key" button</p></div>'
|
||
},
|
||
keyName: {
|
||
title: '✏️ 1. Key Name',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Set an easy-to-manage name for the key.</p><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 Naming Suggestions:</b> "Test Key", "Production", "Mobile", etc.</p></div>',
|
||
nextBtn: 'Next'
|
||
},
|
||
keyGroup: {
|
||
title: '🎯 2. Select Group',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Select the group you just configured.</p><div style="padding: 8px 12px; background: #eff6ff; border-left: 3px solid #3b82f6; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>📌 Group Determines:</b><ul style="margin: 8px 0 0 16px;"><li>Which accounts this key can use</li><li>What billing multiplier applies</li><li>Whether it\'s an exclusive key</li></ul></div><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 Tip:</b> Select the test group you just created</p></div>',
|
||
nextBtn: 'Next'
|
||
},
|
||
keySubmit: {
|
||
title: '🎉 Generate and Copy',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">System will generate a complete API Key after clicking create.</p><div style="padding: 8px 12px; background: #fee2e2; border-left: 3px solid #ef4444; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>⚠️ Important Reminder:</b><ul style="margin: 8px 0 0 16px;"><li>Key is only shown once, copy immediately</li><li>Need to regenerate if lost</li><li>Keep it safe, don\'t share with others</li></ul></div><div style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>🚀 Next Steps:</b><ul style="margin: 8px 0 0 16px;"><li>Copy the generated sk-xxx key</li><li>Use in any OpenAI-compatible client</li><li>Start experiencing AI services!</li></ul></div><p style="margin-top: 12px; color: #10b981; font-weight: 600;">👉 Click "Create" button</p></div>'
|
||
}
|
||
},
|
||
// User tour steps
|
||
user: {
|
||
welcome: {
|
||
title: '👋 Welcome to Sub2API',
|
||
description: '<div style="line-height: 1.8;"><p style="margin-bottom: 16px;">Hello! Welcome to the Sub2API AI service platform.</p><p style="margin-bottom: 12px;"><b>🎯 Quick Start:</b></p><ul style="margin-left: 20px; margin-bottom: 16px;"><li>🔑 Create API Key</li><li>📋 Copy key to your application</li><li>🚀 Start using AI services</li></ul><p style="color: #10b981; font-weight: 600;">Just 1 minute, let\'s get started →</p></div>',
|
||
nextBtn: 'Start 🚀',
|
||
prevBtn: 'Skip'
|
||
},
|
||
keyManage: {
|
||
title: '🔑 API Key Management',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Manage all your API access keys here.</p><p style="padding: 8px 12px; background: #eff6ff; border-left: 3px solid #3b82f6; border-radius: 4px; font-size: 13px;"><b>📌 What is an API Key?</b><br/>An API key is your credential for accessing AI services, like a key that allows your application to call AI capabilities.</p><p style="margin-top: 12px; color: #10b981; font-weight: 600;">👉 Click to enter key page</p></div>'
|
||
},
|
||
createKey: {
|
||
title: '➕ Create New Key',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Click the button to create your first API key.</p><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 Tip:</b> Key is only shown once after creation, make sure to copy and save</p><p style="margin-top: 12px; color: #10b981; font-weight: 600;">👉 Click "Create Key"</p></div>'
|
||
},
|
||
keyName: {
|
||
title: '✏️ Key Name',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Give your key an easy-to-identify name.</p><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 Examples:</b> "My First Key", "For Testing", etc.</p></div>',
|
||
nextBtn: 'Next'
|
||
},
|
||
keyGroup: {
|
||
title: '🎯 Select Group',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Select the service group assigned by the administrator.</p><p style="padding: 8px 12px; background: #eff6ff; border-left: 3px solid #3b82f6; border-radius: 4px; font-size: 13px;"><b>📌 Group Info:</b><br/>Different groups may have different service quality and billing rates, choose according to your needs.</p></div>',
|
||
nextBtn: 'Next'
|
||
},
|
||
keySubmit: {
|
||
title: '🎉 Complete Creation',
|
||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;">Click to confirm and create your API key.</p><div style="padding: 8px 12px; background: #fee2e2; border-left: 3px solid #ef4444; border-radius: 4px; font-size: 13px; margin-bottom: 12px;"><b>⚠️ Important:</b><ul style="margin: 8px 0 0 16px;"><li>Copy the key (sk-xxx) immediately after creation</li><li>Key is only shown once, need to regenerate if lost</li></ul></div><p style="padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>🚀 How to Use:</b><br/>Configure the key in any OpenAI-compatible client (like ChatBox, OpenCat, etc.) and start using!</p><p style="margin-top: 12px; color: #10b981; font-weight: 600;">👉 Click "Create" button</p></div>'
|
||
}
|
||
}
|
||
}
|
||
}
|