123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- 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()
- }
|