i18n: Translate all Chinese to English across the codebase

Translate Chinese comments, log/error messages, validator labels and
Swagger annotations to English throughout the source code, generated
Swagger docs, config files and CI workflows.

Make the English README primary: README.md now holds the English docs
and README_EN.md holds the Chinese version, with cross-language links
updated accordingly.

Note: the generated docs/ swagger files were translated in place; run
`go generate ./...` (swag init) to regenerate them from the now-English
annotations when a Go toolchain is available.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-27 12:17:04 +02:00
co-authored by Claude Opus 4.8
parent c5687e1506
commit 16e97c8efa
103 changed files with 3088 additions and 3086 deletions
+33 -33
View File
@@ -6,23 +6,23 @@ import (
"time"
)
// 安全策略配置
// security policy configuration
type SecurityPolicy struct {
CaptchaThreshold int // 尝试失败次数达到验证码阈值,小于0表示不启用, 0表示强制启用
BanThreshold int // 尝试失败次数达到封禁阈值,为0表示不启用
CaptchaThreshold int // number of failed attempts at which the captcha is required; less than 0 means disabled, 0 means always enabled
BanThreshold int // number of failed attempts at which a ban is triggered; 0 means disabled
AttemptsWindow time.Duration
BanDuration time.Duration
}
// 验证码提供者接口
// captcha provider interface
type CaptchaProvider interface {
Generate() (id string, content string, answer string, err error)
//Validate(ip, code string) bool
Expiration() time.Duration // 验证码过期时间, 应该小于 AttemptsWindow
Draw(content string) (string, error) // 绘制验证码
Expiration() time.Duration // captcha expiration time, should be less than AttemptsWindow
Draw(content string) (string, error) // draw the captcha
}
// 验证码元数据
// captcha metadata
type CaptchaMeta struct {
Id string
Content string
@@ -30,13 +30,13 @@ type CaptchaMeta struct {
ExpiresAt time.Time
}
// IP封禁记录
// IP ban record
type BanRecord struct {
ExpiresAt time.Time
Reason string
}
// 登录限制器
// login limiter
type LoginLimiter struct {
mu sync.Mutex
policy SecurityPolicy
@@ -55,7 +55,7 @@ var defaultSecurityPolicy = SecurityPolicy{
}
func NewLoginLimiter(policy SecurityPolicy) *LoginLimiter {
// 设置默认值
// set default values
if policy.AttemptsWindow == 0 {
policy.AttemptsWindow = 5 * time.Minute
}
@@ -74,19 +74,19 @@ func NewLoginLimiter(policy SecurityPolicy) *LoginLimiter {
return ll
}
// 注册验证码提供者
// register a captcha provider
func (ll *LoginLimiter) RegisterProvider(p CaptchaProvider) {
ll.mu.Lock()
defer ll.mu.Unlock()
ll.provider = p
}
// isDisabled 检查是否禁用登录限制
// isDisabled checks whether the login limiter is disabled
func (ll *LoginLimiter) isDisabled() bool {
return ll.policy.CaptchaThreshold < 0 && ll.policy.BanThreshold == 0
}
// 记录登录失败尝试
// record a failed login attempt
func (ll *LoginLimiter) RecordFailedAttempt(ip string) {
if ll.isDisabled() {
return
@@ -101,14 +101,14 @@ func (ll *LoginLimiter) RecordFailedAttempt(ip string) {
now := time.Now()
windowStart := now.Add(-ll.policy.AttemptsWindow)
// 清理过期尝试
// clean up expired attempts
validAttempts := ll.pruneAttempts(ip, windowStart)
// 记录新尝试
// record a new attempt
validAttempts = append(validAttempts, now)
ll.attempts[ip] = validAttempts
// 检查封禁条件
// check ban conditions
if ll.policy.BanThreshold > 0 && len(validAttempts) >= ll.policy.BanThreshold {
ll.banIP(ip, "excessive failed attempts")
return
@@ -117,7 +117,7 @@ func (ll *LoginLimiter) RecordFailedAttempt(ip string) {
return
}
// 生成验证码
// generate a captcha
func (ll *LoginLimiter) RequireCaptcha() (error, CaptchaMeta) {
ll.mu.Lock()
defer ll.mu.Unlock()
@@ -131,7 +131,7 @@ func (ll *LoginLimiter) RequireCaptcha() (error, CaptchaMeta) {
return err, CaptchaMeta{}
}
// 存储验证码
// store the captcha
ll.captchas[id] = CaptchaMeta{
Id: id,
Content: content,
@@ -142,29 +142,29 @@ func (ll *LoginLimiter) RequireCaptcha() (error, CaptchaMeta) {
return nil, ll.captchas[id]
}
// 验证验证码
// verify the captcha
func (ll *LoginLimiter) VerifyCaptcha(id, answer string) bool {
ll.mu.Lock()
defer ll.mu.Unlock()
// 查找匹配验证码
// find a matching captcha
if ll.provider == nil {
return false
}
// 获取并验证验证码
// get and verify the captcha
captcha, exists := ll.captchas[id]
if !exists {
return false
}
// 清理过期验证码
// clean up expired captchas
if time.Now().After(captcha.ExpiresAt) {
delete(ll.captchas, id)
return false
}
// 验证并清理状态
// verify and clean up state
if answer == captcha.Answer {
delete(ll.captchas, id)
return true
@@ -178,7 +178,7 @@ func (ll *LoginLimiter) DrawCaptcha(content string) (err error, str string) {
return
}
// 清除记录窗口
// clear the record window
func (ll *LoginLimiter) RemoveAttempts(ip string) {
ll.mu.Lock()
defer ll.mu.Unlock()
@@ -189,7 +189,7 @@ func (ll *LoginLimiter) RemoveAttempts(ip string) {
}
}
// CheckSecurityStatus 检查安全状态
// CheckSecurityStatus checks the security status
func (ll *LoginLimiter) CheckSecurityStatus(ip string) (banned bool, captchaRequired bool) {
if ll.isDisabled() {
return
@@ -197,21 +197,21 @@ func (ll *LoginLimiter) CheckSecurityStatus(ip string) (banned bool, captchaRequ
ll.mu.Lock()
defer ll.mu.Unlock()
// 检查封禁状态
// check ban status
if banned, _ = ll.isBanned(ip); banned {
return
}
// 清理过期数据
// clean up expired data
ll.pruneAttempts(ip, time.Now().Add(-ll.policy.AttemptsWindow))
// 检查验证码要求
// check captcha requirement
captchaRequired = len(ll.attempts[ip]) >= ll.policy.CaptchaThreshold
return
}
// 后台清理任务
// background cleanup task
func (ll *LoginLimiter) cleanupRoutine() {
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
@@ -226,7 +226,7 @@ func (ll *LoginLimiter) cleanupRoutine() {
}
}
// 内部工具方法
// internal utility methods
func (ll *LoginLimiter) isBanned(ip string) (bool, BanRecord) {
record, exists := ll.bannedIPs[ip]
if !exists {
@@ -277,19 +277,19 @@ func (ll *LoginLimiter) cleanupExpired() {
now := time.Now()
// 清理封禁记录
// clean up ban records
for ip, record := range ll.bannedIPs {
if now.After(record.ExpiresAt) {
delete(ll.bannedIPs, ip)
}
}
// 清理尝试记录
// clean up attempt records
for ip := range ll.attempts {
ll.pruneAttempts(ip, now.Add(-ll.policy.AttemptsWindow))
}
// 清理验证码
// clean up captchas
for id := range ll.captchas {
ll.pruneCaptchas(id)
}
+62 -62
View File
@@ -33,7 +33,7 @@ func TestSecurityWorkflow(t *testing.T) {
limiter := NewLoginLimiter(policy)
ip := "192.168.1.100"
// 测试正常失败记录
// test normal failure recording
for i := 0; i < 3; i++ {
limiter.RecordFailedAttempt(ip)
}
@@ -45,14 +45,14 @@ func TestSecurityWorkflow(t *testing.T) {
if !capRequired {
t.Error("Captcha should be required")
}
// 测试触发封禁
// test triggering a ban
for i := 0; i < 3; i++ {
limiter.RecordFailedAttempt(ip)
isBanned, capRequired = limiter.CheckSecurityStatus(ip)
fmt.Printf("IP: %s, Banned: %v, Captcha Required: %v\n", ip, isBanned, capRequired)
}
// 测试封禁状态
// test ban status
if isBanned, _ = limiter.CheckSecurityStatus(ip); !isBanned {
t.Error("IP should be banned")
}
@@ -64,36 +64,36 @@ func TestCaptchaFlow(t *testing.T) {
limiter.RegisterProvider(&MockCaptchaProvider{})
ip := "10.0.0.1"
// 触发验证码要求
// trigger captcha requirement
limiter.RecordFailedAttempt(ip)
limiter.RecordFailedAttempt(ip)
// 检查状态
// check status
if _, need := limiter.CheckSecurityStatus(ip); !need {
t.Error("应该需要验证码")
t.Error("captcha should be required")
}
// 生成验证码
// generate captcha
err, capc := limiter.RequireCaptcha()
if err != nil {
t.Fatalf("生成验证码失败: %v", err)
t.Fatalf("failed to generate captcha: %v", err)
}
fmt.Printf("验证码内容: %#v\n", capc)
fmt.Printf("captcha content: %#v\n", capc)
// 验证成功
// verify successfully
if !limiter.VerifyCaptcha(capc.Id, capc.Answer) {
t.Error("验证码应该验证成功")
t.Error("captcha should verify successfully")
}
// 验证已删除
// verify it has been deleted
if limiter.VerifyCaptcha(capc.Id, capc.Answer) {
t.Error("验证码应该已删除")
t.Error("captcha should have been deleted")
}
limiter.RemoveAttempts(ip)
// 验证后状态
// state after verification
if banned, need := limiter.CheckSecurityStatus(ip); banned || need {
t.Error("验证成功后应该重置状态")
t.Error("state should be reset after successful verification")
}
}
@@ -103,26 +103,26 @@ func TestCaptchaMustFlow(t *testing.T) {
limiter.RegisterProvider(&MockCaptchaProvider{})
ip := "10.0.0.1"
// 检查状态
// check status
if _, need := limiter.CheckSecurityStatus(ip); !need {
t.Error("应该需要验证码")
t.Error("captcha should be required")
}
// 生成验证码
// generate captcha
err, capc := limiter.RequireCaptcha()
if err != nil {
t.Fatalf("生成验证码失败: %v", err)
t.Fatalf("failed to generate captcha: %v", err)
}
fmt.Printf("验证码内容: %#v\n", capc)
fmt.Printf("captcha content: %#v\n", capc)
// 验证成功
// verify successfully
if !limiter.VerifyCaptcha(capc.Id, capc.Answer) {
t.Error("验证码应该验证成功")
t.Error("captcha should verify successfully")
}
// 验证后状态
// state after verification
if _, need := limiter.CheckSecurityStatus(ip); !need {
t.Error("应该需要验证码")
t.Error("captcha should be required")
}
}
func TestAttemptTimeout(t *testing.T) {
@@ -131,28 +131,28 @@ func TestAttemptTimeout(t *testing.T) {
limiter.RegisterProvider(&MockCaptchaProvider{})
ip := "10.0.0.1"
// 触发验证码要求
// trigger captcha requirement
limiter.RecordFailedAttempt(ip)
limiter.RecordFailedAttempt(ip)
// 检查状态
// check status
if _, need := limiter.CheckSecurityStatus(ip); !need {
t.Error("应该需要验证码")
t.Error("captcha should be required")
}
// 生成验证码
// generate captcha
err, _ := limiter.RequireCaptcha()
if err != nil {
t.Fatalf("生成验证码失败: %v", err)
t.Fatalf("failed to generate captcha: %v", err)
}
// 等待超过 AttemptsWindow
// wait beyond AttemptsWindow
time.Sleep(2 * time.Second)
// 触发验证码要求
// trigger captcha requirement
limiter.RecordFailedAttempt(ip)
// 检查状态
// check status
if _, need := limiter.CheckSecurityStatus(ip); need {
t.Error("不应该需要验证码")
t.Error("captcha should not be required")
}
}
@@ -162,27 +162,27 @@ func TestCaptchaTimeout(t *testing.T) {
limiter.RegisterProvider(&MockCaptchaProvider{})
ip := "10.0.0.1"
// 触发验证码要求
// trigger captcha requirement
limiter.RecordFailedAttempt(ip)
limiter.RecordFailedAttempt(ip)
// 检查状态
// check status
if _, need := limiter.CheckSecurityStatus(ip); !need {
t.Error("应该需要验证码")
t.Error("captcha should be required")
}
// 生成验证码
// generate captcha
err, capc := limiter.RequireCaptcha()
if err != nil {
t.Fatalf("生成验证码失败: %v", err)
t.Fatalf("failed to generate captcha: %v", err)
}
// 等待超过 CaptchaValidPeriod
// wait beyond CaptchaValidPeriod
time.Sleep(3 * time.Second)
// 验证成功
// verify successfully
if limiter.VerifyCaptcha(capc.Id, capc.Answer) {
t.Error("验证码应该已过期")
t.Error("captcha should have expired")
}
}
@@ -191,12 +191,12 @@ func TestBanFlow(t *testing.T) {
policy := SecurityPolicy{BanThreshold: 5}
limiter := NewLoginLimiter(policy)
ip := "10.0.0.1"
// 触发ban
// trigger ban
for i := 0; i < 5; i++ {
limiter.RecordFailedAttempt(ip)
}
// 检查状态
// check status
if banned, _ := limiter.CheckSecurityStatus(ip); !banned {
t.Error("should be banned")
}
@@ -205,12 +205,12 @@ func TestBanDisableFlow(t *testing.T) {
policy := SecurityPolicy{BanThreshold: 0}
limiter := NewLoginLimiter(policy)
ip := "10.0.0.1"
// 触发ban
// trigger ban
for i := 0; i < 5; i++ {
limiter.RecordFailedAttempt(ip)
}
// 检查状态
// check status
if banned, _ := limiter.CheckSecurityStatus(ip); banned {
t.Error("should not be banned")
}
@@ -219,15 +219,15 @@ func TestBanTimeout(t *testing.T) {
policy := SecurityPolicy{BanThreshold: 5, BanDuration: 1 * time.Second}
limiter := NewLoginLimiter(policy)
ip := "10.0.0.1"
// 触发ban
// 触发ban
// trigger ban
// trigger ban
for i := 0; i < 5; i++ {
limiter.RecordFailedAttempt(ip)
}
time.Sleep(2 * time.Second)
// 检查状态
// check status
if banned, _ := limiter.CheckSecurityStatus(ip); banned {
t.Error("should not be banned")
}
@@ -237,12 +237,12 @@ func TestLimiterDisabled(t *testing.T) {
policy := SecurityPolicy{BanThreshold: 0, CaptchaThreshold: -1}
limiter := NewLoginLimiter(policy)
ip := "10.0.0.1"
// 触发ban
// trigger ban
for i := 0; i < 5; i++ {
limiter.RecordFailedAttempt(ip)
}
// 检查状态
// check status
if banned, capNeed := limiter.CheckSecurityStatus(ip); banned || capNeed {
fmt.Printf("IP: %s, Banned: %v, Captcha Required: %v\n", ip, banned, capNeed)
t.Error("should not be banned or need captcha")
@@ -254,37 +254,37 @@ func TestB64CaptchaFlow(t *testing.T) {
limiter.RegisterProvider(B64StringCaptchaProvider{})
ip := "10.0.0.1"
// 触发验证码要求
// trigger captcha requirement
limiter.RecordFailedAttempt(ip)
limiter.RecordFailedAttempt(ip)
limiter.RecordFailedAttempt(ip)
// 检查状态
// check status
if _, need := limiter.CheckSecurityStatus(ip); !need {
t.Error("应该需要验证码")
t.Error("captcha should be required")
}
// 生成验证码
// generate captcha
err, capc := limiter.RequireCaptcha()
if err != nil {
t.Fatalf("生成验证码失败: %v", err)
t.Fatalf("failed to generate captcha: %v", err)
}
fmt.Printf("验证码内容: %#v\n", capc)
fmt.Printf("captcha content: %#v\n", capc)
//draw
err, b64 := limiter.DrawCaptcha(capc.Content)
if err != nil {
t.Fatalf("绘制验证码失败: %v", err)
t.Fatalf("failed to draw captcha: %v", err)
}
fmt.Printf("验证码内容: %#v\n", b64)
fmt.Printf("captcha content: %#v\n", b64)
// 验证成功
// verify successfully
if !limiter.VerifyCaptcha(capc.Id, capc.Answer) {
t.Error("验证码应该验证成功")
t.Error("captcha should verify successfully")
}
limiter.RemoveAttempts(ip)
// 验证后状态
// state after verification
if banned, need := limiter.CheckSecurityStatus(ip); banned || need {
t.Error("验证成功后应该重置状态")
t.Error("state should be reset after successful verification")
}
}
+4 -4
View File
@@ -23,7 +23,7 @@ func CopyStructByJson(src, dst interface{}) {
}
}
// CopyStructToMap 结构体转map
// CopyStructToMap converts a struct to a map
func CopyStructToMap(src interface{}) map[string]interface{} {
var res = map[string]interface{}{}
str, _ := json.Marshal(src)
@@ -64,7 +64,7 @@ func SafeGo(f interface{}, params ...interface{}) {
}()
}
// RandomString 生成随机字符串
// RandomString generates a random string
func RandomString(n int) string {
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
length := len(letterBytes)
@@ -79,7 +79,7 @@ func RandomString(n int) string {
return string(b)
}
// Keys 泛型函数,K 是键类型,V 是值类型
// Keys generic function; K is the key type, V is the value type
func Keys[K comparable, V any](m map[K]V) []K {
keys := make([]K, 0, len(m))
for k := range m {
@@ -88,7 +88,7 @@ func Keys[K comparable, V any](m map[K]V) []K {
return keys
}
// Values 泛型函数,K 是键类型,V 是值类型
// Values generic function; K is the key type, V is the value type
func Values[K comparable, V any](m map[K]V) []V {
values := make([]V, 0, len(m))
for _, v := range m {