From 353b3f0f4ff96e40c35e51e3db250e446b843b4c Mon Sep 17 00:00:00 2001 From: 474420502 <474420502@qq.com> Date: Fri, 12 Apr 2024 17:17:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E9=83=A8=E5=88=86=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- go.mod | 1 + go.sum | 6 + readme.md | 3 +- server/app/internal/handlers/actions/auth.go | 145 +++-- .../internal/handlers/actions/types_gen.go | 95 ++- server/app/internal/handlers/types_gen.tpl | 39 +- server/app/main_test.go | 31 +- translator/translator.go | 58 ++ translator/translator_gen.go | 538 +++++++++++++++++ translator/translator_test.go | 165 ++++++ translator/zh_cn.toml | 555 ++++++++++++++++++ translator/zh_hk.toml | 0 utils/basic/lang.go | 27 + utils/basic/types.go | 23 +- 15 files changed, 1630 insertions(+), 60 deletions(-) create mode 100644 translator/translator.go create mode 100644 translator/translator_gen.go create mode 100644 translator/translator_test.go create mode 100644 translator/zh_cn.toml create mode 100644 translator/zh_hk.toml create mode 100644 utils/basic/lang.go diff --git a/.gitignore b/.gitignore index e919d7d..f7bcef7 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,6 @@ vendor/ go.work __debug_bin* -.vscode \ No newline at end of file +.vscode + +*.php \ No newline at end of file diff --git a/go.mod b/go.mod index 2726112..1c21e9d 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,7 @@ require ( github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nicksnyder/go-i18n/v2 v2.4.0 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect diff --git a/go.sum b/go.sum index d4816ab..33f9a43 100644 --- a/go.sum +++ b/go.sum @@ -59,8 +59,13 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/nicksnyder/go-i18n v1.10.3 h1:0U60fnLBNrLBVt8vb8Q67yKNs+gykbQuLsIkiesJL+w= +github.com/nicksnyder/go-i18n v1.10.3/go.mod h1:hvLG5HTlZ4UfSuVLSRuX7JRUomIaoKQM19hm6f+no7o= +github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM= +github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -104,6 +109,7 @@ google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/readme.md b/readme.md index 61d0918..ccbf64c 100644 --- a/readme.md +++ b/readme.md @@ -10,4 +10,5 @@ 6. 电话号码和邮件不是唯一, 帐号表数据可能混乱 7. 表绝大部分没建索引, 数量上去, 性能立马下降 8. 表的部分字段不对应, 如 parent_id 和 customer_id 是同一事物, 但是类型不一样. 可能会产生不可预料的错误 -9. 数据库里递归查询, 一个查询n个query请求.出问题数据库死锁,整个项目崩溃. 例如 getFullParents \ No newline at end of file +9. 数据库里递归查询, 一个查询n个query请求.出问题数据库死锁,整个项目崩溃. 例如 getFullParents +10. 部分翻译了, 部分不翻译, 导致国际语言经常会返回中文. 多语言只实现了小部分, 错误非常多. \ No newline at end of file diff --git a/server/app/internal/handlers/actions/auth.go b/server/app/internal/handlers/actions/auth.go index 56f0efa..9b1863e 100644 --- a/server/app/internal/handlers/actions/auth.go +++ b/server/app/internal/handlers/actions/auth.go @@ -4,8 +4,8 @@ import ( "strings" "time" - "github.com/gin-gonic/gin" "github.com/iapologizewhenimwrong/Vestmore_GO/model" + "github.com/iapologizewhenimwrong/Vestmore_GO/translator" "github.com/iapologizewhenimwrong/Vestmore_GO/utils/auth" "github.com/iapologizewhenimwrong/Vestmore_GO/utils/basic" "github.com/iapologizewhenimwrong/Vestmore_GO/utils/email" @@ -21,8 +21,8 @@ var CompanyKey = "vestmore-bjwl" // app_market: string; // lang: string; // token: string; -func BaseGetToken(ctx *gin.Context, param *BaseGetTokenParam) (resp *basic.Response) { - ctx.ShouldBind(param) +func BaseGetToken(ctx *ActionContext[BaseGetTokenParam]) (resp *basic.Response) { + // model.Models.KillaraCustomerModel.Find() log.Println() @@ -43,28 +43,28 @@ func BaseGetToken(ctx *gin.Context, param *BaseGetTokenParam) (resp *basic.Respo // app_market: uint64; // email: string; // timestamp: int64; -func AccountLoginWithTelephonePassword(ctx *gin.Context, param *AccountLoginWithTelephonePasswordParam) (resp *basic.Response) { +func AccountLoginWithTelephonePassword(ctx *ActionContext[AccountLoginWithTelephonePasswordParam]) (resp *basic.Response) { // ctx.ShouldBind() // model.Models.KillaraCustomerModel.Find() - if param.CountryCode == "" { + if ctx.Param.CountryCode == "" { resp.ErrorMsg(1, "country_code 参数缺失") return } - if param.Telephone == "" { + if ctx.Param.Telephone == "" { resp.ErrorMsg(1, "telephone 参数缺失") return } - if param.Password == "" { + if ctx.Param.Password == "" { resp.ErrorMsg(1, "password 参数缺失") return } - telephone := strings.TrimSpace(param.Telephone) - password := strings.TrimSpace(param.Password) - countryCode := strings.TrimSpace(param.CountryCode) + telephone := strings.TrimSpace(ctx.Param.Telephone) + password := strings.TrimSpace(ctx.Param.Password) + countryCode := strings.TrimSpace(ctx.Param.CountryCode) // 假设 modelCustomer 和 modelCustomerToken 是对应的服务接口 @@ -85,52 +85,50 @@ func AccountLoginWithTelephonePassword(ctx *gin.Context, param *AccountLoginWith } if customer == nil { - return resp.ErrorMsg(1, "账号未注册") + return resp.ErrorTrCode(ctx, translator.AccountNotRegistered) } if *customer.CountryCode != countryCode { - return resp.ErrorMsg(1, "电话号码国家不正确") + return resp.ErrorTrCode(ctx, translator.PhoneNumberCountryIncorrect) } if *customer.Status != 1 { - return resp.ErrorMsg(1, "账号已禁用,无法登录") + return resp.ErrorTrCode(ctx, translator.AccountDisabledNotLogin) } if !auth.CheckPassword(*customer.Password, *customer.Salt, *customer.RandomPassword, password) { - return resp.ErrorMsg(1, "账号或密码错误") + return resp.ErrorTrCode(ctx, translator.AccountOrPasswordLoginFailed) } customerID := *customer.CustomerId - err = model.Models.KillaraCustomerTokenModel.UpdateTokenCustomerID(param.Token, customerID) + err = model.Models.KillaraCustomerTokenModel.UpdateTokenCustomerID(ctx.Param.Token, customerID) if err != nil { return resp.ErrorMsg(1, err.Error()) } var deviceCode string - if param.Device != "" { - deviceCode = param.Device + if ctx.Param.Device != "" { + deviceCode = ctx.Param.Device } var version string - if param.Version != "" { - version = param.Version + if ctx.Param.Version != "" { + version = ctx.Param.Version } var ip string - addr := strings.Split(ctx.Request.RemoteAddr, ":") + addr := strings.Split(ctx.GinCtx.Request.RemoteAddr, ":") if len(addr) > 0 { ip = addr[0] } - log.Println(deviceCode, version, ip) - data := &model.KillaraCustomerDevice{ CustomerId: &customerID, Device: &deviceCode, Version: &version, Ip: &ip, - AppMarket: ¶m.AppMarket, + AppMarket: &ctx.Param.AppMarket, Date: basic.TimePtr(time.Now()), } @@ -143,14 +141,7 @@ func AccountLoginWithTelephonePassword(ctx *gin.Context, param *AccountLoginWith // // 假设 clearDuplicateToken 是对应的服务接口 // clearDuplicateToken(customerID, param.Token) - model.Models.KillaraCustomerTokenModel.ClearDuplicateToken(customerID, param.Token, 1) - - // return map[string]interface{}{ - // "success": true, - // "error_code": 0, - // "error_text": "", - // "data": make(map[string]interface{}), - // }, nil + model.Models.KillaraCustomerTokenModel.ClearDuplicateToken(customerID, ctx.Param.Token, 1) // log.Println(param) return resp.Success() @@ -162,7 +153,7 @@ func AccountLoginWithTelephonePassword(ctx *gin.Context, param *AccountLoginWith // country_code?: string; // telephone?: string; // token: string; -func AccountRegisterSmsCode(ctx *gin.Context, param *AccountRegisterSmsCodeParam) (resp *basic.Response) { +func AccountRegisterSmsCode(ctx *ActionContext[AccountRegisterSmsCodeParam]) (resp *basic.Response) { // ctx.ShouldBind() log.Println() return resp.Success() @@ -174,8 +165,9 @@ func AccountRegisterSmsCode(ctx *gin.Context, param *AccountRegisterSmsCodeParam // country_code?: string; // telephone?: string; // token: string; -func AccountForgetSmsCode(ctx *gin.Context, param *AccountForgetSmsCodeParam) (resp *basic.Response) { +func AccountForgetSmsCode(ctx *ActionContext[AccountForgetSmsCodeParam]) (resp *basic.Response) { // ctx.ShouldBind() + // ctx *gin.Context, param *AccountForgetSmsCodeParam log.Println() return resp.Success() } @@ -195,10 +187,9 @@ type RegisterValidEmailCode struct { // email: string; // timestamp: int64; // token: string; -func AccountRegisterEmailCode(ctx *gin.Context, param *AccountRegisterEmailCodeParam) (resp *basic.Response) { - log.Println(param) +func AccountRegisterEmailCode(ctx *ActionContext[AccountRegisterEmailCodeParam]) (resp *basic.Response) { - if !email.IsEmailValid(param.Email) { + if !email.IsEmailValid(ctx.Param.Email) { return resp.Error(basic.ErrEmailFormat) } @@ -207,8 +198,8 @@ func AccountRegisterEmailCode(ctx *gin.Context, param *AccountRegisterEmailCodeP code := auth.GenerateVerificationCode() codetoken := &RegisterValidEmailCode{ Code: code, - Email: param.Email, - AppMarket: param.AppMarket, + Email: ctx.Param.Email, + AppMarket: ctx.Param.AppMarket, } tokenstr, err := gcm.Encrypt(codetoken) if err != nil { @@ -228,7 +219,7 @@ func AccountRegisterEmailCode(ctx *gin.Context, param *AccountRegisterEmailCodeP // new_password: string; // old_password: string; // token?: string; -func MemberAlterPassword(ctx *gin.Context, param *MemberAlterPasswordParam) (resp *basic.Response) { +func MemberAlterPassword(ctx *ActionContext[MemberAlterPasswordParam]) (resp *basic.Response) { // ctx.ShouldBind() log.Println() return resp.Success() @@ -242,12 +233,82 @@ func MemberAlterPassword(ctx *gin.Context, param *MemberAlterPasswordParam) (res // action: string; // device: string; // version: string; -// app_market: int; +// app_market: uint64; // email: string; // timestamp: int64; // token: string; -func AccountLoginWithEmailPassword(ctx *gin.Context, param *AccountLoginWithEmailPasswordParam) (resp *basic.Response) { +func AccountLoginWithEmailPassword(ctx *ActionContext[AccountLoginWithEmailPasswordParam]) (resp *basic.Response) { + + if ctx.Param.Email == "" { + return resp.ErrorMsg(1, "email 参数缺失") + } + + if ctx.Param.Password == "" { + return resp.ErrorMsg(1, "password 参数缺失") + } + + email := strings.TrimSpace(ctx.Param.Email) + password := strings.TrimSpace(ctx.Param.Password) + + var customer *model.KillaraCustomer + var err error + + customer, err = model.Models.KillaraCustomerModel.GetCustomerByEmailForBackEnd(email) + if err != nil { + return resp.ErrorErr(1, err) + } + + ctx.Localize(translator.AccountNotRegistered) + if customer == nil { + return resp.ErrorTrCode(ctx, translator.AccountNotRegistered) + } + + if *customer.Status != 1 { + return resp.ErrorTrCode(ctx, translator.AccountDisabledNotLogin) + } + + if !auth.CheckPassword(*customer.Password, *customer.Salt, *customer.RandomPassword, password) { + return resp.ErrorTrCode(ctx, translator.AccountOrPasswordLoginFailed) + } + + customerID := *customer.CustomerId + + err = model.Models.KillaraCustomerTokenModel.UpdateTokenCustomerID(ctx.Param.Token, customerID) + if err != nil { + return resp.ErrorMsg(1, err.Error()) + } + + var deviceCode string + if ctx.Param.Device != "" { + deviceCode = ctx.Param.Device + } + + var version string + if ctx.Param.Version != "" { + version = ctx.Param.Version + } + + var ip string + addr := strings.Split(ctx.GinCtx.Request.RemoteAddr, ":") + if len(addr) > 0 { + ip = addr[0] + } + + data := &model.KillaraCustomerDevice{ + CustomerId: &customerID, + Device: &deviceCode, + Version: &version, + Ip: &ip, + AppMarket: &ctx.Param.AppMarket, + Date: basic.TimePtr(time.Now()), + } + + err = model.Models.KillaraCustomerModel.InsertCustomerDevice(data) + if err != nil { + return resp.ErrorErr(1, err) + } + + model.Models.KillaraCustomerTokenModel.ClearDuplicateToken(customerID, ctx.Param.Token, 1) - log.Println(param) return resp.Success() } diff --git a/server/app/internal/handlers/actions/types_gen.go b/server/app/internal/handlers/actions/types_gen.go index 2afba6f..4c615d2 100644 --- a/server/app/internal/handlers/actions/types_gen.go +++ b/server/app/internal/handlers/actions/types_gen.go @@ -2,10 +2,40 @@ package actions import ( "github.com/gin-gonic/gin" + + "github.com/iapologizewhenimwrong/Vestmore_GO/translator" "github.com/iapologizewhenimwrong/Vestmore_GO/utils/basic" "github.com/iapologizewhenimwrong/Vestmore_GO/utils/log" ) +type ActionContext[PARAM any] struct { + Lang string + GinCtx *gin.Context + Param *PARAM +} + +func (ctx *ActionContext[PARAM]) Localize(MessageID translator.TrCode) (string, error) { + return translator.Localize(MessageID, ctx.Lang, nil) +} + +func (ctx *ActionContext[PARAM]) LocalizeWithData(MessageID translator.TrCode, MessageTemplateParam any) (string, error) { + return translator.Localize(MessageID, ctx.Lang, MessageTemplateParam) +} + +func (ctx *ActionContext[PARAM]) LocalizeEx(MessageID translator.TrCode, Langs ...string) (string, error) { + if len(Langs) == 0 { + return translator.LocalizeEx(MessageID, nil, ctx.Lang) + } + return translator.LocalizeEx(MessageID, nil, Langs...) +} + +func (ctx *ActionContext[PARAM]) LocalizeExWithData(MessageID translator.TrCode, MessageTemplateParam any, Langs ...string) (string, error) { + if len(Langs) == 0 { + return translator.LocalizeEx(MessageID, MessageTemplateParam, ctx.Lang) + } + return translator.LocalizeEx(MessageID, MessageTemplateParam, Langs...) +} + var HandlersFuncRoutes map[string]gin.HandlerFunc = make(map[string]gin.HandlerFunc) func init() { @@ -40,10 +70,17 @@ func AccountForgetSmsCodeHandler(ctx *gin.Context) { if err != nil { log.Println(err) resp = resp.Error(basic.ErrParamParse) + ctx.JSON(200, resp) return } - resp = AccountForgetSmsCode(ctx, param) + actx := &ActionContext[AccountForgetSmsCodeParam]{ + Lang: basic.GetLangString(param), + GinCtx: ctx, + Param: param, + } + + resp = AccountForgetSmsCode(actx) if resp == nil { resp = resp.Error(basic.ErrRespNotNil) } @@ -57,7 +94,7 @@ type AccountLoginWithEmailPasswordParam struct { Action string `json:"action" form:"action" binding:"-"` Device string `json:"device" form:"device" binding:"-"` Version string `json:"version" form:"version" binding:"-"` - AppMarket int `json:"app_market" form:"app_market" binding:"-"` + AppMarket uint64 `json:"app_market" form:"app_market" binding:"-"` Email string `json:"email" form:"email" binding:"-"` Timestamp int64 `json:"timestamp" form:"timestamp" binding:"-"` Token string `json:"token" form:"token" binding:"-"` @@ -71,10 +108,17 @@ func AccountLoginWithEmailPasswordHandler(ctx *gin.Context) { if err != nil { log.Println(err) resp = resp.Error(basic.ErrParamParse) + ctx.JSON(200, resp) return } - resp = AccountLoginWithEmailPassword(ctx, param) + actx := &ActionContext[AccountLoginWithEmailPasswordParam]{ + Lang: basic.GetLangString(param), + GinCtx: ctx, + Param: param, + } + + resp = AccountLoginWithEmailPassword(actx) if resp == nil { resp = resp.Error(basic.ErrRespNotNil) } @@ -104,10 +148,17 @@ func AccountLoginWithTelephonePasswordHandler(ctx *gin.Context) { if err != nil { log.Println(err) resp = resp.Error(basic.ErrParamParse) + ctx.JSON(200, resp) return } - resp = AccountLoginWithTelephonePassword(ctx, param) + actx := &ActionContext[AccountLoginWithTelephonePasswordParam]{ + Lang: basic.GetLangString(param), + GinCtx: ctx, + Param: param, + } + + resp = AccountLoginWithTelephonePassword(actx) if resp == nil { resp = resp.Error(basic.ErrRespNotNil) } @@ -132,10 +183,17 @@ func AccountRegisterEmailCodeHandler(ctx *gin.Context) { if err != nil { log.Println(err) resp = resp.Error(basic.ErrParamParse) + ctx.JSON(200, resp) return } - resp = AccountRegisterEmailCode(ctx, param) + actx := &ActionContext[AccountRegisterEmailCodeParam]{ + Lang: basic.GetLangString(param), + GinCtx: ctx, + Param: param, + } + + resp = AccountRegisterEmailCode(actx) if resp == nil { resp = resp.Error(basic.ErrRespNotNil) } @@ -157,10 +215,17 @@ func AccountRegisterSmsCodeHandler(ctx *gin.Context) { if err != nil { log.Println(err) resp = resp.Error(basic.ErrParamParse) + ctx.JSON(200, resp) return } - resp = AccountRegisterSmsCode(ctx, param) + actx := &ActionContext[AccountRegisterSmsCodeParam]{ + Lang: basic.GetLangString(param), + GinCtx: ctx, + Param: param, + } + + resp = AccountRegisterSmsCode(actx) if resp == nil { resp = resp.Error(basic.ErrRespNotNil) } @@ -182,10 +247,17 @@ func BaseGetTokenHandler(ctx *gin.Context) { if err != nil { log.Println(err) resp = resp.Error(basic.ErrParamParse) + ctx.JSON(200, resp) return } - resp = BaseGetToken(ctx, param) + actx := &ActionContext[BaseGetTokenParam]{ + Lang: basic.GetLangString(param), + GinCtx: ctx, + Param: param, + } + + resp = BaseGetToken(actx) if resp == nil { resp = resp.Error(basic.ErrRespNotNil) } @@ -208,10 +280,17 @@ func MemberAlterPasswordHandler(ctx *gin.Context) { if err != nil { log.Println(err) resp = resp.Error(basic.ErrParamParse) + ctx.JSON(200, resp) return } - resp = MemberAlterPassword(ctx, param) + actx := &ActionContext[MemberAlterPasswordParam]{ + Lang: basic.GetLangString(param), + GinCtx: ctx, + Param: param, + } + + resp = MemberAlterPassword(actx) if resp == nil { resp = resp.Error(basic.ErrRespNotNil) } diff --git a/server/app/internal/handlers/types_gen.tpl b/server/app/internal/handlers/types_gen.tpl index f640ac3..ec7c79e 100644 --- a/server/app/internal/handlers/types_gen.tpl +++ b/server/app/internal/handlers/types_gen.tpl @@ -2,10 +2,40 @@ package actions import ( "github.com/gin-gonic/gin" + + "{{.ModuleName}}/translator" "{{.ModuleName}}/utils/basic" "{{.ModuleName}}/utils/log" ) +type ActionContext[PARAM any] struct { + Lang string + GinCtx *gin.Context + Param *PARAM +} + +func (ctx *ActionContext[PARAM]) Localize(MessageID translator.TrCode) (string, error) { + return translator.Localize(MessageID, ctx.Lang, nil) +} + +func (ctx *ActionContext[PARAM]) LocalizeWithData(MessageID translator.TrCode, MessageTemplateParam any) (string, error) { + return translator.Localize(MessageID, ctx.Lang, MessageTemplateParam) +} + +func (ctx *ActionContext[PARAM]) LocalizeEx(MessageID translator.TrCode, Langs ...string) (string, error) { + if len(Langs) == 0 { + return translator.LocalizeEx(MessageID, nil, ctx.Lang) + } + return translator.LocalizeEx(MessageID, nil, Langs...) +} + +func (ctx *ActionContext[PARAM]) LocalizeExWithData(MessageID translator.TrCode, MessageTemplateParam any, Langs ...string) (string, error) { + if len(Langs) == 0 { + return translator.LocalizeEx(MessageID, MessageTemplateParam, ctx.Lang) + } + return translator.LocalizeEx(MessageID, MessageTemplateParam, Langs...) +} + var HandlersFuncRoutes map[string]gin.HandlerFunc = make(map[string]gin.HandlerFunc) func init() { @@ -30,10 +60,17 @@ func {{.FuncName}}Handler(ctx *gin.Context) { if err != nil { log.Println(err) resp = resp.Error(basic.ErrParamParse) + ctx.JSON(200, resp) return } + + actx := &ActionContext[{{.ParamStruct.ParamStructName}}]{ + Lang: basic.GetLangString(param), + GinCtx: ctx, + Param: param, + } - resp = {{.FuncName}}(ctx, param) + resp = {{.FuncName}}(actx) if resp == nil { resp = resp.Error(basic.ErrRespNotNil) } diff --git a/server/app/main_test.go b/server/app/main_test.go index a53ce80..fe84999 100644 --- a/server/app/main_test.go +++ b/server/app/main_test.go @@ -2,10 +2,13 @@ package main import ( "log" - "reflect" "testing" _ "github.com/go-sql-driver/mysql" + "github.com/iapologizewhenimwrong/Vestmore_GO/translator" + "github.com/nicksnyder/go-i18n/v2/i18n" + "github.com/pelletier/go-toml/v2" + "golang.org/x/text/language" ) func TestMain(t *testing.T) { @@ -15,10 +18,26 @@ func TestMain(t *testing.T) { } func TestCaseZero(t *testing.T) { - var a string = "" - var b string - var c int = 1 - a = "" + // lang := language.Make("zh_cn") + log.Println(language.Make("zh_cn")) + log.Println(language.ParseAcceptLanguage("zh_cn")) + + tr := i18n.NewBundle(language.Chinese) + tr.RegisterUnmarshalFunc("toml", toml.Unmarshal) + tr.MustLoadMessageFile("./internal/i18n/zh_cn.toml") + + // accept := r.Header.Get("Accept-Language") + // i18n.NewLocalizer() + localizer := i18n.NewLocalizer(tr, "zh_cn") + log.Println(localizer.Localize(&i18n.LocalizeConfig{ + MessageID: "format_account_is_insufficient", + TemplateData: map[string]any{ + "CatalogCurrency": "USDT", + }, + })) +} + +func TestTr(t *testing.T) { + log.Println(translator.Localize("账号未注册", "zh_cn", nil)) - log.Println(reflect.ValueOf(a).IsZero(), reflect.ValueOf(b).IsZero(), reflect.ValueOf(c).IsZero()) } diff --git a/translator/translator.go b/translator/translator.go new file mode 100644 index 0000000..d80bf57 --- /dev/null +++ b/translator/translator.go @@ -0,0 +1,58 @@ +package translator + +import ( + "path/filepath" + "runtime" + + "github.com/nicksnyder/go-i18n/v2/i18n" + "github.com/pelletier/go-toml/v2" + "golang.org/x/text/language" +) + +var Bundle *i18n.Bundle + +func init() { + var err error + + _, currentFile, _, _ := runtime.Caller(0) + currentDir := filepath.Dir(currentFile) + + // 解析模板文件 + globpath := filepath.Join(currentDir, "/*.toml") + + Bundle = i18n.NewBundle(language.Chinese) + Bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) + matches, err := filepath.Glob(globpath) + if err != nil { + panic(err) + } + for _, m := range matches { + Bundle.MustLoadMessageFile(m) + } + + // localizer := i18n.NewLocalizer(Bundle, "zh_cn") + // log.Println(localizer.Localize(&i18n.LocalizeConfig{ + // MessageID: "format_account_is_insufficient", + // TemplateData: map[string]any{ + // "CatalogCurrency": "USDT", + // }, + // })) + +} + +func Localize(MessageID TrCode, Lang string, MessageTemplateParam any) (string, error) { + var localizer = i18n.NewLocalizer(Bundle, Lang) + return localizer.Localize(&i18n.LocalizeConfig{ + MessageID: string(MessageID), + TemplateData: MessageTemplateParam, + }) +} + +func LocalizeEx(MessageID TrCode, MessageTemplateParam any, Langs ...string) (string, error) { + var localizer = i18n.NewLocalizer(Bundle, Langs...) + + return localizer.Localize(&i18n.LocalizeConfig{ + MessageID: string(MessageID), + TemplateData: MessageTemplateParam, + }) +} diff --git a/translator/translator_gen.go b/translator/translator_gen.go new file mode 100644 index 0000000..e5488eb --- /dev/null +++ b/translator/translator_gen.go @@ -0,0 +1,538 @@ +package translator + +type TrCode string + +// 翻译对应的标志字符串ID +const ( + // account_disabled + // description: "您的账户已禁用,暂时无法操作" one: "您的账户已禁用,暂时无法操作" other: "您的账户已禁用,暂时无法操作" + AccountDisabled TrCode = "account_disabled" + + // account_disabled_not_login + // description: "您的账户已禁用,暂时无法操作" one: "您的账户已禁用,暂时无法操作" other: "您的账户已禁用,暂时无法操作" + AccountDisabledNotLogin TrCode = "account_disabled_not_login" + + // account_not_registered + // description: "账户未注册,请注册后再操作" one: "账户未注册,请注册后再操作" other: "账户未注册,请注册后再操作" + AccountNotRegistered TrCode = "account_not_registered" + + // account_or_password_login_failed + // description: "账号或密码不正确,登录失败" one: "账号或密码不正确,登录失败" other: "账号或密码不正确,登录失败" + AccountOrPasswordLoginFailed TrCode = "account_or_password_login_failed" + + // all_day_transaction + // description: "全天交易" one: "全天交易" other: "全天交易" + AllDayTransaction TrCode = "all_day_transaction" + + // application_has_been_submitted + // description: "很抱歉,申请已提交交易所,无法取消" one: "很抱歉,申请已提交交易所,无法取消" other: "很抱歉,申请已提交交易所,无法取消" + ApplicationHasBeenSubmitted TrCode = "application_has_been_submitted" + + // apply_cannot_be_cancelled + // description: "该笔申请已封帐无法取消" one: "该笔申请已封帐无法取消" other: "该笔申请已封帐无法取消" + ApplyCannotBeCancelled TrCode = "apply_cannot_be_cancelled" + + // balance_return + // description: "(退回)" one: "(退回)" other: "(退回)" + BalanceReturn TrCode = "balance_return" + + // balance_role_1 + // description: "股票买入" one: "股票买入" other: "股票买入" + BalanceRole1 TrCode = "balance_role_1" + + // balance_role_10 + // description: "平台分成" one: "平台分成" other: "平台分成" + BalanceRole10 TrCode = "balance_role_10" + + // balance_role_11 + // description: "出金" one: "出金" other: "出金" + BalanceRole11 TrCode = "balance_role_11" + + // balance_role_12 + // description: "入金" one: "入金" other: "入金" + BalanceRole12 TrCode = "balance_role_12" + + // balance_role_13 + // description: "基金申购金额" one: "基金申购金额" other: "基金申购金额" + BalanceRole13 TrCode = "balance_role_13" + + // balance_role_14 + // description: "基金申购费" one: "基金申购费" other: "基金申购费" + BalanceRole14 TrCode = "balance_role_14" + + // balance_role_15 + // description: "基金管理费" one: "基金管理费" other: "基金管理费" + BalanceRole15 TrCode = "balance_role_15" + + // balance_role_16 + // description: "资金补扣" one: "资金补扣" other: "资金补扣" + BalanceRole16 TrCode = "balance_role_16" + + // balance_role_17 + // description: "资金退还" one: "资金退还" other: "资金退还" + BalanceRole17 TrCode = "balance_role_17" + + // balance_role_18 + // description: "基金结算获利" one: "基金结算获利" other: "基金结算获利" + BalanceRole18 TrCode = "balance_role_18" + + // balance_role_19 + // description: "虚拟加金" one: "虚拟加金" other: "虚拟加金" + BalanceRole19 TrCode = "balance_role_19" + + // balance_role_2 + // description: "买入手续费" one: "买入手续费" other: "买入手续费" + BalanceRole2 TrCode = "balance_role_2" + + // balance_role_20 + // description: "手动入金" one: "手动入金" other: "手动入金" + BalanceRole20 TrCode = "balance_role_20" + + // balance_role_21 + // description: "转移资金" one: "转移资金" other: "转移资金" + BalanceRole21 TrCode = "balance_role_21" + + // balance_role_22 + // description: "推广佣金" one: "推广佣金" other: "推广佣金" + BalanceRole22 TrCode = "balance_role_22" + + // balance_role_23 + // description: "基金超额管理费" one: "基金超额管理费" other: "基金超额管理费" + BalanceRole23 TrCode = "balance_role_23" + + // balance_role_24 + // description: "货币转换" one: "货币转换" other: "货币转换" + BalanceRole24 TrCode = "balance_role_24" + + // balance_role_25 + // description: "配资投资本金" one: "配资投资本金" other: "配资投资本金" + BalanceRole25 TrCode = "balance_role_25" + + // balance_role_26 + // description: "配资利息" one: "配资利息" other: "配资利息" + BalanceRole26 TrCode = "balance_role_26" + + // balance_role_28 + // description: "赠金" one: "赠金" other: "赠金" + BalanceRole28 TrCode = "balance_role_28" + + // balance_role_3 + // description: "股票卖出" one: "股票卖出" other: "股票卖出" + BalanceRole3 TrCode = "balance_role_3" + + // balance_role_4 + // description: "卖出手续费" one: "卖出手续费" other: "卖出手续费" + BalanceRole4 TrCode = "balance_role_4" + + // balance_role_5 + // description: "新股申购" one: "新股申购" other: "新股申购" + BalanceRole5 TrCode = "balance_role_5" + + // balance_role_6 + // description: "新股融资利息" one: "新股融资利息" other: "新股融资利息" + BalanceRole6 TrCode = "balance_role_6" + + // balance_role_7 + // description: "新股申购手续费" one: "新股申购手续费" other: "新股申购手续费" + BalanceRole7 TrCode = "balance_role_7" + + // balance_role_8 + // description: "新股中签手续费" one: "新股中签手续费" other: "新股中签手续费" + BalanceRole8 TrCode = "balance_role_8" + + // balance_role_9 + // description: "国配意向金" one: "国配意向金" other: "国配意向金" + BalanceRole9 TrCode = "balance_role_9" + + // buy_success_content_1 + // description: "您的交易 " one: "您的交易 " other: "您的交易 " + BuySuccessContent1 TrCode = "buy_success_content_1" + + // buy_success_content_2 + // description: " 买入 " one: " 买入 " other: " 买入 " + BuySuccessContent2 TrCode = "buy_success_content_2" + + // buy_success_content_3 + // description: " 已成交" one: " 已成交" other: " 已成交" + BuySuccessContent3 TrCode = "buy_success_content_3" + + // buy_success_title + // description: "买入交易已成交" one: "买入交易已成交" other: "买入交易已成交" + BuySuccessTitle TrCode = "buy_success_title" + + // category_not_exist + // description: "分类不存在" one: "分类不存在" other: "分类不存在" + CategoryNotExist TrCode = "category_not_exist" + + // currency_exchange_status_1 + // description: "待处理" one: "待处理" other: "待处理" + CurrencyExchangeStatus1 TrCode = "currency_exchange_status_1" + + // currency_exchange_status_2 + // description: "转换成功" one: "转换成功" other: "转换成功" + CurrencyExchangeStatus2 TrCode = "currency_exchange_status_2" + + // currency_exchange_status_3 + // description: "转换失败" one: "转换失败" other: "转换失败" + CurrencyExchangeStatus3 TrCode = "currency_exchange_status_3" + + // currency_exchange_status_4 + // description: "已取消" one: "已取消" other: "已取消" + CurrencyExchangeStatus4 TrCode = "currency_exchange_status_4" + + // current_position_of_the_stock + // description: "股票当前持仓数量" one: "股票当前持仓数量" other: "股票当前持仓数量" + CurrentPositionOfTheStock TrCode = "current_position_of_the_stock" + + // currently_only_supports_conversion_hkd + // description: "目前只支持转为港币 HKD $" one: "目前只支持转为港币 HKD $" other: "目前只支持转为港币 HKD $" + CurrentlyOnlySupportsConversionHkd TrCode = "currently_only_supports_conversion_hkd" + + // data_not_exist + // description: "数据不存在!" one: "数据不存在!" other: "数据不存在!" + DataNotExist TrCode = "data_not_exist" + + // data_not_exist_retry + // description: "数据不存在,请重试!" one: "数据不存在,请重试!" other: "数据不存在,请重试!" + DataNotExistRetry TrCode = "data_not_exist_retry" + + // day + // description: "日" one: "日" other: "日" + Day TrCode = "day" + + // days_ago + // description: " 天前" one: " 天前" other: " 天前" + DaysAgo TrCode = "days_ago" + + // default_trading_time_period + // description: "默认交易时间段" one: "默认交易时间段" other: "默认交易时间段" + DefaultTradingTimePeriod TrCode = "default_trading_time_period" + + // email_exchange_title + // description: "资金变动提示" one: "资金变动提示" other: "资金变动提示" + EmailExchangeTitle TrCode = "email_exchange_title" + + // email_registered + // description: "邮箱已注册" one: "邮箱已注册" other: "邮箱已注册" + EmailRegistered TrCode = "email_registered" + + // email_registered_forget_password + // description: "邮箱已注册,请直接登录,如您忘记登录密码,请找回登录密码" one: "邮箱已注册,请直接登录,如您忘记登录密码,请找回登录密码" other: "邮箱已注册,请直接登录,如您忘记登录密码,请找回登录密码" + EmailRegisteredForgetPassword TrCode = "email_registered_forget_password" + + // email_verify_code + // description: "邮箱验证码" one: "邮箱验证码" other: "邮箱验证码" + EmailVerifyCode TrCode = "email_verify_code" + + // email_verify_code_wrong + // description: "邮箱验证码不正确" one: "邮箱验证码不正确" other: "邮箱验证码不正确" + EmailVerifyCodeWrong TrCode = "email_verify_code_wrong" + + // email_verify_code_wrong_login_fail + // description: "邮箱验证码不正确,登录失败" one: "邮箱验证码不正确,登录失败" other: "邮箱验证码不正确,登录失败" + EmailVerifyCodeWrongLoginFail TrCode = "email_verify_code_wrong_login_fail" + + // financing_not_cancel + // description: "很抱歉,融资打新不允许取消" one: "很抱歉,融资打新不允许取消" other: "很抱歉,融资打新不允许取消" + FinancingNotCancel TrCode = "financing_not_cancel" + + // format_account_is_insufficient + // description: "账户余额不足,请先入金" one: "{{.CatalogCurrency}} 账户余额不足,请先入金" other: "{{.CatalogCurrency}} 账户余额不足,请先入金" + FormatAccountIsInsufficient TrCode = "format_account_is_insufficient" + + // fund_has_been_delisted + // description: "很抱歉,基金已下架,暂时无法申购" one: "很抱歉,基金已下架,暂时无法申购" other: "很抱歉,基金已下架,暂时无法申购" + FundHasBeenDelisted TrCode = "fund_has_been_delisted" + + // fund_order_status_1 + // description: "已申购" one: "已申购" other: "已申购" + FundOrderStatus1 TrCode = "fund_order_status_1" + + // fund_order_status_2 + // description: "已取消" one: "已取消" other: "已取消" + FundOrderStatus2 TrCode = "fund_order_status_2" + + // fund_order_status_3 + // description: "已结算" one: "已结算" other: "已结算" + FundOrderStatus3 TrCode = "fund_order_status_3" + + // get_email_verify_code_first + // description: "请先获取邮箱验证码" one: "请先获取邮箱验证码" other: "请先获取邮箱验证码" + GetEmailVerifyCodeFirst TrCode = "get_email_verify_code_first" + + // get_phone_number_verify_code_first + // description: "请先获取手机验证码" one: "请先获取手机验证码" other: "请先获取手机验证码" + GetPhoneNumberVerifyCodeFirst TrCode = "get_phone_number_verify_code_first" + + // have_a_subscription_record + // description: "您已有该新股的申购记录,不能重复申请" one: "您已有该新股的申购记录,不能重复申请" other: "您已有该新股的申购记录,不能重复申请" + HaveASubscriptionRecord TrCode = "have_a_subscription_record" + + // have_an_entrustment_record + // description: "您已有该新股的委托记录,不能重复委托" one: "您已有该新股的委托记录,不能重复委托" other: "您已有该新股的委托记录,不能重复委托" + HaveAnEntrustmentRecord TrCode = "have_an_entrustment_record" + + // holding_amount_is_insufficient + // description: ",持有数量不足,无法卖出" one: ",持有数量不足,无法卖出" other: ",持有数量不足,无法卖出" + HoldingAmountIsInsufficient TrCode = "holding_amount_is_insufficient" + + // hongkong_account_is_insufficient + // description: "港股账户余额不足,请先入金" one: "港股账户余额不足,请先入金" other: "港股账户余额不足,请先入金" + HongkongAccountIsInsufficient TrCode = "hongkong_account_is_insufficient" + + // hongkong_stock_account_not_be_less_than + // description: "融资申购要求港股账户余额不低于" one: "融资申购要求港股账户余额不低于" other: "融资申购要求港股账户余额不低于" + HongkongStockAccountNotBeLessThan TrCode = "hongkong_stock_account_not_be_less_than" + + // hours_ago + // description: " 小时前" one: " 小时前" other: " 小时前" + HoursAgo TrCode = "hours_ago" + + // in_transaction + // description: "交易中" one: "交易中" other: "交易中" + InTransaction TrCode = "in_transaction" + + // incorrect_amount_retry + // description: "金额错误,请重试" one: "金额错误,请重试" other: "金额错误,请重试" + IncorrectAmountRetry TrCode = "incorrect_amount_retry" + + // incorrect_security_code_retry + // description: "证券代码错误,请重试" one: "证券代码错误,请重试" other: "证券代码错误,请重试" + IncorrectSecurityCodeRetry TrCode = "incorrect_security_code_retry" + + // insufficient_account_balance + // description: "账户余额不足,转换失败" one: "账户余额不足,转换失败" other: "账户余额不足,转换失败" + InsufficientAccountBalance TrCode = "insufficient_account_balance" + + // invitation_code_not_exists + // description: "邀请码不存在或有误,请检查后重试" one: "邀请码不存在或有误,请检查后重试" other: "邀请码不存在或有误,请检查后重试" + InvitationCodeNotExists TrCode = "invitation_code_not_exists" + + // ipo_cash_commission + // description: "现金委托" one: "现金委托" other: "现金委托" + IpoCashCommission TrCode = "ipo_cash_commission" + + // ipo_financing_10_times + // description: "融资10倍" one: "融资10倍" other: "融资10倍" + IpoFinancing10Times TrCode = "ipo_financing_10_times" + + // ipo_financing_20_times + // description: "融资20倍" one: "融资20倍" other: "融资20倍" + IpoFinancing20Times TrCode = "ipo_financing_20_times" + + // ipo_times + // description: " 倍" one: " 倍" other: " 倍" + IpoTimes TrCode = "ipo_times" + + // market_closed_orders_cannot_be_placed + // description: "当前市场休市中,暂不能下单" one: "当前市场休市中,暂不能下单" other: "当前市场休市中,暂不能下单" + MarketClosedOrdersCannotBePlaced TrCode = "market_closed_orders_cannot_be_placed" + + // max + // description: "最大" one: "最大" other: "最大" + Max TrCode = "max" + + // maximum_number_of_sells + // description: "允许卖出数量最多为" one: "允许卖出数量最多为" other: "允许卖出数量最多为" + MaximumNumberOfSells TrCode = "maximum_number_of_sells" + + // minimum_purchase_amount + // description: "申购金额最低" one: "申购金额最低" other: "申购金额最低" + MinimumPurchaseAmount TrCode = "minimum_purchase_amount" + + // minutes_ago + // description: " 分钟前" one: " 分钟前" other: " 分钟前" + MinutesAgo TrCode = "minutes_ago" + + // month + // description: "月" one: "月" other: "月" + Month TrCode = "month" + + // new_shares_do_not_exist + // description: "新股不存在" one: "新股不存在" other: "新股不存在" + NewSharesDoNotExist TrCode = "new_shares_do_not_exist" + + // non_default_trading_time_period + // description: "非默认交易时间段" one: "非默认交易时间段" other: "非默认交易时间段" + NonDefaultTradingTimePeriod TrCode = "non_default_trading_time_period" + + // non_grey_market_trading_time + // description: "当前为非暗盘交易时间,下单失败" one: "当前为非暗盘交易时间,下单失败" other: "当前为非暗盘交易时间,下单失败" + NonGreyMarketTradingTime TrCode = "non_grey_market_trading_time" + + // order_failed_odd_lot_trading + // description: "下单失败,不支持碎股交易" one: "下单失败,不支持碎股交易" other: "下单失败,不支持碎股交易" + OrderFailedOddLotTrading TrCode = "order_failed_odd_lot_trading" + + // order_failed_price_cannot_be_higher_than + // description: "下单失败,价格不能高于" one: "下单失败,价格不能高于" other: "下单失败,价格不能高于" + OrderFailedPriceCannotBeHigherThan TrCode = "order_failed_price_cannot_be_higher_than" + + // order_failed_price_cannot_be_lower_than + // description: "下单失败,价格不能低于" one: "下单失败,价格不能低于" other: "下单失败,价格不能低于" + OrderFailedPriceCannotBeLowerThan TrCode = "order_failed_price_cannot_be_lower_than" + + // order_failed_price_is_incorrect + // description: "下单失败,价格不正确" one: "下单失败,价格不正确" other: "下单失败,价格不正确" + OrderFailedPriceIsIncorrect TrCode = "order_failed_price_is_incorrect" + + // order_failed_retry + // description: "下单失败,请重试" one: "下单失败,请重试" other: "下单失败,请重试" + OrderFailedRetry TrCode = "order_failed_retry" + + // order_is_not_cancel + // description: "该订单当前不支持撤单" one: "该订单当前不支持撤单" other: "该订单当前不支持撤单" + OrderIsNotCancel TrCode = "order_is_not_cancel" + + // order_needs_3_minutes_before + // description: "市价单需要下单3分钟后才能撤单" one: "市价单需要下单3分钟后才能撤单" other: "市价单需要下单3分钟后才能撤单" + OrderNeeds3MinutesBefore TrCode = "order_needs_3_minutes_before" + + // other + // description: "其他" one: "其他" other: "其他" + Other TrCode = "other" + + // password_match_error + // description: "两次密码不一致" one: "两次密码不一致" other: "两次密码不一致" + PasswordMatchError TrCode = "password_match_error" + + // password_match_error_origin + // description: "原始密码不一致" one: "原始密码不一致" other: "原始密码不一致" + PasswordMatchErrorOrigin TrCode = "password_match_error_origin" + + // phone_number_country_incorrect + // description: "手机所属国家不正确,请检查" one: "手机所属国家不正确,请检查" other: "手机所属国家不正确,请检查" + PhoneNumberCountryIncorrect TrCode = "phone_number_country_incorrect" + + // phone_number_not_registered + // description: "账户未注册,请注册后再操作" one: "账户未注册,请注册后再操作" other: "账户未注册,请注册后再操作" + PhoneNumberNotRegistered TrCode = "phone_number_not_registered" + + // phone_number_registered + // description: "手机已注册" one: "手机已注册" other: "手机已注册" + PhoneNumberRegistered TrCode = "phone_number_registered" + + // phone_number_registered_forget_password + // description: "手机号已注册,请直接登录,如您忘记登录密码,请找回登录密码" one: "手机号已注册,请直接登录,如您忘记登录密码,请找回登录密码" other: "手机号已注册,请直接登录,如您忘记登录密码,请找回登录密码" + PhoneNumberRegisteredForgetPassword TrCode = "phone_number_registered_forget_password" + + // phone_number_verify_code_wrong + // description: "手机验证码不正确" one: "手机验证码不正确" other: "手机验证码不正确" + PhoneNumberVerifyCodeWrong TrCode = "phone_number_verify_code_wrong" + + // phone_number_verify_code_wrong_login_fail + // description: "手机验证码不正确,登录失败" one: "手机验证码不正确,登录失败" other: "手机验证码不正确,登录失败" + PhoneNumberVerifyCodeWrongLoginFail TrCode = "phone_number_verify_code_wrong_login_fail" + + // please_deposit_first + // description: ",请先入金!" one: ",请先入金!" other: ",请先入金!" + PleaseDepositFirst TrCode = "please_deposit_first" + + // pretrade_not_allow_market_order + // description: "盘前交易不支持市价单,请更换重试" one: "盘前交易不支持市价单,请更换重试" other: "盘前交易不支持市价单,请更换重试" + PretradeNotAllowMarketOrder TrCode = "pretrade_not_allow_market_order" + + // record_is_not_cancel + // description: "该记录当前已不允许取消" one: "该记录当前已不允许取消" other: "该记录当前已不允许取消" + RecordIsNotCancel TrCode = "record_is_not_cancel" + + // record_not_exist + // description: "记录不存在" one: "记录不存在" other: "记录不存在" + RecordNotExist TrCode = "record_not_exist" + + // rise + // description: "起" one: "起" other: "起" + Rise TrCode = "rise" + + // sell_success_content_1 + // description: "您的交易 " one: "您的交易 " other: "您的交易 " + SellSuccessContent1 TrCode = "sell_success_content_1" + + // sell_success_content_2 + // description: " 卖出 " one: " 卖出 " other: " 卖出 " + SellSuccessContent2 TrCode = "sell_success_content_2" + + // sell_success_content_3 + // description: " 已成交" one: " 已成交" other: " 已成交" + SellSuccessContent3 TrCode = "sell_success_content_3" + + // sell_success_title + // description: "卖出交易已成交" one: "卖出交易已成交" other: "卖出交易已成交" + SellSuccessTitle TrCode = "sell_success_title" + + // sms_verify_code_content_1 + // description: "你的短讯验证码为:" one: "你的短讯验证码为:" other: "你的短讯验证码为:" + SmsVerifyCodeContent1 TrCode = "sms_verify_code_content_1" + + // state_not_cancel + // description: "当前状态不允许取消" one: "当前状态不允许取消" other: "当前状态不允许取消" + StateNotCancel TrCode = "state_not_cancel" + + // stock + // description: "股票" one: "股票" other: "股票" + Stock TrCode = "stock" + + // stock_insufficient + // description: "您当前未持仓该股票或持仓数量不足,无法卖出" one: "您当前未持仓该股票或持仓数量不足,无法卖出" other: "您当前未持仓该股票或持仓数量不足,无法卖出" + StockInsufficient TrCode = "stock_insufficient" + + // stocks_do_not_support_grey_market_trading + // description: "股票不支持暗盘交易" one: "股票不支持暗盘交易" other: "股票不支持暗盘交易" + StocksDoNotSupportGreyMarketTrading TrCode = "stocks_do_not_support_grey_market_trading" + + // subscription_deadline_has_passed + // description: "当前已超过认购截止时间!" one: "当前已超过认购截止时间!" other: "当前已超过认购截止时间!" + SubscriptionDeadlineHasPassed TrCode = "subscription_deadline_has_passed" + + // subscription_failed_over_purchased + // description: "申购失败,购买超额。基金当前剩余申购额度为:" one: "申购失败,购买超额。基金当前剩余申购额度为:" other: "申购失败,购买超额。基金当前剩余申购额度为:" + SubscriptionFailedOverPurchased TrCode = "subscription_failed_over_purchased" + + // text_required + // description: "数据有误" one: "数据有误" other: "数据有误" + TextRequired TrCode = "text_required" + + // the_market_is_closed + // description: "休市中" one: "休市中" other: "休市中" + TheMarketIsClosed TrCode = "the_market_is_closed" + + // the_market_is_closed_on_weekends + // description: "周末休市中" one: "周末休市中" other: "周末休市中" + TheMarketIsClosedOnWeekends TrCode = "the_market_is_closed_on_weekends" + + // the_maximum_quantity_is_limited_to_10_digits + // description: "数量最大限制10位数以内" one: "数量最大限制10位数以内" other: "数量最大限制10位数以内" + TheMaximumQuantityIsLimitedTo10Digits TrCode = "the_maximum_quantity_is_limited_to_10_digits" + + // to_be_disclosed + // description: "待公布" one: "待公布" other: "待公布" + ToBeDisclosed TrCode = "to_be_disclosed" + + // us_account_is_insufficient + // description: "美股账户余额不足,请先入金" one: "美股账户余额不足,请先入金" other: "美股账户余额不足,请先入金" + UsAccountIsInsufficient TrCode = "us_account_is_insufficient" + + // us_stock_account_not_be_less_than + // description: "融资申购要求美股账户余额不低于" one: "融资申购要求美股账户余额不低于" other: "融资申购要求美股账户余额不低于" + UsStockAccountNotBeLessThan TrCode = "us_stock_account_not_be_less_than" + + // usdt_account_is_insufficient + // description: "USDT账户余额不足,请先入金" one: "USDT账户余额不足,请先入金" other: "USDT账户余额不足,请先入金" + UsdtAccountIsInsufficient TrCode = "usdt_account_is_insufficient" + + // waiting_to_open + // description: "待开盘" one: "待开盘" other: "待开盘" + WaitingToOpen TrCode = "waiting_to_open" + + // week + // description: "周" one: "周" other: "周" + Week TrCode = "week" + + // withfunding_asset_total_must_large_than_order_total + // description: "下单失败,操盘资金必须大于下单金额" one: "下单失败,操盘资金必须大于下单金额" other: "下单失败,操盘资金必须大于下单金额" + WithfundingAssetTotalMustLargeThanOrderTotal TrCode = "withfunding_asset_total_must_large_than_order_total" + + // withfunding_must_all_clear + // description: "下单失败,配资必须全仓卖出" one: "下单失败,配资必须全仓卖出" other: "下单失败,配资必须全仓卖出" + WithfundingMustAllClear TrCode = "withfunding_must_all_clear" +) diff --git a/translator/translator_test.go b/translator/translator_test.go new file mode 100644 index 0000000..9d4d306 --- /dev/null +++ b/translator/translator_test.go @@ -0,0 +1,165 @@ +package translator + +import ( + "bytes" + "encoding/json" + "fmt" + "go/format" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "runtime" + "sort" + "strings" + "testing" + "text/template" + "unicode" + + "github.com/pelletier/go-toml/v2" +) + +var trTemplateStr = ` +package translator + +type TrCode string +// 翻译对应的标志字符串ID +const ( +{{- range .}} + // {{.MessageID}} + // description: "{{.Desc}}" one: "{{.One}}" other: "{{.Other}}" + {{.MessageCamelID}} TrCode = "{{.MessageID}}" +{{end}} +) +` + +// type TrCode string + +// const ( +// a TrCode = "1" +// ) + +type TrCodeData struct { + MessageID string + MessageCamelID string + Desc string + One string + Other string +} + +func TestGenTrCode(t *testing.T) { + _, currentFile, _, _ := runtime.Caller(0) + currentDir := filepath.Dir(currentFile) + + // 解析模板文件 + tfile := filepath.Join(currentDir, "/zh_cn.toml") + createTrCode(tfile) + // log.Println(string(buf.Bytes())) +} + +func createTrCode(filePath string) { + tomlFile, err := os.Open(filePath) + if err != nil { + panic(err) + } + var tomlMap map[string]any + err = toml.NewDecoder(tomlFile).Decode(&tomlMap) + if err != nil { + panic(err) + } + + var datas []*TrCodeData + + for key, valuesMap := range tomlMap { + values := valuesMap.(map[string]any) + data := &TrCodeData{} + data.MessageID = key + data.MessageCamelID = toCamelCase(key) + data.Desc = values["description"].(string) + data.One = values["one"].(string) + data.Other = values["other"].(string) + datas = append(datas, data) + } + + sort.Slice(datas, func(i, j int) bool { + return datas[i].MessageID < datas[j].MessageID + }) + + tpl := template.New("translator") + tpl.Parse(trTemplateStr) + var buf bytes.Buffer + err = tpl.Execute(&buf, datas) + if err != nil { + panic(err) + } + + wdata, err := format.Source(buf.Bytes()) + if err != nil { + panic(err) + } + + err = os.WriteFile("./translator_gen.go", wdata, 0644) + if err != nil { + panic(err) + } +} + +type TranslateRequest struct { + Q string `json:"q"` + Source string `json:"source"` + Target string `json:"target"` +} + +type TranslateResponse struct { + TranslatedText string `json:"translatedText"` +} + +func TestTrOnline(t *testing.T) { + requestBody, err := json.Marshal(TranslateRequest{ + Q: "Hello!", + Source: "en", + Target: "es", + }) + if err != nil { + fmt.Println("Error marshaling request body:", err) + return + } + + resp, err := http.Post("https://libretranslate.com/translate", "application/json", bytes.NewBuffer(requestBody)) + if err != nil { + fmt.Println("Error sending request:", err) + return + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + fmt.Println("Error reading response body:", err) + return + } + + var translationResponse TranslateResponse + err = json.Unmarshal(body, &translationResponse) + if err != nil { + fmt.Println("Error unmarshaling response:", err) + return + } + + fmt.Println(translationResponse.TranslatedText) +} + +func toCamelCase(s string) string { + var sb strings.Builder + capitalizeNext := true + for _, r := range s { + if r == '_' || r == '/' || r == '-' { + capitalizeNext = true + } else if capitalizeNext { + sb.WriteRune(unicode.ToUpper(r)) + capitalizeNext = false + } else { + sb.WriteRune(unicode.ToLower(r)) + } + } + return sb.String() +} diff --git a/translator/zh_cn.toml b/translator/zh_cn.toml new file mode 100644 index 0000000..c4897fe --- /dev/null +++ b/translator/zh_cn.toml @@ -0,0 +1,555 @@ +[email_verify_code] +description = "邮箱验证码" +one = "邮箱验证码" +other = "邮箱验证码" +[get_email_verify_code_first] +description = "请先获取邮箱验证码" +one = "请先获取邮箱验证码" +other = "请先获取邮箱验证码" +[email_verify_code_wrong] +description = "邮箱验证码不正确" +one = "邮箱验证码不正确" +other = "邮箱验证码不正确" +[email_verify_code_wrong_login_fail] +description = "邮箱验证码不正确,登录失败" +one = "邮箱验证码不正确,登录失败" +other = "邮箱验证码不正确,登录失败" +[email_registered] +description = "邮箱已注册" +one = "邮箱已注册" +other = "邮箱已注册" +[email_registered_forget_password] +description = "邮箱已注册,请直接登录,如您忘记登录密码,请找回登录密码" +one = "邮箱已注册,请直接登录,如您忘记登录密码,请找回登录密码" +other = "邮箱已注册,请直接登录,如您忘记登录密码,请找回登录密码" +[invitation_code_not_exists] +description = "邀请码不存在或有误,请检查后重试" +one = "邀请码不存在或有误,请检查后重试" +other = "邀请码不存在或有误,请检查后重试" +[password_match_error] +description = "两次密码不一致" +one = "两次密码不一致" +other = "两次密码不一致" +[account_not_registered] +description = "账户未注册,请注册后再操作" +one = "账户未注册,请注册后再操作" +other = "账户未注册,请注册后再操作" +[account_disabled] +description = "您的账户已禁用,暂时无法操作" +one = "您的账户已禁用,暂时无法操作" +other = "您的账户已禁用,暂时无法操作" +[phone_number_not_registered] +description = "账户未注册,请注册后再操作" +one = "账户未注册,请注册后再操作" +other = "账户未注册,请注册后再操作" +[phone_number_country_incorrect] +description = "手机所属国家不正确,请检查" +one = "手机所属国家不正确,请检查" +other = "手机所属国家不正确,请检查" +[phone_number_verify_code_wrong] +description = "手机验证码不正确" +one = "手机验证码不正确" +other = "手机验证码不正确" +[phone_number_registered] +description = "手机已注册" +one = "手机已注册" +other = "手机已注册" +[phone_number_registered_forget_password] +description = "手机号已注册,请直接登录,如您忘记登录密码,请找回登录密码" +one = "手机号已注册,请直接登录,如您忘记登录密码,请找回登录密码" +other = "手机号已注册,请直接登录,如您忘记登录密码,请找回登录密码" +[get_phone_number_verify_code_first] +description = "请先获取手机验证码" +one = "请先获取手机验证码" +other = "请先获取手机验证码" +[phone_number_verify_code_wrong_login_fail] +description = "手机验证码不正确,登录失败" +one = "手机验证码不正确,登录失败" +other = "手机验证码不正确,登录失败" +[account_disabled_not_login] +description = "您的账户已禁用,暂时无法操作" +one = "您的账户已禁用,暂时无法操作" +other = "您的账户已禁用,暂时无法操作" +[account_or_password_login_failed] +description = "账号或密码不正确,登录失败" +one = "账号或密码不正确,登录失败" +other = "账号或密码不正确,登录失败" + +[record_not_exist] +description = "记录不存在" +one = "记录不存在" +other = "记录不存在" +[category_not_exist] +description = "分类不存在" +one = "分类不存在" +other = "分类不存在" +[data_not_exist] +description = "数据不存在!" +one = "数据不存在!" +other = "数据不存在!" +[data_not_exist_retry] +description = "数据不存在,请重试!" +one = "数据不存在,请重试!" +other = "数据不存在,请重试!" +[new_shares_do_not_exist] +description = "新股不存在" +one = "新股不存在" +other = "新股不存在" + +[state_not_cancel] +description = "当前状态不允许取消" +one = "当前状态不允许取消" +other = "当前状态不允许取消" +[apply_cannot_be_cancelled] +description = "该笔申请已封帐无法取消" +one = "该笔申请已封帐无法取消" +other = "该笔申请已封帐无法取消" +[financing_not_cancel] +description = "很抱歉,融资打新不允许取消" +one = "很抱歉,融资打新不允许取消" +other = "很抱歉,融资打新不允许取消" +[application_has_been_submitted] +description = "很抱歉,申请已提交交易所,无法取消" +one = "很抱歉,申请已提交交易所,无法取消" +other = "很抱歉,申请已提交交易所,无法取消" +[fund_has_been_delisted] +description = "很抱歉,基金已下架,暂时无法申购" +one = "很抱歉,基金已下架,暂时无法申购" +other = "很抱歉,基金已下架,暂时无法申购" +[subscription_failed_over_purchased] +description = "申购失败,购买超额。基金当前剩余申购额度为:" +one = "申购失败,购买超额。基金当前剩余申购额度为:" +other = "申购失败,购买超额。基金当前剩余申购额度为:" +[minimum_purchase_amount] +description = "申购金额最低" +one = "申购金额最低" +other = "申购金额最低" +[rise] +description = "起" +one = "起" +other = "起" +[hongkong_account_is_insufficient] +description = "港股账户余额不足,请先入金" +one = "港股账户余额不足,请先入金" +other = "港股账户余额不足,请先入金" +[us_account_is_insufficient] +description = "美股账户余额不足,请先入金" +one = "美股账户余额不足,请先入金" +other = "美股账户余额不足,请先入金" +[usdt_account_is_insufficient] +description = "USDT账户余额不足,请先入金" +one = "USDT账户余额不足,请先入金" +other = "USDT账户余额不足,请先入金" +[format_account_is_insufficient] +description = "账户余额不足,请先入金" +one = "{{.CatalogCurrency}} 账户余额不足,请先入金" +other = "{{.CatalogCurrency}} 账户余额不足,请先入金" +[subscription_deadline_has_passed] +description = "当前已超过认购截止时间!" +one = "当前已超过认购截止时间!" +other = "当前已超过认购截止时间!" +[have_a_subscription_record] +description = "您已有该新股的申购记录,不能重复申请" +one = "您已有该新股的申购记录,不能重复申请" +other = "您已有该新股的申购记录,不能重复申请" +[hongkong_stock_account_not_be_less_than] +description = "融资申购要求港股账户余额不低于" +one = "融资申购要求港股账户余额不低于" +other = "融资申购要求港股账户余额不低于" +[us_stock_account_not_be_less_than] +description = "融资申购要求美股账户余额不低于" +one = "融资申购要求美股账户余额不低于" +other = "融资申购要求美股账户余额不低于" +[please_deposit_first] +description = ",请先入金!" +one = ",请先入金!" +other = ",请先入金!" +[incorrect_amount_retry] +description = "金额错误,请重试" +one = "金额错误,请重试" +other = "金额错误,请重试" +[have_an_entrustment_record] +description = "您已有该新股的委托记录,不能重复委托" +one = "您已有该新股的委托记录,不能重复委托" +other = "您已有该新股的委托记录,不能重复委托" +[currently_only_supports_conversion_hkd] +description = "目前只支持转为港币 HKD $" +one = "目前只支持转为港币 HKD $" +other = "目前只支持转为港币 HKD $" +[insufficient_account_balance] +description = "账户余额不足,转换失败" +one = "账户余额不足,转换失败" +other = "账户余额不足,转换失败" +[record_is_not_cancel] +description = "该记录当前已不允许取消" +one = "该记录当前已不允许取消" +other = "该记录当前已不允许取消" +[order_needs_3_minutes_before] +description = "市价单需要下单3分钟后才能撤单" +one = "市价单需要下单3分钟后才能撤单" +other = "市价单需要下单3分钟后才能撤单" +[order_is_not_cancel] +description = "该订单当前不支持撤单" +one = "该订单当前不支持撤单" +other = "该订单当前不支持撤单" +[order_failed_retry] +description = "下单失败,请重试" +one = "下单失败,请重试" +other = "下单失败,请重试" +[market_closed_orders_cannot_be_placed] +description = "当前市场休市中,暂不能下单" +one = "当前市场休市中,暂不能下单" +other = "当前市场休市中,暂不能下单" +[incorrect_security_code_retry] +description = "证券代码错误,请重试" +one = "证券代码错误,请重试" +other = "证券代码错误,请重试" +[stock] +description = "股票" +one = "股票" +other = "股票" +[order_failed_odd_lot_trading] +description = "下单失败,不支持碎股交易" +one = "下单失败,不支持碎股交易" +other = "下单失败,不支持碎股交易" +[withfunding_asset_total_must_large_than_order_total] +description = "下单失败,操盘资金必须大于下单金额" +one = "下单失败,操盘资金必须大于下单金额" +other = "下单失败,操盘资金必须大于下单金额" +[order_failed_price_cannot_be_lower_than] +description = "下单失败,价格不能低于" +one = "下单失败,价格不能低于" +other = "下单失败,价格不能低于" +[order_failed_price_cannot_be_higher_than] +description = "下单失败,价格不能高于" +one = "下单失败,价格不能高于" +other = "下单失败,价格不能高于" +[order_failed_price_is_incorrect] +description = "下单失败,价格不正确" +one = "下单失败,价格不正确" +other = "下单失败,价格不正确" +[stock_insufficient] +description = "您当前未持仓该股票或持仓数量不足,无法卖出" +one = "您当前未持仓该股票或持仓数量不足,无法卖出" +other = "您当前未持仓该股票或持仓数量不足,无法卖出" +[current_position_of_the_stock] +description = "股票当前持仓数量" +one = "股票当前持仓数量" +other = "股票当前持仓数量" +[holding_amount_is_insufficient] +description = ",持有数量不足,无法卖出" +one = ",持有数量不足,无法卖出" +other = ",持有数量不足,无法卖出" +[maximum_number_of_sells] +description = "允许卖出数量最多为" +one = "允许卖出数量最多为" +other = "允许卖出数量最多为" +[stocks_do_not_support_grey_market_trading] +description = "股票不支持暗盘交易" +one = "股票不支持暗盘交易" +other = "股票不支持暗盘交易" +[non_grey_market_trading_time] +description = "当前为非暗盘交易时间,下单失败" +one = "当前为非暗盘交易时间,下单失败" +other = "当前为非暗盘交易时间,下单失败" +[pretrade_not_allow_market_order] +description = "盘前交易不支持市价单,请更换重试" +one = "盘前交易不支持市价单,请更换重试" +other = "盘前交易不支持市价单,请更换重试" + +[text_required] +description = "数据有误" +one = "数据有误" +other = "数据有误" + +[days_ago] +description = " 天前" +one = " 天前" +other = " 天前" +[hours_ago] +description = " 小时前" +one = " 小时前" +other = " 小时前" +[minutes_ago] +description = " 分钟前" +one = " 分钟前" +other = " 分钟前" + +[balance_role_1] +description = "股票买入" +one = "股票买入" +other = "股票买入" +[balance_role_2] +description = "买入手续费" +one = "买入手续费" +other = "买入手续费" +[balance_role_3] +description = "股票卖出" +one = "股票卖出" +other = "股票卖出" +[balance_role_4] +description = "卖出手续费" +one = "卖出手续费" +other = "卖出手续费" +[balance_role_5] +description = "新股申购" +one = "新股申购" +other = "新股申购" +[balance_role_6] +description = "新股融资利息" +one = "新股融资利息" +other = "新股融资利息" +[balance_role_7] +description = "新股申购手续费" +one = "新股申购手续费" +other = "新股申购手续费" +[balance_role_8] +description = "新股中签手续费" +one = "新股中签手续费" +other = "新股中签手续费" +[balance_role_9] +description = "国配意向金" +one = "国配意向金" +other = "国配意向金" +[balance_role_10] +description = "平台分成" +one = "平台分成" +other = "平台分成" +[balance_role_11] +description = "出金" +one = "出金" +other = "出金" +[balance_role_12] +description = "入金" +one = "入金" +other = "入金" +[balance_role_13] +description = "基金申购金额" +one = "基金申购金额" +other = "基金申购金额" +[balance_role_14] +description = "基金申购费" +one = "基金申购费" +other = "基金申购费" +[balance_role_15] +description = "基金管理费" +one = "基金管理费" +other = "基金管理费" +[balance_role_16] +description = "资金补扣" +one = "资金补扣" +other = "资金补扣" +[balance_role_17] +description = "资金退还" +one = "资金退还" +other = "资金退还" +[balance_role_18] +description = "基金结算获利" +one = "基金结算获利" +other = "基金结算获利" +[balance_role_19] +description = "虚拟加金" +one = "虚拟加金" +other = "虚拟加金" +[balance_role_20] +description = "手动入金" +one = "手动入金" +other = "手动入金" +[balance_role_21] +description = "转移资金" +one = "转移资金" +other = "转移资金" +[balance_role_22] +description = "推广佣金" +one = "推广佣金" +other = "推广佣金" +[balance_role_23] +description = "基金超额管理费" +one = "基金超额管理费" +other = "基金超额管理费" +[balance_role_24] +description = "货币转换" +one = "货币转换" +other = "货币转换" +[balance_role_25] +description = "配资投资本金" +one = "配资投资本金" +other = "配资投资本金" +[balance_role_26] +description = "配资利息" +one = "配资利息" +other = "配资利息" +[balance_role_28] +description = "赠金" +one = "赠金" +other = "赠金" +[balance_return] +description = "(退回)" +one = "(退回)" +other = "(退回)" + +[day] +description = "日" +one = "日" +other = "日" +[week] +description = "周" +one = "周" +other = "周" +[month] +description = "月" +one = "月" +other = "月" +[fund_order_status_1] +description = "已申购" +one = "已申购" +other = "已申购" +[fund_order_status_2] +description = "已取消" +one = "已取消" +other = "已取消" +[fund_order_status_3] +description = "已结算" +one = "已结算" +other = "已结算" + +[withfunding_must_all_clear] +description = "下单失败,配资必须全仓卖出" +one = "下单失败,配资必须全仓卖出" +other = "下单失败,配资必须全仓卖出" + +[sell_success_title] +description = "卖出交易已成交" +one = "卖出交易已成交" +other = "卖出交易已成交" +[sell_success_content_1] +description = "您的交易 " +one = "您的交易 " +other = "您的交易 " +[sell_success_content_2] +description = " 卖出 " +one = " 卖出 " +other = " 卖出 " +[sell_success_content_3] +description = " 已成交" +one = " 已成交" +other = " 已成交" +[buy_success_title] +description = "买入交易已成交" +one = "买入交易已成交" +other = "买入交易已成交" +[buy_success_content_1] +description = "您的交易 " +one = "您的交易 " +other = "您的交易 " +[buy_success_content_2] +description = " 买入 " +one = " 买入 " +other = " 买入 " +[buy_success_content_3] +description = " 已成交" +one = " 已成交" +other = " 已成交" + +[sms_verify_code_content_1] +description = "你的短讯验证码为:" +one = "你的短讯验证码为:" +other = "你的短讯验证码为:" + +[email_exchange_title] +description = "资金变动提示" +one = "资金变动提示" +other = "资金变动提示" + +[currency_exchange_status_1] +description = "待处理" +one = "待处理" +other = "待处理" +[currency_exchange_status_2] +description = "转换成功" +one = "转换成功" +other = "转换成功" +[currency_exchange_status_3] +description = "转换失败" +one = "转换失败" +other = "转换失败" +[currency_exchange_status_4] +description = "已取消" +one = "已取消" +other = "已取消" + +[to_be_disclosed] +description = "待公布" +one = "待公布" +other = "待公布" + +[ipo_times] +description = " 倍" +one = " 倍" +other = " 倍" + +[ipo_cash_commission] +description = "现金委托" +one = "现金委托" +other = "现金委托" +[password_match_error_origin] +description = "原始密码不一致" +one = "原始密码不一致" +other = "原始密码不一致" +[ipo_financing_10_times] +description = "融资10倍" +one = "融资10倍" +other = "融资10倍" +[ipo_financing_20_times] +description = "融资20倍" +one = "融资20倍" +other = "融资20倍" +[other] +description = "其他" +one = "其他" +other = "其他" +[max] +description = "最大" +one = "最大" +other = "最大" + +[the_maximum_quantity_is_limited_to_10_digits] +description = "数量最大限制10位数以内" +one = "数量最大限制10位数以内" +other = "数量最大限制10位数以内" + +[waiting_to_open] +description = "待开盘" +one = "待开盘" +other = "待开盘" +[the_market_is_closed_on_weekends] +description = "周末休市中" +one = "周末休市中" +other = "周末休市中" +[the_market_is_closed] +description = "休市中" +one = "休市中" +other = "休市中" +[in_transaction] +description = "交易中" +one = "交易中" +other = "交易中" +[default_trading_time_period] +description = "默认交易时间段" +one = "默认交易时间段" +other = "默认交易时间段" +[non_default_trading_time_period] +description = "非默认交易时间段" +one = "非默认交易时间段" +other = "非默认交易时间段" +[all_day_transaction] +description = "全天交易" +one = "全天交易" +other = "全天交易" + +# 新增规范翻译 + +# ["账号未注册"] +# description = "账号未注册" +# one = "账号未注册" +# other = "账号未注册" diff --git a/translator/zh_hk.toml b/translator/zh_hk.toml new file mode 100644 index 0000000..e69de29 diff --git a/utils/basic/lang.go b/utils/basic/lang.go new file mode 100644 index 0000000..3b74c0a --- /dev/null +++ b/utils/basic/lang.go @@ -0,0 +1,27 @@ +package basic + +import "reflect" + +func GetLangString(param any) string { + // 获取参数的反射值 + value := reflect.ValueOf(param) + + // 如果参数是指针,则获取指针指向的值 + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + // 如果参数是结构体 + if value.Kind() == reflect.Struct { + // 通过字段名获取字段值 + langField := value.FieldByName("Lang") + + // 如果字段存在并且是字符串类型 + if langField.IsValid() && langField.Kind() == reflect.String { + return langField.String() + } + } + + // 如果无法获取有效的字符串值,则返回zh_cn + return "zh_cn" +} diff --git a/utils/basic/types.go b/utils/basic/types.go index 1f19ffa..ee835cf 100644 --- a/utils/basic/types.go +++ b/utils/basic/types.go @@ -1,6 +1,9 @@ package basic -import "github.com/iapologizewhenimwrong/Vestmore_GO/utils/log" +import ( + "github.com/iapologizewhenimwrong/Vestmore_GO/translator" + "github.com/iapologizewhenimwrong/Vestmore_GO/utils/log" +) // 全局返回的结构体 type Response struct { @@ -10,6 +13,10 @@ type Response struct { IsSuccess bool `json:"success"` } +type ILocalize interface { + Localize(MessageID translator.TrCode) (string, error) +} + func (resp *Response) Error(errcode *ErrorCode, Data ...interface{}) *Response { if resp == nil { resp = &Response{} @@ -46,6 +53,20 @@ func (resp *Response) ErrorMsg(Code int, Message string, Data ...interface{}) *R return resp } +// 实现翻译返回错误的代码 +func (resp *Response) ErrorTrCode(il ILocalize, trcode translator.TrCode, Data ...interface{}) *Response { + if resp == nil { + resp = &Response{} + } + + trStr, err := il.Localize(trcode) + if err != nil { + return resp.ErrorErr(1, err) + } + + return resp.ErrorMsg(1, trStr) +} + func (resp *Response) Success(Data ...interface{}) *Response { if resp == nil { resp = &Response{}