增加多语言支持
This commit is contained in:
@@ -5,11 +5,13 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.toowon.com/jimmy/go-common/config"
|
||||
"git.toowon.com/jimmy/go-common/email"
|
||||
commonhttp "git.toowon.com/jimmy/go-common/http"
|
||||
"git.toowon.com/jimmy/go-common/i18n"
|
||||
"git.toowon.com/jimmy/go-common/logger"
|
||||
"git.toowon.com/jimmy/go-common/middleware"
|
||||
"git.toowon.com/jimmy/go-common/migration"
|
||||
@@ -118,6 +120,7 @@ type Factory struct {
|
||||
sms *sms.SMS // 短信客户端(延迟初始化)
|
||||
db *gorm.DB // 数据库连接(延迟初始化)
|
||||
redis *redis.Client // Redis客户端(延迟初始化)
|
||||
i18n *i18n.I18n // 国际化工具(延迟初始化)
|
||||
}
|
||||
|
||||
// NewFactory 创建工厂实例
|
||||
@@ -774,7 +777,10 @@ func (f *Factory) GetMiddlewareChain() *middleware.Chain {
|
||||
middlewares = append(middlewares, middleware.CORS(corsConfig))
|
||||
}
|
||||
|
||||
// 5. Timezone 中间件(必需,处理时区)
|
||||
// 5. Language 中间件(必需,处理语言)
|
||||
middlewares = append(middlewares, middleware.Language)
|
||||
|
||||
// 6. Timezone 中间件(必需,处理时区)
|
||||
middlewares = append(middlewares, middleware.Timezone)
|
||||
|
||||
return middleware.NewChain(middlewares...)
|
||||
@@ -911,10 +917,69 @@ func (f *Factory) SuccessPage(w http.ResponseWriter, list interface{}, total int
|
||||
|
||||
// Error 错误响应(黑盒模式,推荐使用)
|
||||
// w: ResponseWriter
|
||||
// code: 业务错误码,非0表示业务错误
|
||||
// message: 错误消息
|
||||
func (f *Factory) Error(w http.ResponseWriter, code int, message string) {
|
||||
commonhttp.Error(w, code, message)
|
||||
// r: HTTP请求(用于获取语言信息和i18n处理)
|
||||
// code: 业务错误码(如果message是消息代码,此参数会被语言文件中的code覆盖)
|
||||
// message: 错误消息或消息代码(如果i18n已初始化且message是消息代码格式,会自动获取国际化消息和业务code)
|
||||
// args: 可选参数,用于格式化消息(类似 fmt.Sprintf,仅在message是消息代码时使用)
|
||||
//
|
||||
// 使用逻辑:
|
||||
// 1. 如果i18n已初始化,且message看起来是消息代码(包含点号,如 "user.not_found"),
|
||||
// 则从请求context中获取语言,并尝试从语言文件中获取国际化消息和业务code
|
||||
// 2. 如果获取到国际化消息,使用语言文件中的code作为响应code,使用国际化消息作为响应message
|
||||
// 3. 如果未获取到或i18n未初始化,使用传入的code和message
|
||||
//
|
||||
// 示例:
|
||||
//
|
||||
// // 方式1:直接传入消息代码(推荐,自动国际化)
|
||||
// fac.Error(w, r, 0, "user.not_found")
|
||||
// // 如果请求语言是 zh-CN,且语言文件中 "user.not_found" 的 code 是 1001,
|
||||
// // 返回: {"code": 1001, "message": "用户不存在"}
|
||||
// // 如果请求语言是 en-US,返回: {"code": 1001, "message": "User not found"}
|
||||
//
|
||||
// // 方式2:带参数的消息代码
|
||||
// fac.Error(w, r, 0, "user.welcome", "Alice")
|
||||
// // 如果消息内容是 "欢迎,%s",返回: {"code": 0, "message": "欢迎,Alice"}
|
||||
//
|
||||
// // 方式3:直接传入消息文本(不使用国际化)
|
||||
// fac.Error(w, r, 500, "系统错误")
|
||||
// // 返回: {"code": 500, "message": "系统错误"}
|
||||
func (f *Factory) Error(w http.ResponseWriter, r *http.Request, code int, message string, args ...interface{}) {
|
||||
// 判断message是否是消息代码(简单判断:包含点号)
|
||||
isMessageCode := strings.Contains(message, ".")
|
||||
|
||||
var finalCode int
|
||||
var finalMessage string
|
||||
|
||||
if isMessageCode {
|
||||
// 尝试从i18n获取国际化消息和业务code
|
||||
if i, err := f.getI18n(); err == nil {
|
||||
// i18n已初始化,获取语言并查找消息
|
||||
lang := f.GetLanguage(r)
|
||||
if lang == "" {
|
||||
lang = i.GetDefaultLang()
|
||||
}
|
||||
msgInfo := i.GetMessageInfo(lang, message, args...)
|
||||
// 如果获取到了国际化消息(不是返回code本身),使用国际化消息和业务code
|
||||
if msgInfo.Message != message {
|
||||
finalCode = msgInfo.Code
|
||||
finalMessage = msgInfo.Message
|
||||
} else {
|
||||
// 消息代码不存在,使用传入的code和消息代码作为消息
|
||||
finalCode = code
|
||||
finalMessage = message
|
||||
}
|
||||
} else {
|
||||
// i18n未初始化,使用传入的code和消息代码作为消息
|
||||
finalCode = code
|
||||
finalMessage = message
|
||||
}
|
||||
} else {
|
||||
// 不是消息代码格式,使用传入的code和消息
|
||||
finalCode = code
|
||||
finalMessage = message
|
||||
}
|
||||
|
||||
commonhttp.Error(w, finalCode, finalMessage)
|
||||
}
|
||||
|
||||
// SystemError 系统错误响应(返回HTTP 500)(黑盒模式,推荐使用)
|
||||
@@ -1010,6 +1075,14 @@ func (f *Factory) GetTimezone(r *http.Request) string {
|
||||
return commonhttp.GetTimezone(r)
|
||||
}
|
||||
|
||||
// GetLanguage 从请求的context中获取语言(黑盒模式,推荐使用)
|
||||
// r: HTTP请求
|
||||
// 如果使用了middleware.Language中间件,可以从context中获取语言信息
|
||||
// 如果未设置,返回默认语言 zh-CN
|
||||
func (f *Factory) GetLanguage(r *http.Request) string {
|
||||
return commonhttp.GetLanguage(r)
|
||||
}
|
||||
|
||||
// ========== Tools工具方法(黑盒模式,推荐使用) ==========
|
||||
//
|
||||
// 这些方法直接调用 tools 包的公共方法,保持低耦合。
|
||||
@@ -1355,3 +1428,128 @@ func (f *Factory) GenerateRefundNo() string {
|
||||
func (f *Factory) GenerateTransferNo() string {
|
||||
return tools.GenerateTransferNo()
|
||||
}
|
||||
|
||||
// ========== I18n 国际化工具(黑盒模式,推荐使用) ==========
|
||||
//
|
||||
// 这些方法提供多语言内容管理功能,支持从文件加载语言内容,通过语言代码和消息代码获取对应语言的内容。
|
||||
|
||||
// getI18n 获取国际化工具实例(内部方法,延迟初始化)
|
||||
func (f *Factory) getI18n() (*i18n.I18n, error) {
|
||||
if f.i18n != nil {
|
||||
return f.i18n, nil
|
||||
}
|
||||
|
||||
// 如果没有配置,返回错误
|
||||
return nil, fmt.Errorf("i18n not initialized, please call InitI18n first")
|
||||
}
|
||||
|
||||
// InitI18n 初始化国际化工具(黑盒模式,推荐使用)
|
||||
// defaultLang: 默认语言代码(如 "zh-CN", "en-US")
|
||||
// 初始化后可以调用 LoadI18nFromDir 或 LoadI18nFromFile 加载语言文件
|
||||
//
|
||||
// 示例:
|
||||
//
|
||||
// fac, _ := factory.NewFactoryFromFile("config.json")
|
||||
// fac.InitI18n("zh-CN")
|
||||
// fac.LoadI18nFromDir("locales")
|
||||
func (f *Factory) InitI18n(defaultLang string) {
|
||||
f.i18n = i18n.NewI18n(defaultLang)
|
||||
}
|
||||
|
||||
// LoadI18nFromDir 从目录加载多个语言文件(黑盒模式,推荐使用)
|
||||
// dirPath: 语言文件目录路径
|
||||
// 文件命名规则:{语言代码}.json(如 zh-CN.json, en-US.json)
|
||||
//
|
||||
// 文件格式示例(zh-CN.json):
|
||||
//
|
||||
// {
|
||||
// "user.not_found": "用户不存在",
|
||||
// "user.login_success": "登录成功",
|
||||
// "user.welcome": "欢迎,%s"
|
||||
// }
|
||||
//
|
||||
// 示例:
|
||||
//
|
||||
// fac.InitI18n("zh-CN")
|
||||
// fac.LoadI18nFromDir("locales")
|
||||
func (f *Factory) LoadI18nFromDir(dirPath string) error {
|
||||
i, err := f.getI18n()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return i.LoadFromDir(dirPath)
|
||||
}
|
||||
|
||||
// LoadI18nFromFile 从单个语言文件加载内容(黑盒模式,推荐使用)
|
||||
// filePath: 语言文件路径(JSON格式)
|
||||
// lang: 语言代码(如 "zh-CN", "en-US")
|
||||
//
|
||||
// 示例:
|
||||
//
|
||||
// fac.InitI18n("zh-CN")
|
||||
// fac.LoadI18nFromFile("locales/zh-CN.json", "zh-CN")
|
||||
// fac.LoadI18nFromFile("locales/en-US.json", "en-US")
|
||||
func (f *Factory) LoadI18nFromFile(filePath, lang string) error {
|
||||
i, err := f.getI18n()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return i.LoadFromFile(filePath, lang)
|
||||
}
|
||||
|
||||
// GetMessage 获取指定语言和代码的消息内容(黑盒模式,推荐使用)
|
||||
// lang: 语言代码(如 "zh-CN", "en-US")
|
||||
// code: 消息代码(如 "user.not_found")
|
||||
// args: 可选参数,用于格式化消息(类似 fmt.Sprintf)
|
||||
//
|
||||
// 返回逻辑:
|
||||
// 1. 如果指定语言存在该code,返回对应内容
|
||||
// 2. 如果指定语言不存在,尝试使用默认语言
|
||||
// 3. 如果默认语言也不存在,返回code本身(作为fallback)
|
||||
//
|
||||
// 示例:
|
||||
//
|
||||
// // 简单消息
|
||||
// msg := fac.GetMessage("zh-CN", "user.not_found")
|
||||
// // 返回: "用户不存在"
|
||||
//
|
||||
// // 带参数的消息
|
||||
// msg := fac.GetMessage("zh-CN", "user.welcome", "Alice")
|
||||
// // 如果消息内容是 "欢迎,%s",返回: "欢迎,Alice"
|
||||
func (f *Factory) GetMessage(lang, code string, args ...interface{}) string {
|
||||
i, err := f.getI18n()
|
||||
if err != nil {
|
||||
// 如果未初始化,返回code本身
|
||||
return code
|
||||
}
|
||||
return i.GetMessage(lang, code, args...)
|
||||
}
|
||||
|
||||
// GetI18n 获取国际化工具对象(高级功能时使用)
|
||||
// 返回已初始化的国际化工具对象
|
||||
//
|
||||
// ℹ️ 推荐使用黑盒方法:
|
||||
// - GetMessage():获取消息内容
|
||||
// - LoadI18nFromDir():加载语言文件目录
|
||||
// - LoadI18nFromFile():加载单个语言文件
|
||||
//
|
||||
// 仅在需要使用高级功能时获取对象:
|
||||
// - HasLang():检查语言是否存在
|
||||
// - GetSupportedLangs():获取所有支持的语言
|
||||
// - ReloadFromFile():重新加载语言文件
|
||||
// - SetDefaultLang():动态设置默认语言
|
||||
//
|
||||
// 示例(常用操作,推荐):
|
||||
//
|
||||
// fac.InitI18n("zh-CN")
|
||||
// fac.LoadI18nFromDir("locales")
|
||||
// msg := fac.GetMessage("zh-CN", "user.not_found")
|
||||
//
|
||||
// 示例(高级功能):
|
||||
//
|
||||
// i18n, _ := fac.GetI18n()
|
||||
// langs := i18n.GetSupportedLangs()
|
||||
// hasLang := i18n.HasLang("en-US")
|
||||
func (f *Factory) GetI18n() (*i18n.I18n, error) {
|
||||
return f.getI18n()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user