package middleware import ( "encoding/json" "fmt" "io/ioutil" "net/http" "os" "path" "path/filepath" "strings" "gitee.com/xuyiping_admin/pkg/logger/zaplog" "gitee.com/xuyiping_admin/pkg/xerr" "github.com/gin-gonic/gin" "github.com/nicksnyder/go-i18n/v2/i18n" "go.uber.org/zap" "golang.org/x/text/language" ) var ( localesDir = "./locales" bundle *i18n.Bundle defaultLanguage = language.Chinese supported = map[string]bool{ // 支持的语言列表 "zh": true, // 中文 "en": true, // 英文 // 添加其他支持的语言... } ) // I18N 初始化国际化中间件 func I18N() gin.HandlerFunc { bundle = i18n.NewBundle(defaultLanguage) bundle.RegisterUnmarshalFunc("json", json.Unmarshal) // 加载翻译文件 if err := loadTranslations(); err != nil { zaplog.Error("Failed to load translations", zap.Error(err)) } return func(c *gin.Context) { lang := detectLanguage(c.Request) localize := i18n.NewLocalizer(bundle, lang) c.Set(LanguageContent, localize) c.Set(Language, lang) c.Next() } } // loadTranslations 加载所有翻译文件 func loadTranslations() error { absPath, err := filepath.Abs(localesDir) if err != nil { return xerr.WithStack(err) } files, err := ioutil.ReadDir(absPath) if err != nil { return xerr.WithStack(fmt.Errorf("failed to read files dir: %w", err)) } if len(files) == 0 { return xerr.Custom("no found in files directory") } loaded := false for _, entry := range files { if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".json") { continue } filePath := path.Join(absPath, entry.Name()) data, err := os.ReadFile(filePath) if err != nil { zaplog.Error("Failed to read translation file", zap.String("file", filePath), zap.Error(err)) continue } messageFile, err := bundle.ParseMessageFileBytes(data, filePath) if err != nil { zaplog.Error("Failed to parse translation file", zap.String("file", filePath), zap.Error(err)) continue } zaplog.Info("Loaded translation", zap.String("language", messageFile.Tag.String()), zap.Any("messageFile", messageFile)) loaded = true } if !loaded { return xerr.Custom("no valid translation files were loaded") } return nil } // detectLanguage 检测客户端语言偏好 func detectLanguage(r *http.Request) string { // 1. 检查查询参数 if lang := r.URL.Query().Get("language"); lang != "" { return normalizeLanguage(lang) } // 2. 检查 Cookie if langCookie, err := r.Cookie("language"); err == nil && langCookie.Value != "" { return normalizeLanguage(langCookie.Value) } // 3. 检查 Accept-Language 头 if acceptLang := r.Header.Get("Accept-Language"); acceptLang != "" { if langs := strings.Split(acceptLang, ","); len(langs) > 0 { return normalizeLanguage(langs[0]) } } // 默认返回中文 return defaultLanguage.String() } // normalizeLanguage 规范化语言代码 func normalizeLanguage(lang string) string { // 去除权重值 (如 en;q=0.9 -> en) if parts := strings.Split(lang, ";"); len(parts) > 0 { lang = parts[0] } // 转换为小写并去除地区代码 (en-US -> en) lang = strings.ToLower(strings.Split(lang, "-")[0]) if supported[lang] { return lang } return defaultLanguage.String() }