'
},
soraS3: {
title: 'Sora S3 Storage',
description: 'Manage multiple Sora S3 endpoints and switch the active profile',
newProfile: 'New Profile',
reloadProfiles: 'Reload Profiles',
empty: 'No Sora S3 profiles yet, create one first',
createTitle: 'Create Sora S3 Profile',
editTitle: 'Edit Sora S3 Profile',
profileID: 'Profile ID',
profileName: 'Profile Name',
setActive: 'Set as active after creation',
saveProfile: 'Save Profile',
activateProfile: 'Activate',
profileCreated: 'Sora S3 profile created',
profileSaved: 'Sora S3 profile saved',
profileDeleted: 'Sora S3 profile deleted',
profileActivated: 'Sora S3 active profile switched',
profileIDRequired: 'Profile ID is required',
profileNameRequired: 'Profile name is required',
profileSelectRequired: 'Please select a profile first',
endpointRequired: 'S3 endpoint is required when enabled',
bucketRequired: 'Bucket is required when enabled',
accessKeyRequired: 'Access Key ID is required when enabled',
deleteConfirm: 'Delete Sora S3 profile {profileID}?',
columns: {
profile: 'Profile',
active: 'Active',
endpoint: 'Endpoint',
bucket: 'Bucket',
quota: 'Default Quota',
updatedAt: 'Updated At',
actions: 'Actions'
},
enabled: 'Enable S3 Storage',
enabledHint: 'When enabled, Sora generated media files will be automatically uploaded to S3 storage',
endpoint: 'S3 Endpoint',
region: 'Region',
bucket: 'Bucket',
prefix: 'Object Prefix',
accessKeyId: 'Access Key ID',
secretAccessKey: 'Secret Access Key',
secretConfigured: '(Configured, leave blank to keep)',
cdnUrl: 'CDN URL',
cdnUrlHint: 'Optional. When configured, files are accessed via CDN URL instead of presigned URLs',
forcePathStyle: 'Force Path Style',
defaultQuota: 'Default Storage Quota',
defaultQuotaHint: 'Default quota when not specified at user or group level. 0 means unlimited',
testConnection: 'Test Connection',
testing: 'Testing...',
testSuccess: 'S3 connection test successful',
testFailed: 'S3 connection test failed',
saved: 'Sora S3 settings saved successfully',
saveFailed: 'Failed to save Sora S3 settings'
},
streamTimeout: {
title: 'Stream Timeout Handling',
description: 'Configure account handling strategy when upstream response times out',
enabled: 'Enable Stream Timeout Handling',
enabledHint: 'Automatically handle problematic accounts when upstream times out',
timeoutSeconds: 'Timeout Threshold (seconds)',
timeoutSecondsHint: 'Stream data interval exceeding this time is considered timeout (30-300s)',
action: 'Action',
actionTempUnsched: 'Temporarily Unschedulable',
actionError: 'Mark as Error',
actionNone: 'No Action',
actionHint: 'Action to take on the account after timeout',
tempUnschedMinutes: 'Pause Duration (minutes)',
tempUnschedMinutesHint: 'Duration of temporary unschedulable state (1-60 minutes)',
thresholdCount: 'Trigger Threshold (count)',
thresholdCountHint: 'Number of timeouts before triggering action (1-10)',
thresholdWindowMinutes: 'Threshold Window (minutes)',
thresholdWindowMinutesHint: 'Time window for counting timeouts (1-60 minutes)',
saved: 'Stream timeout settings saved',
saveFailed: 'Failed to save stream timeout settings'
},
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'
},
// Error Passthrough Rules
errorPassthrough: {
title: 'Error Passthrough Rules',
description: 'Configure how upstream errors are returned to clients',
createRule: 'Create Rule',
editRule: 'Edit Rule',
deleteRule: 'Delete Rule',
noRules: 'No rules configured',
createFirstRule: 'Create your first error passthrough rule',
allPlatforms: 'All Platforms',
passthrough: 'Passthrough',
custom: 'Custom',
code: 'Code',
body: 'Body',
skipMonitoring: 'Skip Monitoring',
// Columns
columns: {
priority: 'Priority',
name: 'Name',
conditions: 'Conditions',
platforms: 'Platforms',
behavior: 'Behavior',
status: 'Status',
actions: 'Actions'
},
// Match Mode
matchMode: {
any: 'Code OR Keyword',
all: 'Code AND Keyword',
anyHint: 'Status code matches any error code, OR message contains any keyword',
allHint: 'Status code matches any error code, AND message contains any keyword'
},
// Form
form: {
name: 'Rule Name',
namePlaceholder: 'e.g., Context Limit Passthrough',
priority: 'Priority',
priorityHint: 'Lower values have higher priority',
description: 'Description',
descriptionPlaceholder: 'Describe the purpose of this rule...',
matchConditions: 'Match Conditions',
errorCodes: 'Error Codes',
errorCodesPlaceholder: '422, 400, 429',
errorCodesHint: 'Separate multiple codes with commas',
keywords: 'Keywords',
keywordsPlaceholder: 'One keyword per line\ncontext limit\nmodel not supported',
keywordsHint: 'One keyword per line, case-insensitive',
matchMode: 'Match Mode',
platforms: 'Platforms',
platformsHint: 'Leave empty to apply to all platforms',
responseBehavior: 'Response Behavior',
passthroughCode: 'Passthrough upstream status code',
responseCode: 'Custom status code',
passthroughBody: 'Passthrough upstream error message',
customMessage: 'Custom error message',
customMessagePlaceholder: 'Error message to return to client...',
skipMonitoring: 'Skip monitoring',
skipMonitoringHint: 'When enabled, errors matching this rule will not be recorded in ops monitoring',
enabled: 'Enable this rule'
},
// Messages
nameRequired: 'Please enter rule name',
conditionsRequired: 'Please configure at least one error code or keyword',
ruleCreated: 'Rule created successfully',
ruleUpdated: 'Rule updated successfully',
ruleDeleted: 'Rule deleted successfully',
deleteConfirm: 'Are you sure you want to delete rule "{name}"?',
failedToLoad: 'Failed to load rules',
failedToSave: 'Failed to save rule',
failedToDelete: 'Failed to delete rule',
failedToToggle: 'Failed to toggle status'
}
},
// 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'
},
// Recharge / Subscription Page
purchase: {
title: 'Recharge / Subscription',
description: 'Recharge balance or purchase subscription via the embedded page',
openInNewTab: 'Open in new tab',
notEnabledTitle: 'Feature not enabled',
notEnabledDesc: 'The administrator has not enabled the recharge/subscription entry. Please contact admin.',
notConfiguredTitle: 'Recharge / Subscription URL not configured',
notConfiguredDesc:
'The administrator enabled the entry but has not configured a recharge/subscription URL. Please contact admin.'
},
// Custom Page (iframe embed)
customPage: {
title: 'Custom Page',
openInNewTab: 'Open in new tab',
notFoundTitle: 'Page not found',
notFoundDesc: 'This custom page does not exist or has been removed.',
notConfiguredTitle: 'Page URL not configured',
notConfiguredDesc: 'The URL for this custom page has not been properly configured.',
},
// Announcements Page
announcements: {
title: 'Announcements',
description: 'View system announcements',
unreadOnly: 'Show unread only',
markRead: 'Mark as read',
markAllRead: 'Mark all as read',
viewAll: 'View all announcements',
markedAsRead: 'Marked as read',
allMarkedAsRead: 'All announcements marked as read',
newCount: '{count} new announcement | {count} new announcements',
readAt: 'Read at',
read: 'Read',
unread: 'Unread',
startsAt: 'Starts at',
endsAt: 'Ends at',
empty: 'No announcements',
emptyUnread: 'No unread announcements',
total: 'announcements',
emptyDescription: 'There are no system announcements at this time',
readStatus: 'You have read this announcement',
markReadHint: 'Click "Mark as read" to mark this announcement'
},
// 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: 'Sub2API is a powerful AI service gateway platform that helps you easily manage and distribute AI services.
🎯 Core Features:
- 📦 Group Management - Create service tiers (VIP, Free Trial, etc.)
- 🔗 Account Pool - Connect multiple upstream AI service accounts
- 🔑 Key Distribution - Generate independent API Keys for users
- 💰 Billing Control - Flexible rate and quota management
Let\'s complete the initial setup in 3 minutes →
',
nextBtn: 'Start Setup 🚀',
prevBtn: 'Skip'
},
groupManage: {
title: '📦 Step 1: Group Management',
description: 'What is a Group?
Groups are the core concept of Sub2API, like a "service package":
- 🎯 Each group can contain multiple upstream accounts
- 💰 Each group has independent billing multiplier
- 👥 Can be set as public or exclusive
💡 Example: You can create "VIP Premium" (high rate) and "Free Trial" (low rate) groups
👉 Click "Group Management" on the left sidebar
'
},
createGroup: {
title: '➕ Create New Group',
description: 'Let\'s create your first group.
📝 Tip: Recommend creating a test group first to familiarize yourself with the process
👉 Click the "Create Group" button
'
},
groupName: {
title: '✏️ 1. Group Name',
description: 'Give your group an easy-to-identify name.
💡 Naming Suggestions:- "Test Group" - For testing
- "VIP Premium" - High-quality service
- "Free Trial" - Trial version
Click "Next" when done
',
nextBtn: 'Next'
},
groupPlatform: {
title: '🤖 2. Select Platform',
description: 'Choose the AI platform this group supports.
📌 Platform Guide:- Anthropic - Claude models
- OpenAI - GPT models
- Google - Gemini models
One group can only have one platform
',
nextBtn: 'Next'
},
groupMultiplier: {
title: '💰 3. Rate Multiplier',
description: 'Set the billing multiplier to control user charges.
⚙️ Billing Rules:- 1.0 - Original price (cost price)
- 1.5 - User consumes $1, charged $1.5
- 2.0 - User consumes $1, charged $2
- 0.8 - Subsidy mode (loss-making)
Recommend setting test group to 1.0
',
nextBtn: 'Next'
},
groupExclusive: {
title: '🔒 4. Exclusive Group (Optional)',
description: 'Control group visibility and access permissions.
🔐 Permission Guide:- Off - Public group, visible to all users
- On - Exclusive group, only for specified users
💡 Use Cases: VIP exclusive, internal testing, special customers
',
nextBtn: 'Next'
},
groupSubmit: {
title: '✅ Save Group',
description: 'Confirm the information and click create to save the group.
⚠️ Note: Platform type cannot be changed after creation, but other settings can be edited anytime
📌 Next Step: After creation, we\'ll add upstream accounts to this group
👉 Click "Create" button
'
},
accountManage: {
title: '🔗 Step 2: Add Account',
description: 'Great! Group created successfully 🎉
Now add upstream AI service accounts to enable actual service delivery.
🔑 Account Purpose:- Connect to upstream AI services (Claude, GPT, etc.)
- One group can contain multiple accounts (load balancing)
- Supports OAuth and Session Key methods
👉 Click "Account Management" on the left sidebar
'
},
createAccount: {
title: '➕ Add New Account',
description: 'Click the button to start adding your first upstream account.
💡 Tip: Recommend using OAuth method - more secure and no manual key extraction needed
👉 Click "Add Account" button
'
},
accountName: {
title: '✏️ 1. Account Name',
description: 'Set an easy-to-identify name for the account.
💡 Naming Suggestions: "Claude Main", "GPT Backup 1", "Test Account", etc.
',
nextBtn: 'Next'
},
accountPlatform: {
title: '🤖 2. Select Platform',
description: 'Choose the service provider platform for this account.
⚠️ Important: Platform must match the group you just created
',
nextBtn: 'Next'
},
accountType: {
title: '🔐 3. Authorization Method',
description: 'Choose the account authorization method.
✅ Recommended: OAuth Method- No manual key extraction needed
- More secure with auto-refresh support
- Works with Claude Code, ChatGPT OAuth
📌 Session Key Method- Requires manual extraction from browser
- May need periodic updates
- For platforms without OAuth support
',
nextBtn: 'Next'
},
accountPriority: {
title: '⚖️ 4. Priority (Optional)',
description: 'Set the account call priority.
📊 Priority Rules:- Lower number = higher priority
- System uses low-value accounts first
- Same priority = random selection
💡 Use Case: Set main account to lower value, backup accounts to higher value
',
nextBtn: 'Next'
},
accountGroups: {
title: '🎯 5. Assign Groups',
description: 'Key Step! Assign the account to the group you just created.
⚠️ Important Reminder:- Must select at least one group
- Unassigned accounts cannot be used
- One account can be assigned to multiple groups
💡 Tip: Select the test group you just created
',
nextBtn: 'Next'
},
accountSubmit: {
title: '✅ Save Account',
description: 'Confirm the information and click save.
📌 OAuth Flow:- Will redirect to service provider page after clicking save
- Complete login and authorization on provider page
- Auto-return after successful authorization
📌 Next Step: After adding account, we\'ll create an API key
👉 Click "Save" button
'
},
keyManage: {
title: '🔑 Step 3: Generate Key',
description: 'Congratulations! Account setup complete 🎉
Final step: generate an API Key to test if the service works properly.
🔑 API Key Purpose:- Credential for calling AI services
- Each key is bound to one group
- Can set quota and expiration
- Supports independent usage statistics
👉 Click "API Keys" on the left sidebar
'
},
createKey: {
title: '➕ Create Key',
description: 'Click the button to create your first API Key.
💡 Tip: Copy and save immediately after creation - key is only shown once
👉 Click "Create Key" button
'
},
keyName: {
title: '✏️ 1. Key Name',
description: 'Set an easy-to-manage name for the key.
💡 Naming Suggestions: "Test Key", "Production", "Mobile", etc.
',
nextBtn: 'Next'
},
keyGroup: {
title: '🎯 2. Select Group',
description: 'Select the group you just configured.
📌 Group Determines:- Which accounts this key can use
- What billing multiplier applies
- Whether it\'s an exclusive key
💡 Tip: Select the test group you just created
',
nextBtn: 'Next'
},
keySubmit: {
title: '🎉 Generate and Copy',
description: 'System will generate a complete API Key after clicking create.
⚠️ Important Reminder:- Key is only shown once, copy immediately
- Need to regenerate if lost
- Keep it safe, don\'t share with others
🚀 Next Steps:- Copy the generated sk-xxx key
- Use in any OpenAI-compatible client
- Start experiencing AI services!
👉 Click "Create" button
'
}
},
// User tour steps
user: {
welcome: {
title: '👋 Welcome to Sub2API',
description: 'Hello! Welcome to the Sub2API AI service platform.
🎯 Quick Start:
- 🔑 Create API Key
- 📋 Copy key to your application
- 🚀 Start using AI services
Just 1 minute, let\'s get started →
',
nextBtn: 'Start 🚀',
prevBtn: 'Skip'
},
keyManage: {
title: '🔑 API Key Management',
description: 'Manage all your API access keys here.
📌 What is an API Key?
An API key is your credential for accessing AI services, like a key that allows your application to call AI capabilities.
👉 Click to enter key page
'
},
createKey: {
title: '➕ Create New Key',
description: 'Click the button to create your first API key.
💡 Tip: Key is only shown once after creation, make sure to copy and save
👉 Click "Create Key"
'
},
keyName: {
title: '✏️ Key Name',
description: 'Give your key an easy-to-identify name.
💡 Examples: "My First Key", "For Testing", etc.
',
nextBtn: 'Next'
},
keyGroup: {
title: '🎯 Select Group',
description: 'Select the service group assigned by the administrator.
📌 Group Info:
Different groups may have different service quality and billing rates, choose according to your needs.
',
nextBtn: 'Next'
},
keySubmit: {
title: '🎉 Complete Creation',
description: 'Click to confirm and create your API key.
⚠️ Important:- Copy the key (sk-xxx) immediately after creation
- Key is only shown once, need to regenerate if lost
🚀 How to Use:
Configure the key in any OpenAI-compatible client (like ChatBox, OpenCat, etc.) and start using!
👉 Click "Create" button
'
}
}
},
// Sora Studio
sora: {
title: 'Sora Studio',
description: 'Generate videos and images with Sora AI',
notEnabled: 'Feature Not Available',
notEnabledDesc: 'The Sora Studio feature has not been enabled by the administrator. Please contact your admin.',
tabGenerate: 'Generate',
tabLibrary: 'Library',
noActiveGenerations: 'No active generations',
startGenerating: 'Enter a prompt below to start creating',
storage: 'Storage',
promptPlaceholder: 'Describe what you want to create...',
generate: 'Generate',
generating: 'Generating...',
selectModel: 'Select Model',
statusPending: 'Pending',
statusGenerating: 'Generating',
statusCompleted: 'Completed',
statusFailed: 'Failed',
statusCancelled: 'Cancelled',
cancel: 'Cancel',
delete: 'Delete',
save: 'Save to Cloud',
saved: 'Saved',
retry: 'Retry',
download: 'Download',
justNow: 'Just now',
minutesAgo: '{n} min ago',
hoursAgo: '{n} hr ago',
noSavedWorks: 'No saved works',
saveWorksHint: 'Save your completed generations to the library',
filterAll: 'All',
filterVideo: 'Video',
filterImage: 'Image',
confirmDelete: 'Are you sure you want to delete this work?',
loading: 'Loading...',
loadMore: 'Load More',
noStorageWarningTitle: 'No Storage Configured',
noStorageWarningDesc: 'Generated content is only available via temporary upstream links that expire in ~15 minutes. Consider configuring S3 storage.',
mediaTypeVideo: 'Video',
mediaTypeImage: 'Image',
notificationCompleted: 'Generation Complete',
notificationFailed: 'Generation Failed',
notificationCompletedBody: 'Your {model} task has completed',
notificationFailedBody: 'Your {model} task has failed',
upstreamExpiresSoon: 'Expiring soon',
upstreamExpired: 'Link expired',
upstreamCountdown: '{time} remaining',
previewTitle: 'Preview',
closePreview: 'Close',
beforeUnloadWarning: 'You have unsaved generated content. Are you sure you want to leave?',
downloadTitle: 'Download Generated Content',
downloadExpirationWarning: 'This link expires in approximately 15 minutes. Please download and save promptly.',
downloadNow: 'Download Now',
referenceImage: 'Reference Image',
removeImage: 'Remove',
imageTooLarge: 'Image size cannot exceed 20MB',
// Sora dark theme additions
welcomeTitle: 'Turn your imagination into video',
welcomeSubtitle: 'Enter a description and Sora will create realistic videos or images for you. Try the examples below to get started.',
queueTasks: 'tasks',
queueWaiting: 'Queued',
waiting: 'Waiting',
waited: 'Waited',
errorCategory: 'Content Policy Violation',
savedToCloud: 'Saved to Cloud',
downloadLocal: 'Download',
canDownload: 'to download',
regenrate: 'Regenerate',
creatorPlaceholder: 'Describe the video or image you want to create...',
videoModels: 'Video Models',
imageModels: 'Image Models',
noStorageConfigured: 'No Storage',
selectCredential: 'Select Credential',
apiKeys: 'API Keys',
subscriptions: 'Subscriptions',
subscription: 'Subscription',
noCredentialHint: 'Please create an API Key or contact admin for subscription',
uploadReference: 'Upload reference image',
generatingCount: 'Generating {current}/{max}',
noStorageToastMessage: 'Cloud storage is not configured. Please use "Download" to save files after generation, otherwise they will be lost.',
galleryCount: '{count} works',
galleryEmptyTitle: 'No works yet',
galleryEmptyDesc: 'Your creations will be displayed here. Go to the generate page to start your first creation.',
startCreating: 'Start Creating',
yesterday: 'Yesterday'
}
}