2064 lines
95 KiB
TypeScript
2064 lines
95 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',
|
||
description: 'Connect to your PostgreSQL database',
|
||
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',
|
||
description: 'Connect to your Redis server',
|
||
host: 'Host',
|
||
port: 'Port',
|
||
password: 'Password (optional)',
|
||
database: 'Database',
|
||
passwordPlaceholder: 'Password'
|
||
},
|
||
admin: {
|
||
title: 'Admin Account',
|
||
description: 'Create your administrator 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',
|
||
description: 'Review your configuration and complete setup',
|
||
database: 'Database',
|
||
redis: 'Redis',
|
||
adminEmail: 'Admin Email'
|
||
},
|
||
status: {
|
||
testing: 'Testing...',
|
||
success: 'Connection Successful',
|
||
testConnection: 'Test Connection',
|
||
installing: 'Installing...',
|
||
completeInstallation: 'Complete Installation',
|
||
completed: 'Installation completed!',
|
||
redirecting: 'Redirecting to login page...',
|
||
restarting: 'Service is restarting, please wait...',
|
||
timeout: 'Service restart is taking longer than expected. Please refresh the page manually.'
|
||
}
|
||
},
|
||
|
||
// 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',
|
||
noGroupsAvailable: 'No groups available',
|
||
unknownError: 'Unknown error occurred',
|
||
saving: 'Saving...',
|
||
selectedCount: '({count} selected)', refresh: 'Refresh',
|
||
notAvailable: 'N/A',
|
||
now: 'Now',
|
||
unknown: 'Unknown',
|
||
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}.',
|
||
reloginRequired: 'Session expired. Please log in again.',
|
||
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.',
|
||
},
|
||
antigravity: {
|
||
description: 'Configure API access for Antigravity group. Select the configuration method based on your client.',
|
||
claudeCode: 'Claude Code',
|
||
geminiCli: 'Gemini CLI',
|
||
claudeNote: 'These environment variables will be active in the current terminal session. For permanent configuration, add them to ~/.bashrc, ~/.zshrc, or the appropriate configuration file.',
|
||
geminiNote: 'These environment variables will be active in the current terminal session. For permanent configuration, add them to ~/.bashrc, ~/.zshrc, or the appropriate configuration file.',
|
||
},
|
||
gemini: {
|
||
description: 'Add the following environment variables to your terminal profile or run directly in terminal to configure Gemini CLI access.',
|
||
modelComment: 'If you have Gemini 3 access, you can use: gemini-3-pro-preview',
|
||
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.',
|
||
},
|
||
},
|
||
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.',
|
||
ccsClientSelect: {
|
||
title: 'Select Client',
|
||
description: 'Please select the client type to import to CC-Switch:',
|
||
claudeCode: 'Claude Code',
|
||
claudeCodeDesc: 'Import as Claude Code configuration',
|
||
geminiCli: 'Gemini CLI',
|
||
geminiCliDesc: 'Import as Gemini CLI configuration',
|
||
},
|
||
},
|
||
|
||
// Usage
|
||
usage: {
|
||
title: 'Usage Records',
|
||
description: 'View and analyze your API usage history',
|
||
costDetails: 'Cost Breakdown',
|
||
tokenDetails: 'Token Breakdown',
|
||
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',
|
||
imageUnit: ' images'
|
||
},
|
||
|
||
// 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.',
|
||
pleaseEnterCode: 'Please enter a redeem code'
|
||
},
|
||
|
||
// 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',
|
||
enterUsername: 'Enter username',
|
||
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',
|
||
notes: 'Notes',
|
||
enterEmail: 'Enter email',
|
||
enterPassword: 'Enter password',
|
||
enterUsername: 'Enter username (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',
|
||
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',
|
||
emailRequired: 'Please enter email',
|
||
concurrencyMin: 'Concurrency must be at least 1',
|
||
amountRequired: 'Please enter a valid amount',
|
||
insufficientBalance: 'Insufficient balance',
|
||
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',
|
||
roles: {
|
||
admin: 'Admin',
|
||
user: 'User'
|
||
},
|
||
// Settings Dropdowns
|
||
filterSettings: 'Filter Settings',
|
||
columnSettings: 'Column Settings',
|
||
filterValue: 'Enter value',
|
||
// User Attributes
|
||
attributes: {
|
||
title: 'User Attributes',
|
||
description: 'Configure custom user attribute fields',
|
||
configButton: 'Attributes',
|
||
addAttribute: 'Add Attribute',
|
||
editAttribute: 'Edit Attribute',
|
||
deleteAttribute: 'Delete Attribute',
|
||
deleteConfirm: "Are you sure you want to delete attribute '{name}'? All user values for this attribute will be deleted.",
|
||
noAttributes: 'No custom attributes',
|
||
noAttributesHint: 'Click the button above to add custom attributes',
|
||
key: 'Attribute Key',
|
||
keyHint: 'For programmatic reference, only letters, numbers and underscores',
|
||
name: 'Display Name',
|
||
nameHint: 'Name shown in forms',
|
||
type: 'Attribute Type',
|
||
fieldDescription: 'Description',
|
||
fieldDescriptionHint: 'Description text for the attribute',
|
||
placeholder: 'Placeholder',
|
||
placeholderHint: 'Placeholder text for input field',
|
||
required: 'Required',
|
||
enabled: 'Enabled',
|
||
options: 'Options',
|
||
optionsHint: 'For select/multi-select types',
|
||
addOption: 'Add Option',
|
||
optionValue: 'Option Value',
|
||
optionLabel: 'Display Text',
|
||
validation: 'Validation Rules',
|
||
minLength: 'Min Length',
|
||
maxLength: 'Max Length',
|
||
min: 'Min Value',
|
||
max: 'Max Value',
|
||
pattern: 'Regex Pattern',
|
||
patternMessage: 'Validation Error Message',
|
||
types: {
|
||
text: 'Text',
|
||
textarea: 'Textarea',
|
||
number: 'Number',
|
||
email: 'Email',
|
||
url: 'URL',
|
||
date: 'Date',
|
||
select: 'Select',
|
||
multi_select: 'Multi-Select'
|
||
},
|
||
created: 'Attribute created successfully',
|
||
updated: 'Attribute updated successfully',
|
||
deleted: 'Attribute deleted successfully',
|
||
reordered: 'Attribute order updated successfully',
|
||
failedToLoad: 'Failed to load attributes',
|
||
failedToCreate: 'Failed to create attribute',
|
||
failedToUpdate: 'Failed to update attribute',
|
||
keyRequired: 'Please enter attribute key',
|
||
nameRequired: 'Please enter display name',
|
||
optionsRequired: 'Please add at least one option',
|
||
failedToDelete: 'Failed to delete attribute',
|
||
failedToReorder: 'Failed to update order',
|
||
keyExists: 'Attribute key already exists',
|
||
dragToReorder: 'Drag to reorder'
|
||
}
|
||
},
|
||
|
||
// Groups
|
||
groups: {
|
||
title: 'Group Management',
|
||
description: 'Manage API key groups and rate multipliers',
|
||
searchGroups: 'Search groups...',
|
||
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',
|
||
nameRequired: 'Please enter group name',
|
||
platforms: {
|
||
all: 'All Platforms',
|
||
anthropic: 'Anthropic',
|
||
openai: 'OpenAI',
|
||
gemini: 'Gemini',
|
||
antigravity: 'Antigravity'
|
||
},
|
||
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'
|
||
},
|
||
imagePricing: {
|
||
title: 'Image Generation Pricing',
|
||
description: 'Configure pricing for gemini-3-pro-image model. Leave empty to use default prices.'
|
||
}
|
||
},
|
||
|
||
// 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',
|
||
unlimited: 'Unlimited',
|
||
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',
|
||
pleaseSelectUser: 'Please select a user',
|
||
pleaseSelectGroup: 'Please select a group',
|
||
validityDaysRequired: 'Please enter a valid number of days (at least 1)',
|
||
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...',
|
||
notes: 'Notes',
|
||
notesPlaceholder: 'Enter notes',
|
||
notesHint: 'Notes are optional',
|
||
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'
|
||
},
|
||
status: {
|
||
active: 'Active',
|
||
inactive: 'Inactive',
|
||
error: 'Error',
|
||
cooldown: 'Cooldown',
|
||
paused: 'Paused',
|
||
limited: 'Limited',
|
||
tempUnschedulable: 'Temp Unschedulable',
|
||
rateLimitedUntil: 'Rate limited until {time}',
|
||
overloadedUntil: 'Overloaded until {time}',
|
||
viewTempUnschedDetails: 'View temp unschedulable details'
|
||
},
|
||
columns: {
|
||
name: 'Name',
|
||
platformType: 'Platform/Type',
|
||
platform: 'Platform',
|
||
type: 'Type',
|
||
concurrencyStatus: 'Concurrency',
|
||
notes: 'Notes',
|
||
priority: 'Priority',
|
||
weight: 'Weight',
|
||
status: 'Status',
|
||
schedulable: 'Schedulable',
|
||
todayStats: 'Today Stats',
|
||
groups: 'Groups',
|
||
usageWindows: 'Usage Windows',
|
||
lastUsed: 'Last Used',
|
||
expiresAt: 'Expires At',
|
||
actions: 'Actions'
|
||
},
|
||
tempUnschedulable: {
|
||
title: 'Temp Unschedulable',
|
||
statusTitle: 'Temp Unschedulable Status',
|
||
hint: 'Disable accounts temporarily when error code and keyword both match.',
|
||
notice: 'Rules are evaluated in order and require both error code and keyword match.',
|
||
addRule: 'Add Rule',
|
||
ruleOrder: 'Rule Order',
|
||
ruleIndex: 'Rule #{index}',
|
||
errorCode: 'Error Code',
|
||
errorCodePlaceholder: 'e.g. 429',
|
||
durationMinutes: 'Duration (minutes)',
|
||
durationPlaceholder: 'e.g. 30',
|
||
keywords: 'Keywords',
|
||
keywordsPlaceholder: 'e.g. overloaded, too many requests',
|
||
keywordsHint: 'Separate keywords with commas; any keyword match will trigger.',
|
||
description: 'Description',
|
||
descriptionPlaceholder: 'Optional note for this rule',
|
||
rulesInvalid: 'Add at least one rule with error code, keywords, and duration.',
|
||
viewDetails: 'View temp unschedulable details',
|
||
accountName: 'Account',
|
||
triggeredAt: 'Triggered At',
|
||
until: 'Until',
|
||
remaining: 'Remaining',
|
||
matchedKeyword: 'Matched Keyword',
|
||
errorMessage: 'Error Details',
|
||
reset: 'Reset Status',
|
||
resetSuccess: 'Temp unschedulable status reset',
|
||
resetFailed: 'Failed to reset temp unschedulable status',
|
||
failedToLoad: 'Failed to load temp unschedulable status',
|
||
notActive: 'This account is not temporarily unschedulable.',
|
||
expired: 'Expired',
|
||
remainingMinutes: 'About {minutes} minutes',
|
||
remainingHours: 'About {hours} hours',
|
||
remainingHoursMinutes: 'About {hours} hours {minutes} minutes',
|
||
presets: {
|
||
overloadLabel: '529 Overloaded',
|
||
overloadDesc: 'Overloaded - pause 60 minutes',
|
||
rateLimitLabel: '429 Rate Limit',
|
||
rateLimitDesc: 'Rate limited - pause 10 minutes',
|
||
unavailableLabel: '503 Unavailable',
|
||
unavailableDesc: 'Unavailable - pause 30 minutes'
|
||
}
|
||
},
|
||
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',
|
||
searchModels: 'Search models...',
|
||
noMatchingModels: 'No matching models',
|
||
fillRelatedModels: 'Fill related models',
|
||
clearAllModels: 'Clear all models',
|
||
customModelName: 'Custom model name',
|
||
enterCustomModelName: 'Enter custom model name',
|
||
addModel: 'Add',
|
||
modelExists: 'Model already exists',
|
||
modelCount: '{count} models',
|
||
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',
|
||
autoPauseOnExpired: 'Auto Pause On Expired',
|
||
autoPauseOnExpiredDesc: 'When enabled, the account will auto pause scheduling after it expires',
|
||
expired: 'Expired',
|
||
proxy: 'Proxy',
|
||
noProxy: 'No Proxy',
|
||
concurrency: 'Concurrency',
|
||
priority: 'Priority',
|
||
priorityHint: 'Higher priority accounts are used first',
|
||
expiresAt: 'Expires At',
|
||
expiresAtHint: 'Leave empty for no expiration',
|
||
higherPriorityFirst: 'Higher value means higher priority',
|
||
mixedScheduling: 'Use in /v1/messages',
|
||
mixedSchedulingHint: 'Enable to participate in Anthropic/Gemini group scheduling',
|
||
mixedSchedulingTooltip:
|
||
'!! WARNING !! Antigravity Claude and Anthropic Claude cannot be used in the same context. If you have both Anthropic and Antigravity accounts, enabling this option will cause frequent 400 errors. When enabled, please use the group feature to isolate Antigravity accounts from Anthropic accounts. Make sure you understand this before enabling!!',
|
||
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 claude.ai in your browser',
|
||
step2: 'Press F12 to open Developer Tools',
|
||
step3: 'Go to Application tab',
|
||
step4: 'Find Cookies → https://claude.ai',
|
||
step5: 'Find the row with key sessionKey',
|
||
step6: 'Copy the Value',
|
||
sessionKeyFormat: 'sessionKey usually starts with sk-ant-sid01-',
|
||
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:
|
||
'Note: 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 Authorization Code. 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:
|
||
'Important: The page may take a while to load after authorization. Please wait patiently. When the browser address bar changes to http://localhost..., the authorization is complete.',
|
||
step3EnterCode: 'Enter Authorization URL or Code',
|
||
authCodeDesc:
|
||
'After authorization is complete, when the page URL becomes http://localhost:xxx/auth/callback?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 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',
|
||
missingProjectId: 'GCP Project ID retrieval failed: Your Google account is not linked to an active GCP project. Please activate GCP and bind a credit card in Google Cloud Console, or manually enter the Project ID during authorization.',
|
||
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: 'Built-in OAuth (Code Assist)',
|
||
needsProjectIdDesc: 'Requires GCP project and Project ID',
|
||
noProjectIdNeeded: 'Custom OAuth (AI Studio)',
|
||
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:
|
||
'Important: The page may take a while to load after authorization. Please wait patiently. When the browser address bar shows http://localhost..., authorization is complete.',
|
||
step3EnterCode: 'Enter Authorization URL or Code',
|
||
authCodeDesc:
|
||
'After authorization, when the page URL becomes http://localhost:xxx/auth/callback?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: {
|
||
helpButton: 'Help',
|
||
helpDialog: {
|
||
title: 'Gemini Usage Guide',
|
||
apiKeySection: 'API Key Links'
|
||
},
|
||
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)',
|
||
tier: {
|
||
label: 'Account Tier',
|
||
hint: 'Tip: The system will try to auto-detect the tier first; if auto-detection is unavailable or fails, your selected tier is used as a fallback (simulated quota).',
|
||
aiStudioHint:
|
||
'AI Studio quotas are per-model (Pro/Flash are limited independently). If billing is enabled, choose Pay-as-you-go.',
|
||
googleOne: {
|
||
free: 'Google One Free',
|
||
pro: 'Google One Pro',
|
||
ultra: 'Google One Ultra'
|
||
},
|
||
gcp: {
|
||
standard: 'GCP Standard',
|
||
enterprise: 'GCP Enterprise'
|
||
},
|
||
aiStudio: {
|
||
free: 'Google AI Free',
|
||
paid: 'Google AI Pay-as-you-go'
|
||
}
|
||
},
|
||
accountType: {
|
||
oauthTitle: 'OAuth (Gemini)',
|
||
oauthDesc: 'Authorize with your Google account and choose an OAuth type.',
|
||
apiKeyTitle: 'API Key (AI Studio)',
|
||
apiKeyDesc: 'Fastest setup. Use an AIza API key.',
|
||
apiKeyNote:
|
||
'Best for light testing. Free tier has strict rate limits and data may be used for training.',
|
||
apiKeyLink: 'Get API Key',
|
||
quotaLink: 'Quota guide'
|
||
},
|
||
oauthType: {
|
||
builtInTitle: 'Built-in OAuth (Gemini CLI / Code Assist)',
|
||
builtInDesc: 'Uses Google built-in client ID. No admin configuration required.',
|
||
builtInRequirement: 'Requires a GCP project and Project ID.',
|
||
gcpProjectLink: 'Create project',
|
||
customTitle: 'Custom OAuth (AI Studio OAuth)',
|
||
customDesc: 'Uses admin-configured OAuth client for org management.',
|
||
customRequirement: 'Admin must configure Client ID and add you as a test user.',
|
||
badges: {
|
||
recommended: 'Recommended',
|
||
highConcurrency: 'High concurrency',
|
||
noAdmin: 'No admin setup',
|
||
orgManaged: 'Org managed',
|
||
adminRequired: 'Admin required'
|
||
}
|
||
},
|
||
setupGuide: {
|
||
title: 'Gemini Setup Checklist',
|
||
checklistTitle: 'Checklist',
|
||
checklistItems: {
|
||
usIp: 'Use a US IP and ensure your account country is set to US.',
|
||
age: 'Account must be 18+.'
|
||
},
|
||
activationTitle: 'One-click Activation',
|
||
activationItems: {
|
||
geminiWeb: 'Activate Gemini Web to avoid User not initialized.',
|
||
gcpProject: 'Activate a GCP project and get the Project ID for Code Assist.'
|
||
},
|
||
links: {
|
||
countryCheck: 'Check country association',
|
||
geminiWebActivation: 'Activate Gemini Web',
|
||
gcpProject: 'Open GCP Console'
|
||
}
|
||
},
|
||
quotaPolicy: {
|
||
title: 'Gemini Quota & Limit Policy (Reference)',
|
||
note: 'Note: Gemini does not provide an official quota inquiry API. The "Daily Quota" shown here is an estimate simulated by the system based on account tiers for scheduling reference only. Please refer to official Google errors for actual limits.',
|
||
columns: {
|
||
channel: 'Auth Channel',
|
||
account: 'Account Status',
|
||
limits: 'Limit Policy',
|
||
docs: 'Official Docs'
|
||
},
|
||
docs: {
|
||
codeAssist: 'Code Assist Quotas',
|
||
aiStudio: 'AI Studio Pricing',
|
||
vertex: 'Vertex AI Quotas'
|
||
},
|
||
simulatedNote: 'Simulated quota, for reference only',
|
||
rows: {
|
||
googleOne: {
|
||
channel: 'Google One OAuth (Individuals / Code Assist for Individuals)',
|
||
limitsFree: 'Shared pool: 1000 RPD / 60 RPM',
|
||
limitsPro: 'Shared pool: 1500 RPD / 120 RPM',
|
||
limitsUltra: 'Shared pool: 2000 RPD / 120 RPM'
|
||
},
|
||
gcp: {
|
||
channel: 'GCP Code Assist OAuth (Enterprise)',
|
||
limitsStandard: 'Shared pool: 1500 RPD / 120 RPM',
|
||
limitsEnterprise: 'Shared pool: 2000 RPD / 120 RPM'
|
||
},
|
||
cli: {
|
||
channel: 'Gemini CLI (Official Google Login / Code Assist)',
|
||
free: 'Free Google Account',
|
||
premium: 'Google One AI Premium',
|
||
limitsFree: 'RPD ~1000; RPM ~60 (soft)',
|
||
limitsPremium: 'RPD ~1500+; RPM ~60+ (priority queue)'
|
||
},
|
||
gcloud: {
|
||
channel: 'GCP Code Assist (gcloud auth)',
|
||
account: 'No Code Assist subscription',
|
||
limits: 'RPD ~1000; RPM ~60 (preview)'
|
||
},
|
||
aiStudio: {
|
||
channel: 'AI Studio API Key / OAuth',
|
||
free: 'No billing (free tier)',
|
||
paid: 'Billing enabled (pay-as-you-go)',
|
||
limitsFree: 'RPD 50; RPM 2 (Pro) / 15 (Flash)',
|
||
limitsPaid: 'RPD unlimited; RPM 1000 (Pro) / 2000 (Flash) (per model)'
|
||
},
|
||
customOAuth: {
|
||
channel: 'Custom OAuth Client (GCP)',
|
||
free: 'Project not billed',
|
||
paid: 'Project billed',
|
||
limitsFree: 'RPD 50; RPM 2 (project quota)',
|
||
limitsPaid: 'RPD unlimited; RPM 1000+ (project quota)'
|
||
}
|
||
}
|
||
},
|
||
rateLimit: {
|
||
ok: 'Not rate limited',
|
||
unlimited: 'Unlimited',
|
||
limited: 'Rate limited {time}',
|
||
now: 'now'
|
||
}
|
||
},
|
||
// 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',
|
||
statsTitleDaily: 'Daily Usage Statistics',
|
||
geminiProDaily: 'Pro',
|
||
geminiFlashDaily: 'Flash',
|
||
gemini3Pro: 'G3P',
|
||
gemini3Flash: 'G3F',
|
||
gemini3Image: 'G3I',
|
||
claude45: 'C4.5'
|
||
},
|
||
tier: {
|
||
free: 'Free',
|
||
pro: 'Pro',
|
||
ultra: 'Ultra',
|
||
aiPremium: 'AI Premium',
|
||
standard: 'Standard',
|
||
basic: 'Basic',
|
||
personal: 'Personal',
|
||
unlimited: 'Unlimited'
|
||
},
|
||
ineligibleWarning:
|
||
'This account is not eligible for Antigravity, but API forwarding still works. Use at your own risk.'
|
||
},
|
||
|
||
// 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',
|
||
protocols: {
|
||
http: 'HTTP',
|
||
https: 'HTTPS',
|
||
socks5: 'SOCKS5',
|
||
socks5h: 'SOCKS5H (Remote DNS)'
|
||
},
|
||
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',
|
||
nameRequired: 'Please enter proxy name',
|
||
hostRequired: 'Please enter host address',
|
||
portInvalid: 'Port must be between 1-65535',
|
||
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',
|
||
types: {
|
||
balance: 'Balance',
|
||
concurrency: 'Concurrency',
|
||
subscription: 'Subscription',
|
||
// Admin adjustment types (created when admin modifies user balance/concurrency)
|
||
admin_balance: 'Balance (Admin)',
|
||
admin_concurrency: 'Concurrency (Admin)'
|
||
},
|
||
selectGroup: 'Select Group',
|
||
selectGroupPlaceholder: 'Choose a subscription group',
|
||
validityDays: 'Validity Days',
|
||
groupRequired: 'Please select a subscription group',
|
||
days: ' days',
|
||
status: {
|
||
unused: 'Unused',
|
||
used: 'Used',
|
||
expired: 'Expired',
|
||
disabled: 'Disabled'
|
||
}
|
||
},
|
||
|
||
// Usage Records
|
||
usage: {
|
||
title: 'Usage Records',
|
||
description: 'View and manage all user usage records',
|
||
userFilter: 'User',
|
||
searchUserPlaceholder: 'Search user by email...',
|
||
searchApiKeyPlaceholder: 'Search API key by name...',
|
||
searchAccountPlaceholder: 'Search account by name...',
|
||
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',
|
||
cloudflareDashboard: 'Cloudflare Dashboard',
|
||
secretKeyHint: 'Server-side verification key (keep this secret)',
|
||
secretKeyConfiguredHint: 'Secret key configured. Leave empty to keep the current value.' },
|
||
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',
|
||
passwordConfiguredPlaceholder: '********',
|
||
passwordConfiguredHint: 'Password configured. Leave empty to keep the current value.',
|
||
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',
|
||
unlimited: 'Unlimited'
|
||
},
|
||
|
||
// 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.",
|
||
failedToLoad: 'Failed to load subscriptions',
|
||
status: {
|
||
active: 'Active',
|
||
expired: 'Expired',
|
||
revoked: 'Revoked'
|
||
},
|
||
usage: 'Usage',
|
||
expires: 'Expires',
|
||
noExpiration: 'No expiration',
|
||
unlimited: 'Unlimited',
|
||
unlimitedDesc: 'No usage limits on this subscription',
|
||
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>'
|
||
}
|
||
}
|
||
}
|
||
}
|