feat: Backend part translation
This commit is contained in:
12 files changed
+147
-74
No files matched your search
@@ -20,7 +20,7 @@ func (s *Server) Stats(c *gin.Context) {
|
||||
now := time.Now()
|
||||
rpmStats, err := s.getRPMStats(now)
|
||||
if err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrDatabase, "failed to get rpm stats"))
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrDatabase, "database.rpm_stats_failed")
|
||||
return
|
||||
}
|
||||
twentyFourHoursAgo := now.Add(-24 * time.Hour)
|
||||
@@ -28,12 +28,12 @@ func (s *Server) Stats(c *gin.Context) {
|
||||
|
||||
currentPeriod, err := s.getHourlyStats(twentyFourHoursAgo, now)
|
||||
if err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrDatabase, "failed to get current period stats"))
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrDatabase, "database.current_stats_failed")
|
||||
return
|
||||
}
|
||||
previousPeriod, err := s.getHourlyStats(fortyEightHoursAgo, twentyFourHoursAgo)
|
||||
if err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrDatabase, "failed to get previous period stats"))
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrDatabase, "database.previous_stats_failed")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ func (s *Server) Chart(c *gin.Context) {
|
||||
query = query.Where("group_id = ?", groupID)
|
||||
}
|
||||
if err := query.Order("time asc").Find(&hourlyStats).Error; err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrDatabase, "failed to get chart data"))
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrDatabase, "database.chart_data_failed")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -272,14 +272,14 @@ func checkPasswordSecurity(c *gin.Context, password, keyType string) []models.Se
|
||||
if len(password) < 16 {
|
||||
warnings = append(warnings, models.SecurityWarning{
|
||||
Type: keyType,
|
||||
Message: i18n.Message(c, "security.password_too_short", map[string]interface{}{"keyType": keyType, "length": len(password)}),
|
||||
Message: i18n.Message(c, "security.password_too_short", map[string]any{"keyType": keyType, "length": len(password)}),
|
||||
Severity: "high", // 长度不足是高风险
|
||||
Suggestion: i18n.Message(c, "security.password_recommendation_16"),
|
||||
})
|
||||
} else if len(password) < 32 {
|
||||
warnings = append(warnings, models.SecurityWarning{
|
||||
Type: keyType,
|
||||
Message: i18n.Message(c, "security.password_short", map[string]interface{}{"keyType": keyType, "length": len(password)}),
|
||||
Message: i18n.Message(c, "security.password_short", map[string]any{"keyType": keyType, "length": len(password)}),
|
||||
Severity: "medium",
|
||||
Suggestion: i18n.Message(c, "security.password_recommendation_32"),
|
||||
})
|
||||
@@ -297,7 +297,7 @@ func checkPasswordSecurity(c *gin.Context, password, keyType string) []models.Se
|
||||
if strings.Contains(lower, pattern) {
|
||||
warnings = append(warnings, models.SecurityWarning{
|
||||
Type: keyType,
|
||||
Message: i18n.Message(c, "security.password_weak_pattern", map[string]interface{}{"keyType": keyType, "pattern": pattern}),
|
||||
Message: i18n.Message(c, "security.password_weak_pattern", map[string]any{"keyType": keyType, "pattern": pattern}),
|
||||
Severity: "high",
|
||||
Suggestion: i18n.Message(c, "security.password_avoid_common"),
|
||||
})
|
||||
@@ -309,7 +309,7 @@ func checkPasswordSecurity(c *gin.Context, password, keyType string) []models.Se
|
||||
if len(password) >= 16 && !hasGoodComplexity(password) {
|
||||
warnings = append(warnings, models.SecurityWarning{
|
||||
Type: keyType,
|
||||
Message: i18n.Message(c, "security.password_low_complexity", map[string]interface{}{"keyType": keyType}),
|
||||
Message: i18n.Message(c, "security.password_low_complexity", map[string]any{"keyType": keyType}),
|
||||
Severity: "medium",
|
||||
Suggestion: i18n.Message(c, "security.password_complexity"),
|
||||
})
|
||||
|
||||
@@ -190,7 +190,7 @@ func (s *Server) CreateGroup(c *gin.Context) {
|
||||
channelType := strings.TrimSpace(req.ChannelType)
|
||||
if !isValidChannelType(channelType) {
|
||||
supported := strings.Join(channel.GetChannels(), ", ")
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.invalid_channel_type", map[string]interface{}{"types": supported})
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.invalid_channel_type", map[string]any{"types": supported})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -202,13 +202,13 @@ func (s *Server) CreateGroup(c *gin.Context) {
|
||||
|
||||
cleanedUpstreams, err := validateAndCleanUpstreams(req.Upstreams)
|
||||
if err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.invalid_upstreams", map[string]any{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
cleanedConfig, err := s.validateAndCleanConfig(req.Config)
|
||||
if err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, fmt.Sprintf("Invalid config format: %v", err)))
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "error.invalid_config_format", map[string]any{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ func (s *Server) CreateGroup(c *gin.Context) {
|
||||
|
||||
// Check for duplicate keys
|
||||
if seenKeys[canonicalKey] {
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.duplicate_header", map[string]interface{}{"key": canonicalKey})
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.duplicate_header", map[string]any{"key": canonicalKey})
|
||||
return
|
||||
}
|
||||
seenKeys[canonicalKey] = true
|
||||
@@ -250,7 +250,7 @@ func (s *Server) CreateGroup(c *gin.Context) {
|
||||
if len(normalizedHeaderRules) > 0 {
|
||||
headerRulesBytes, err := json.Marshal(normalizedHeaderRules)
|
||||
if err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrInternalServer, fmt.Sprintf("Failed to process header rules: %v", err)))
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrInternalServer, "error.process_header_rules", map[string]any{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
headerRulesJSON = headerRulesBytes
|
||||
@@ -378,7 +378,7 @@ func (s *Server) UpdateGroup(c *gin.Context) {
|
||||
cleanedChannelType := strings.TrimSpace(*req.ChannelType)
|
||||
if !isValidChannelType(cleanedChannelType) {
|
||||
supported := strings.Join(channel.GetChannels(), ", ")
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.invalid_channel_type", map[string]interface{}{"types": supported})
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.invalid_channel_type", map[string]any{"types": supported})
|
||||
return
|
||||
}
|
||||
group.ChannelType = cleanedChannelType
|
||||
@@ -409,7 +409,7 @@ func (s *Server) UpdateGroup(c *gin.Context) {
|
||||
if req.Config != nil {
|
||||
cleanedConfig, err := s.validateAndCleanConfig(req.Config)
|
||||
if err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, fmt.Sprintf("Invalid config format: %v", err)))
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "error.invalid_config_format", map[string]any{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
group.Config = cleanedConfig
|
||||
@@ -436,7 +436,7 @@ func (s *Server) UpdateGroup(c *gin.Context) {
|
||||
|
||||
// Check for duplicate keys
|
||||
if seenKeys[canonicalKey] {
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.duplicate_header", map[string]interface{}{"key": canonicalKey})
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.duplicate_header", map[string]any{"key": canonicalKey})
|
||||
return
|
||||
}
|
||||
seenKeys[canonicalKey] = true
|
||||
@@ -451,7 +451,7 @@ func (s *Server) UpdateGroup(c *gin.Context) {
|
||||
if len(normalizedHeaderRules) > 0 {
|
||||
headerRulesBytes, err := json.Marshal(normalizedHeaderRules)
|
||||
if err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrInternalServer, fmt.Sprintf("Failed to process header rules: %v", err)))
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrInternalServer, "error.process_header_rules", map[string]any{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
headerRulesJSON = headerRulesBytes
|
||||
@@ -607,8 +607,7 @@ func (s *Server) DeleteGroup(c *gin.Context) {
|
||||
"error": err,
|
||||
}).Error("Failed to remove keys from memory store, rolling back transaction")
|
||||
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrDatabase,
|
||||
"Failed to delete group: unable to clean up cache"))
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrDatabase, "error.delete_group_cache")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -623,7 +622,7 @@ func (s *Server) DeleteGroup(c *gin.Context) {
|
||||
if err := s.GroupManager.Invalidate(); err != nil {
|
||||
logrus.WithContext(c.Request.Context()).WithError(err).Error("failed to invalidate group cache")
|
||||
}
|
||||
response.Success(c, gin.H{"message": "Group and associated keys deleted successfully"})
|
||||
response.SuccessI18n(c, "success.group_deleted", nil)
|
||||
}
|
||||
|
||||
// ConfigOption represents a single configurable option for a group.
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"gpt-load/internal/config"
|
||||
"gpt-load/internal/encryption"
|
||||
"gpt-load/internal/i18n"
|
||||
"gpt-load/internal/services"
|
||||
"gpt-load/internal/types"
|
||||
|
||||
@@ -84,7 +85,7 @@ func (s *Server) Login(c *gin.Context) {
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": "Invalid request format",
|
||||
"message": i18n.Message(c, "auth.invalid_request"),
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -96,12 +97,12 @@ func (s *Server) Login(c *gin.Context) {
|
||||
if isValid {
|
||||
c.JSON(http.StatusOK, LoginResponse{
|
||||
Success: true,
|
||||
Message: "Authentication successful",
|
||||
Message: i18n.Message(c, "auth.authentication_successful"),
|
||||
})
|
||||
} else {
|
||||
c.JSON(http.StatusUnauthorized, LoginResponse{
|
||||
Success: false,
|
||||
Message: "Authentication failed",
|
||||
Message: i18n.Message(c, "auth.authentication_failed"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,27 +16,32 @@ import (
|
||||
)
|
||||
|
||||
// validateGroupIDFromQuery validates and parses group ID from a query parameter.
|
||||
func validateGroupIDFromQuery(c *gin.Context) (uint, error) {
|
||||
// Returns 0 and false if validation fails (error is already sent to client)
|
||||
func validateGroupIDFromQuery(c *gin.Context) (uint, bool) {
|
||||
groupIDStr := c.Query("group_id")
|
||||
if groupIDStr == "" {
|
||||
return 0, fmt.Errorf("group_id query parameter is required")
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrBadRequest, "validation.group_id_required")
|
||||
return 0, false
|
||||
}
|
||||
|
||||
groupID, err := strconv.Atoi(groupIDStr)
|
||||
if err != nil || groupID <= 0 {
|
||||
return 0, fmt.Errorf("invalid group_id format")
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrBadRequest, "validation.invalid_group_id_format")
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return uint(groupID), nil
|
||||
return uint(groupID), true
|
||||
}
|
||||
|
||||
// validateKeysText validates the keys text input
|
||||
func validateKeysText(keysText string) error {
|
||||
// Returns false if validation fails (error is already sent to client)
|
||||
func validateKeysText(c *gin.Context, keysText string) bool {
|
||||
if strings.TrimSpace(keysText) == "" {
|
||||
return fmt.Errorf("keys text cannot be empty")
|
||||
response.ErrorI18nFromAPIError(c, app_errors.ErrValidation, "validation.keys_text_empty")
|
||||
return false
|
||||
}
|
||||
|
||||
return nil
|
||||
return true
|
||||
}
|
||||
|
||||
// findGroupByID is a helper function to find a group by its ID.
|
||||
@@ -82,8 +87,7 @@ func (s *Server) AddMultipleKeys(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateKeysText(req.KeysText); err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||
if !validateKeysText(c, req.KeysText) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -115,8 +119,7 @@ func (s *Server) AddMultipleKeysAsync(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateKeysText(req.KeysText); err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||
if !validateKeysText(c, req.KeysText) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -131,9 +134,8 @@ func (s *Server) AddMultipleKeysAsync(c *gin.Context) {
|
||||
|
||||
// ListKeysInGroup handles listing all keys within a specific group with pagination.
|
||||
func (s *Server) ListKeysInGroup(c *gin.Context) {
|
||||
groupID, err := validateGroupIDFromQuery(c)
|
||||
if err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrBadRequest, err.Error()))
|
||||
groupID, ok := validateGroupIDFromQuery(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -189,8 +191,7 @@ func (s *Server) DeleteMultipleKeys(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateKeysText(req.KeysText); err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||
if !validateKeysText(c, req.KeysText) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -222,8 +223,7 @@ func (s *Server) DeleteMultipleKeysAsync(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateKeysText(req.KeysText); err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||
if !validateKeysText(c, req.KeysText) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -248,8 +248,7 @@ func (s *Server) RestoreMultipleKeys(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateKeysText(req.KeysText); err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||
if !validateKeysText(c, req.KeysText) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -287,8 +286,7 @@ func (s *Server) TestMultipleKeys(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateKeysText(req.KeysText); err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||
if !validateKeysText(c, req.KeysText) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -364,7 +362,7 @@ func (s *Server) RestoreAllInvalidKeys(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{"message": fmt.Sprintf("%d keys restored.", rowsAffected)})
|
||||
response.SuccessI18n(c, "success.keys_restored", nil, map[string]any{"count": rowsAffected})
|
||||
}
|
||||
|
||||
// ClearAllInvalidKeys deletes all 'inactive' keys from a group.
|
||||
@@ -385,7 +383,7 @@ func (s *Server) ClearAllInvalidKeys(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{"message": fmt.Sprintf("%d invalid keys cleared.", rowsAffected)})
|
||||
response.SuccessI18n(c, "success.invalid_keys_cleared", nil, map[string]any{"count": rowsAffected})
|
||||
}
|
||||
|
||||
// ClearAllKeys deletes all keys from a group.
|
||||
@@ -406,14 +404,13 @@ func (s *Server) ClearAllKeys(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{"message": fmt.Sprintf("%d keys cleared.", rowsAffected)})
|
||||
response.SuccessI18n(c, "success.all_keys_cleared", nil, map[string]any{"count": rowsAffected})
|
||||
}
|
||||
|
||||
// ExportKeys handles exporting keys to a text file.
|
||||
func (s *Server) ExportKeys(c *gin.Context) {
|
||||
groupID, err := validateGroupIDFromQuery(c)
|
||||
if err != nil {
|
||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrBadRequest, err.Error()))
|
||||
groupID, ok := validateGroupIDFromQuery(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -438,8 +435,7 @@ func (s *Server) ExportKeys(c *gin.Context) {
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.Header("Content-Type", "text/plain; charset=utf-8")
|
||||
|
||||
err = s.KeyService.StreamKeysToWriter(groupID, statusFilter, c.Writer)
|
||||
if err != nil {
|
||||
if err := s.KeyService.StreamKeysToWriter(groupID, statusFilter, c.Writer); err != nil {
|
||||
log.Printf("Failed to stream keys: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package handler
|
||||
import (
|
||||
"fmt"
|
||||
app_errors "gpt-load/internal/errors"
|
||||
"gpt-load/internal/i18n"
|
||||
"gpt-load/internal/models"
|
||||
"gpt-load/internal/response"
|
||||
"log"
|
||||
@@ -56,7 +57,7 @@ func (s *Server) ExportLogs(c *gin.Context) {
|
||||
err := s.LogService.StreamLogKeysToCSV(c, c.Writer)
|
||||
if err != nil {
|
||||
log.Printf("Failed to stream log keys to CSV: %v", err)
|
||||
c.JSON(500, gin.H{"error": "Failed to export logs"})
|
||||
c.JSON(500, gin.H{"error": i18n.Message(c, "error.export_logs")})
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,5 @@ func (s *Server) UpdateSettings(c *gin.Context) {
|
||||
|
||||
time.Sleep(100 * time.Millisecond) // 等待异步更新配置
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "Settings updated successfully. Configuration will be reloaded in the background across all instances.",
|
||||
})
|
||||
response.SuccessI18n(c, "settings.update_success", nil)
|
||||
}
|
||||
@@ -110,7 +110,7 @@ func normalizeLanguageCode(lang string) string {
|
||||
}
|
||||
|
||||
// T 翻译消息
|
||||
func T(localizer *i18n.Localizer, msgID string, data ...map[string]interface{}) string {
|
||||
func T(localizer *i18n.Localizer, msgID string, data ...map[string]any) string {
|
||||
config := &i18n.LocalizeConfig{
|
||||
MessageID: msgID,
|
||||
}
|
||||
|
||||
@@ -55,6 +55,10 @@ var MessagesEnUS = map[string]string{
|
||||
"validation.invalid_channel_type": "Invalid channel type. Supported types: {types}",
|
||||
"validation.test_model_empty": "Test model cannot be empty or contain only spaces",
|
||||
"validation.invalid_status_value": "Invalid status value",
|
||||
"validation.invalid_upstreams": "Invalid upstreams configuration: {error}",
|
||||
"validation.group_id_required": "group_id query parameter is required",
|
||||
"validation.invalid_group_id_format": "Invalid group_id format",
|
||||
"validation.keys_text_empty": "Keys text cannot be empty",
|
||||
|
||||
// Task related
|
||||
"task.validation_started": "Key validation task started",
|
||||
@@ -151,4 +155,26 @@ var MessagesEnUS = map[string]string{
|
||||
"error.upstream_weight_positive": "upstream weight must be a positive integer",
|
||||
"error.marshal_upstreams_failed": "failed to marshal cleaned upstreams",
|
||||
"error.unknown_config_field": "unknown config field: '{field}'",
|
||||
"error.invalid_config_format": "Invalid config format: {error}",
|
||||
"error.process_header_rules": "Failed to process header rules: {error}",
|
||||
"error.invalidate_group_cache": "failed to invalidate group cache",
|
||||
"error.unmarshal_header_rules": "Failed to unmarshal header rules",
|
||||
"error.delete_group_cache": "Failed to delete group: unable to clean up cache",
|
||||
"error.get_total_keys": "failed to get total keys: {error}",
|
||||
"error.get_active_keys": "failed to get active keys: {error}",
|
||||
"error.get_hourly_requests": "failed to get hourly total requests: {error}",
|
||||
"error.get_hourly_failures": "failed to get hourly failed requests: {error}",
|
||||
"error.get_daily_stats": "failed to get daily stats: {error}",
|
||||
"error.get_weekly_stats": "failed to get weekly stats: {error}",
|
||||
"error.decrypt_key_copy": "Failed to decrypt key during group copy, skipping",
|
||||
"error.start_import_task": "Failed to start async key import task for group copy",
|
||||
"error.export_logs": "Failed to export logs",
|
||||
|
||||
// Login related
|
||||
"auth.invalid_request": "Invalid request format",
|
||||
"auth.authentication_successful": "Authentication successful",
|
||||
"auth.authentication_failed": "Authentication failed",
|
||||
|
||||
// Settings success message
|
||||
"settings.update_success": "Settings updated successfully. Configuration will be reloaded in the background across all instances.",
|
||||
}
|
||||
@@ -55,6 +55,10 @@ var MessagesJaJP = map[string]string{
|
||||
"validation.invalid_channel_type": "無効なチャンネルタイプ。サポートされるタイプ: {types}",
|
||||
"validation.test_model_empty": "テストモデルは空またはスペースのみにできません",
|
||||
"validation.invalid_status_value": "無効なステータス値",
|
||||
"validation.invalid_upstreams": "無効なupstreams設定: {error}",
|
||||
"validation.group_id_required": "group_idクエリパラメータが必要です",
|
||||
"validation.invalid_group_id_format": "無効なgroup_id形式",
|
||||
"validation.keys_text_empty": "キーテキストは空にできません",
|
||||
|
||||
// タスク関連
|
||||
"task.validation_started": "キー検証タスクが開始されました",
|
||||
@@ -151,4 +155,26 @@ var MessagesJaJP = map[string]string{
|
||||
"error.upstream_weight_positive": "upstreamの重みは正の整数である必要があります",
|
||||
"error.marshal_upstreams_failed": "クリーンアップされたupstreamsのシリアル化に失敗しました",
|
||||
"error.unknown_config_field": "未知の設定フィールド: '{field}'",
|
||||
"error.invalid_config_format": "無効な設定形式: {error}",
|
||||
"error.process_header_rules": "ヘッダールールの処理に失敗しました: {error}",
|
||||
"error.invalidate_group_cache": "グループキャッシュの無効化に失敗しました",
|
||||
"error.unmarshal_header_rules": "ヘッダールールのアンマーシャルに失敗しました",
|
||||
"error.delete_group_cache": "グループの削除に失敗: キャッシュをクリーンアップできません",
|
||||
"error.get_total_keys": "合計キー数の取得に失敗: {error}",
|
||||
"error.get_active_keys": "アクティブキー数の取得に失敗: {error}",
|
||||
"error.get_hourly_requests": "時間別リクエスト数の取得に失敗: {error}",
|
||||
"error.get_hourly_failures": "時間別失敗数の取得に失敗: {error}",
|
||||
"error.get_daily_stats": "日別統計の取得に失敗: {error}",
|
||||
"error.get_weekly_stats": "週別統計の取得に失敗: {error}",
|
||||
"error.decrypt_key_copy": "グループコピー中のキー復号化に失敗、スキップします",
|
||||
"error.start_import_task": "グループコピー用の非同期キーインポートタスクの開始に失敗しました",
|
||||
"error.export_logs": "ログのエクスポートに失敗しました",
|
||||
|
||||
// ログイン関連
|
||||
"auth.invalid_request": "無効なリクエスト形式",
|
||||
"auth.authentication_successful": "認証成功",
|
||||
"auth.authentication_failed": "認証失敗",
|
||||
|
||||
// 設定成功メッセージ
|
||||
"settings.update_success": "設定が正常に更新されました。設定はすべてのインスタンスでバックグラウンドで再読み込みされます。",
|
||||
}
|
||||
@@ -55,6 +55,10 @@ var MessagesZhCN = map[string]string{
|
||||
"validation.invalid_channel_type": "无效的通道类型。支持的类型有: {types}",
|
||||
"validation.test_model_empty": "测试模型不能为空或只有空格",
|
||||
"validation.invalid_status_value": "无效的状态值",
|
||||
"validation.invalid_upstreams": "upstreams配置错误: {error}",
|
||||
"validation.group_id_required": "需要提供group_id参数",
|
||||
"validation.invalid_group_id_format": "无效的group_id格式",
|
||||
"validation.keys_text_empty": "密钥文本不能为空",
|
||||
|
||||
// 任务相关
|
||||
"task.validation_started": "密钥验证任务已开始",
|
||||
@@ -151,4 +155,26 @@ var MessagesZhCN = map[string]string{
|
||||
"error.upstream_weight_positive": "upstream权重必须是正整数",
|
||||
"error.marshal_upstreams_failed": "序列化清理后的upstreams失败",
|
||||
"error.unknown_config_field": "未知的配置字段: '{field}'",
|
||||
"error.invalid_config_format": "无效的配置格式: {error}",
|
||||
"error.process_header_rules": "处理请求头规则失败: {error}",
|
||||
"error.invalidate_group_cache": "刷新分组缓存失败",
|
||||
"error.unmarshal_header_rules": "解析请求头规则失败",
|
||||
"error.delete_group_cache": "删除分组失败: 无法清理缓存",
|
||||
"error.get_total_keys": "获取密钥总数失败: {error}",
|
||||
"error.get_active_keys": "获取活跃密钥数失败: {error}",
|
||||
"error.get_hourly_requests": "获取每小时请求数失败: {error}",
|
||||
"error.get_hourly_failures": "获取每小时失败数失败: {error}",
|
||||
"error.get_daily_stats": "获取每日统计失败: {error}",
|
||||
"error.get_weekly_stats": "获取每周统计失败: {error}",
|
||||
"error.decrypt_key_copy": "解密密钥时失败,跳过该密钥",
|
||||
"error.start_import_task": "启动异步密钥导入任务失败",
|
||||
"error.export_logs": "导出日志失败",
|
||||
|
||||
// 登录相关
|
||||
"auth.invalid_request": "无效的请求格式",
|
||||
"auth.authentication_successful": "认证成功",
|
||||
"auth.authentication_failed": "认证失败",
|
||||
|
||||
// 设置成功消息
|
||||
"settings.update_success": "设置更新成功。配置将在后台在所有实例间重新加载。",
|
||||
}
|
||||
+13
-13
@@ -17,17 +17,17 @@ func Middleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 获取 Accept-Language 头
|
||||
acceptLang := c.GetHeader("Accept-Language")
|
||||
|
||||
|
||||
// 获取 Localizer
|
||||
localizer := GetLocalizer(acceptLang)
|
||||
|
||||
|
||||
// 将 Localizer 存储到 Context 中
|
||||
c.Set(LocalizerKey, localizer)
|
||||
|
||||
|
||||
// 存储当前语言
|
||||
lang := normalizeLanguageCode(acceptLang)
|
||||
c.Set(LangKey, lang)
|
||||
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -54,10 +54,10 @@ func GetLangFromContext(c *gin.Context) string {
|
||||
}
|
||||
|
||||
// Success 返回成功响应(带国际化消息)
|
||||
func Success(c *gin.Context, msgID string, data interface{}) {
|
||||
func Success(c *gin.Context, msgID string, data any) {
|
||||
localizer := GetLocalizerFromContext(c)
|
||||
message := T(localizer, msgID)
|
||||
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"success": true,
|
||||
"message": message,
|
||||
@@ -67,10 +67,10 @@ func Success(c *gin.Context, msgID string, data interface{}) {
|
||||
}
|
||||
|
||||
// SuccessWithData 返回成功响应(带模板数据)
|
||||
func SuccessWithData(c *gin.Context, msgID string, templateData map[string]interface{}, data interface{}) {
|
||||
func SuccessWithData(c *gin.Context, msgID string, templateData map[string]any, data any) {
|
||||
localizer := GetLocalizerFromContext(c)
|
||||
message := T(localizer, msgID, templateData)
|
||||
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"success": true,
|
||||
"message": message,
|
||||
@@ -83,7 +83,7 @@ func SuccessWithData(c *gin.Context, msgID string, templateData map[string]inter
|
||||
func Error(c *gin.Context, code int, msgID string) {
|
||||
localizer := GetLocalizerFromContext(c)
|
||||
message := T(localizer, msgID)
|
||||
|
||||
|
||||
c.JSON(code, gin.H{
|
||||
"success": false,
|
||||
"message": message,
|
||||
@@ -92,10 +92,10 @@ func Error(c *gin.Context, code int, msgID string) {
|
||||
}
|
||||
|
||||
// ErrorWithData 返回错误响应(带模板数据)
|
||||
func ErrorWithData(c *gin.Context, code int, msgID string, templateData map[string]interface{}) {
|
||||
func ErrorWithData(c *gin.Context, code int, msgID string, templateData map[string]any) {
|
||||
localizer := GetLocalizerFromContext(c)
|
||||
message := T(localizer, msgID, templateData)
|
||||
|
||||
|
||||
c.JSON(code, gin.H{
|
||||
"success": false,
|
||||
"message": message,
|
||||
@@ -104,7 +104,7 @@ func ErrorWithData(c *gin.Context, code int, msgID string, templateData map[stri
|
||||
}
|
||||
|
||||
// Message 获取国际化消息
|
||||
func Message(c *gin.Context, msgID string, templateData ...map[string]interface{}) string {
|
||||
func Message(c *gin.Context, msgID string, templateData ...map[string]any) string {
|
||||
localizer := GetLocalizerFromContext(c)
|
||||
return T(localizer, msgID, templateData...)
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func Error(c *gin.Context, apiErr *app_errors.APIError) {
|
||||
}
|
||||
|
||||
// SuccessI18n sends a standardized success response with i18n message.
|
||||
func SuccessI18n(c *gin.Context, msgID string, data any, templateData ...map[string]interface{}) {
|
||||
func SuccessI18n(c *gin.Context, msgID string, data any, templateData ...map[string]any) {
|
||||
message := i18n.Message(c, msgID, templateData...)
|
||||
c.JSON(http.StatusOK, SuccessResponse{
|
||||
Code: 0,
|
||||
@@ -50,7 +50,7 @@ func SuccessI18n(c *gin.Context, msgID string, data any, templateData ...map[str
|
||||
}
|
||||
|
||||
// ErrorI18n sends a standardized error response with i18n message.
|
||||
func ErrorI18n(c *gin.Context, httpStatus int, code string, msgID string, templateData ...map[string]interface{}) {
|
||||
func ErrorI18n(c *gin.Context, httpStatus int, code string, msgID string, templateData ...map[string]any) {
|
||||
message := i18n.Message(c, msgID, templateData...)
|
||||
c.JSON(httpStatus, ErrorResponse{
|
||||
Code: code,
|
||||
@@ -59,7 +59,7 @@ func ErrorI18n(c *gin.Context, httpStatus int, code string, msgID string, templa
|
||||
}
|
||||
|
||||
// ErrorI18nFromAPIError sends a standardized error response using an APIError with i18n message.
|
||||
func ErrorI18nFromAPIError(c *gin.Context, apiErr *app_errors.APIError, msgID string, templateData ...map[string]interface{}) {
|
||||
func ErrorI18nFromAPIError(c *gin.Context, apiErr *app_errors.APIError, msgID string, templateData ...map[string]any) {
|
||||
message := i18n.Message(c, msgID, templateData...)
|
||||
c.JSON(apiErr.HTTPStatus, ErrorResponse{
|
||||
Code: apiErr.Code,
|
||||
|
||||
Reference in new issue
Block a user