diff --git a/etc/dict.json b/etc/dict.json new file mode 100644 index 00000000..55278f0c --- /dev/null +++ b/etc/dict.json @@ -0,0 +1,63 @@ +{ + "zh": { + "stra not found": "聚合策略为找到", + "unauthorized": "用户未授权", + "same stra name %s in node": "同节点下策略名称 %s 已存在", + "collect type not support": "采集类型不合法", + "[%s] is blank": "参数[%s]值不能为空", + "cannot convert %s to int64": "%s 无法转为 int64 类型", + "cannot convert %s to int": "%s 无法转为 int 类型", + "arg[%s] not found": "参数[%s]没找到", + "cannot retrieve node[%d]: %v": "获取不到节点[%d],原因:%v", + "no such node[%d]": "节点[%d]不存在", + "no such task[id:%d]": "任务[%d]不存在", + "no such task tpl[id:%d]": "任务模板[%d]不存在", + "cannot retrieve screen[%d]: %v": "获取不到大盘[%d],原因:%v", + "no such screen[%d]": "大盘[%d]不存在", + "cannot retrieve subclass[%d]: %v": "获取不到大盘分组[%d],原因:%v", + "no such subclass[%d]": "大盘分组[%d]不存在", + "cannot retrieve chart[%d]: %v": "获取不到大盘图表[%d],原因:%v", + "no such chart[%d]": "大盘图表[%d]不存在", + "cannot retrieve eventCur[%d]: %v": "获取不到未恢复告警事件[%d],原因:%v", + "no such eventCur[%d]": "未恢复告警事件[%d]不存在", + "cannot retrieve event[%d]: %v": "获取不到告警事件[%d],原因:%v", + "no such event[%d]": "告警事件[%d]不存在", + "cannot retrieve user[%d]: %v": "获取不到用户[%d],原因:%v", + "no such user[%d]": "用户[%d]不存在", + "no such user: %s": "用户[%s]不存在", + "cannot retrieve team[%d]: %v": "获取不到团队[%d],原因:%v", + "no such team[%d]": "团队[%d]不存在", + "cannot retrieve role[%d]: %v": "获取不到角色[%d],原因:%v", + "no such role[%d]": "角色[%d]不存在", + "no such NodeCate[id:%d]": "节点类型[%d]没找到", + "no such field": "扩展字段为找到", + "field_type cannot modify": "字段类型不能被修改", + "arg[endpoints] empty": "参数不能[endpoints]为空", + "arg[cur_nid_paths] empty": "参数不能[cur_nid_paths]为空", + "arg[tags] empty": "参数不能[tags]为空", + "arg[hosts] empty": "参数不能[hosts]为空", + "arg[btime,etime] empty": "参数[btime,etime]不合规范", + "arg[name] empty": "参数[name]不合规范", + "arg[name] is blank": "参数[名称]不能为空", + "arg[ids] is empty": "参数[ids]不能为空", + "%s invalid": "%s 不符合规范", + "%s too long > 64": "%s 超过64长度限制", + "arg[%s] too long > %d": "参数 %s 长度不能超过 %d", + "cate is blank": "节点分类不能为空", + "uuid is blank": "uuid不能为空", + "ident is blank": "唯一标识不能为空", + "tenant is blank": "租户不能为空", + "ids is blank": "ids不能为空", + "items empty": "提交内容不能为空", + "url param[%s] is blank": "url参数[%s]不能为空", + "query param[%s] is necessary": "query参数[%s]不能为空", + "ident legal characters: [a-z0-9_-]": "唯一标识英文只能字母开头,包括数字、中划线、下划线", + "ident length should be less than 32": "唯一标识长度需小于32", + "cannot modify tenant's node-category": "租户分类不允许修改", + "cannot modify node-category to tenant": "节点分类不允许修改为租户", + "node is managed by other system": "租户正在被系统系统使用", + "resources not found by %s": "通过 %s 没有找到资源", + "cannot delete root user": "root用户不能删除", + "user not found": "用户未找到" + } +} \ No newline at end of file diff --git a/go.mod b/go.mod index 2154310c..36a53eda 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( go.uber.org/automaxprocs v1.3.0 // indirect golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d + golang.org/x/text v0.3.3 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ldap.v3 v3.1.0 diff --git a/src/modules/ams/ams.go b/src/modules/ams/ams.go index c883ca85..523b8937 100644 --- a/src/modules/ams/ams.go +++ b/src/modules/ams/ams.go @@ -16,6 +16,7 @@ import ( "github.com/didi/nightingale/src/models" "github.com/didi/nightingale/src/modules/ams/config" "github.com/didi/nightingale/src/modules/ams/http" + "github.com/didi/nightingale/src/toolkits/i18n" ) var ( @@ -42,6 +43,8 @@ func init() { os.Exit(0) } + i18n.Init() + runner.Init() fmt.Println("runner.cwd:", runner.Cwd) fmt.Println("runner.hostname:", runner.Hostname) @@ -55,6 +58,8 @@ func main() { // 初始化数据库和相关数据 models.InitMySQL("rdb", "ams") + i18n.Init(config.Config.I18n) + http.Start() endingProc() diff --git a/src/modules/ams/config/yaml.go b/src/modules/ams/config/yaml.go index 4658aad2..b994c9ce 100644 --- a/src/modules/ams/config/yaml.go +++ b/src/modules/ams/config/yaml.go @@ -4,13 +4,16 @@ import ( "fmt" "github.com/didi/nightingale/src/common/loggeri" + "github.com/didi/nightingale/src/toolkits/i18n" + "github.com/toolkits/pkg/file" ) type ConfigT struct { - Logger loggeri.Config `yaml:"logger"` - HTTP httpSection `yaml:"http"` - Tokens []string `yaml:"tokens"` + Logger loggeri.Config `yaml:"logger"` + HTTP httpSection `yaml:"http"` + Tokens []string `yaml:"tokens"` + I18n i18n.I18nSection `yaml:"i18n"` } type httpSection struct { @@ -35,6 +38,14 @@ func Parse() error { } Config = &c + if Config.I18n.DictPath == "" { + Config.I18n.DictPath = "etc/dict.json" + } + + if Config.I18n.Lang == "" { + Config.I18n.Lang = "zh" + } + fmt.Println("config.file:", ymlFile) return nil diff --git a/src/modules/ams/http/router_funcs.go b/src/modules/ams/http/router_funcs.go index 46cfad61..6d4dc8d3 100644 --- a/src/modules/ams/http/router_funcs.go +++ b/src/modules/ams/http/router_funcs.go @@ -7,6 +7,7 @@ import ( "github.com/toolkits/pkg/errors" "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/toolkits/i18n" ) func dangerous(v interface{}) { @@ -14,7 +15,7 @@ func dangerous(v interface{}) { } func bomb(format string, a ...interface{}) { - errors.Bomb(format, a...) + errors.Bomb(i18n.Sprintf(format, a...)) } func bind(c *gin.Context, ptr interface{}) { diff --git a/src/modules/ams/http/router_host_field.go b/src/modules/ams/http/router_host_field.go index 8409fe4a..5b7d3320 100644 --- a/src/modules/ams/http/router_host_field.go +++ b/src/modules/ams/http/router_host_field.go @@ -13,15 +13,15 @@ func hostFieldNew(c *gin.Context) { bind(c, &obj) if obj.FieldIdent == "" { - bomb("field_ident is blank") + bomb("[%s] is blank", "field_ident") } if obj.FieldName == "" { - bomb("field_name is blank") + bomb("[%s] is blank", "field_name") } if obj.FieldType == "" { - bomb("field_type is blank") + bomb("[%s] is blank", "field_type") } if obj.FieldCate == "" { diff --git a/src/modules/job/config/config.go b/src/modules/job/config/config.go index de70309e..f8067a5f 100644 --- a/src/modules/job/config/config.go +++ b/src/modules/job/config/config.go @@ -7,13 +7,15 @@ import ( "github.com/didi/nightingale/src/common/identity" "github.com/didi/nightingale/src/common/loggeri" + "github.com/didi/nightingale/src/toolkits/i18n" ) type ConfigT struct { - Logger loggeri.Config `yaml:"logger"` - HTTP httpSection `yaml:"http"` - Tokens []string `yaml:"tokens"` - Output outputSection `yaml:"output"` + Logger loggeri.Config `yaml:"logger"` + HTTP httpSection `yaml:"http"` + Tokens []string `yaml:"tokens"` + Output outputSection `yaml:"output"` + I18n i18n.I18nSection `yaml:"i18n"` } type httpSection struct { @@ -43,6 +45,15 @@ func Parse() error { } Config = &c + + if Config.I18n.DictPath == "" { + Config.I18n.DictPath = "etc/dict.json" + } + + if Config.I18n.Lang == "" { + Config.I18n.Lang = "zh" + } + fmt.Println("config.file:", ymlFile) return identity.Parse() diff --git a/src/modules/job/http/router_funcs.go b/src/modules/job/http/router_funcs.go index db72e396..82ed7919 100644 --- a/src/modules/job/http/router_funcs.go +++ b/src/modules/job/http/router_funcs.go @@ -8,6 +8,7 @@ import ( "github.com/toolkits/pkg/errors" "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/toolkits/i18n" ) func dangerous(v interface{}) { @@ -15,9 +16,8 @@ func dangerous(v interface{}) { } func bomb(format string, a ...interface{}) { - errors.Bomb(format, a...) + errors.Bomb(i18n.Sprintf(format, a...)) } - func bind(c *gin.Context, ptr interface{}) { dangerous(c.ShouldBindJSON(ptr)) } @@ -212,7 +212,7 @@ func Node(id int64) *models.Node { dangerous(err) if node == nil { - bomb("no such node[id:%d]", id) + bomb("no such node[%d]", id) } return node diff --git a/src/modules/job/job.go b/src/modules/job/job.go index 78391bb3..a98d90d1 100644 --- a/src/modules/job/job.go +++ b/src/modules/job/job.go @@ -19,6 +19,7 @@ import ( "github.com/didi/nightingale/src/modules/job/http" "github.com/didi/nightingale/src/modules/job/rpc" "github.com/didi/nightingale/src/modules/job/timer" + "github.com/didi/nightingale/src/toolkits/i18n" ) var ( @@ -82,6 +83,8 @@ func main() { // 将task_host_doing表缓存到内存里,减少DB压力 timer.CacheHostDoing() + i18n.Init(config.Config.I18n) + go rpc.Start() http.Start() diff --git a/src/modules/monapi/config/yaml.go b/src/modules/monapi/config/yaml.go index dc88a691..55b2d36f 100644 --- a/src/modules/monapi/config/yaml.go +++ b/src/modules/monapi/config/yaml.go @@ -5,6 +5,8 @@ import ( "fmt" "sync" + "github.com/didi/nightingale/src/toolkits/i18n" + "github.com/spf13/viper" "github.com/toolkits/pkg/file" ) @@ -26,6 +28,7 @@ type ConfYaml struct { Notify map[string][]string `yaml:"notify"` Link linkSection `yaml:"link"` IndexMod string `yaml:"indexMod"` + I18n i18n.I18nSection `yaml:"i18n"` } type mergeSection struct { @@ -141,6 +144,9 @@ func Parse(ymlfile string) error { viper.SetDefault("habits.identity", "ip") + viper.SetDefault("i18n.dictPath", "etc/dict.json") + viper.SetDefault("i18n.lang", "zh") + viper.SetDefault("redis.idle", 5) viper.SetDefault("redis.timeout", map[string]int{ "conn": 500, diff --git a/src/modules/monapi/http/http_middlewre.go b/src/modules/monapi/http/http_middlewre.go index 45132659..3048f05c 100644 --- a/src/modules/monapi/http/http_middlewre.go +++ b/src/modules/monapi/http/http_middlewre.go @@ -21,7 +21,7 @@ func GetCookieUser() gin.HandlerFunc { } if username == "" { - errors.Bomb("unauthorized") + bomb("unauthorized") } c.Set("username", username) @@ -61,7 +61,7 @@ func CheckHeaderToken() gin.HandlerFunc { return func(c *gin.Context) { token := c.GetHeader("X-Srv-Token") if token != internalToken && !slice.ContainsString(config.Get().Tokens, token) { - errors.Bomb("token[%s] invalid", token) + bomb("token[%s] invalid", token) } c.Next() } diff --git a/src/modules/monapi/http/router_aggr.go b/src/modules/monapi/http/router_aggr.go index aa83166f..c513876e 100644 --- a/src/modules/monapi/http/router_aggr.go +++ b/src/modules/monapi/http/router_aggr.go @@ -16,7 +16,7 @@ func aggrCalcPost(c *gin.Context) { can, err := models.UsernameCandoNodeOp(username, "mon_aggr_write", stra.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } stra.Creator = username @@ -26,7 +26,7 @@ func aggrCalcPost(c *gin.Context) { oldStra, _ := models.AggrCalcGet("nid=? and new_metric=?", stra.Nid, stra.NewMetric) if oldStra != nil { - errors.Bomb("同节点下指标计算 新指标名称 %s 已存在", stra.NewMetric) + bomb("同节点下指标计算 新指标名称 %s 已存在", stra.NewMetric) } err = stra.Save() @@ -42,7 +42,7 @@ func aggrCalcPut(c *gin.Context) { can, err := models.UsernameCandoNodeOp(username, "mon_aggr_write", stra.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } stra.LastUpdator = username @@ -50,7 +50,7 @@ func aggrCalcPut(c *gin.Context) { oldStra, _ := models.AggrCalcGet("nid=? and new_metric=?", stra.Nid, stra.NewMetric) if oldStra != nil && oldStra.Id != stra.Id { - errors.Bomb("同节点下指标计算 新指标名称 %s 已存在", stra.NewMetric) + bomb("同节点下指标计算 新指标名称 %s 已存在", stra.NewMetric) } err = stra.Update("new_metric", "new_step", "groupby", "raw_metrics", "global_operator", @@ -80,7 +80,7 @@ func aggrCalcsDel(c *gin.Context) { can, err := models.UsernameCandoNodeOp(username, "mon_aggr_write", stra.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } } @@ -97,7 +97,7 @@ func aggrCalcGet(c *gin.Context) { stra, err := models.AggrCalcGet("id=?", id) errors.Dangerous(err) if stra == nil { - errors.Bomb("stra not found") + bomb("stra not found") } err = stra.Decode() diff --git a/src/modules/monapi/http/router_chart.go b/src/modules/monapi/http/router_chart.go index 065a42fc..998a0ebd 100644 --- a/src/modules/monapi/http/router_chart.go +++ b/src/modules/monapi/http/router_chart.go @@ -23,7 +23,7 @@ func chartPost(c *gin.Context) { can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_screen_write", screen.NodeId) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } chart := models.Chart{ @@ -55,7 +55,7 @@ func chartPut(c *gin.Context) { can, err := canWriteChart(f.SubclassId, loginUsername(c)) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } chart.Configs = f.Configs @@ -81,7 +81,7 @@ func chartWeightsPut(c *gin.Context) { can, err := canWriteChart(chart.SubclassId, loginUsername(c)) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } } @@ -99,7 +99,7 @@ func chartDel(c *gin.Context) { can, err := canWriteChart(chart.SubclassId, loginUsername(c)) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } errors.Dangerous(chart.Del()) diff --git a/src/modules/monapi/http/router_collect.go b/src/modules/monapi/http/router_collect.go index d5d8606c..556d550f 100644 --- a/src/modules/monapi/http/router_collect.go +++ b/src/modules/monapi/http/router_collect.go @@ -33,18 +33,18 @@ func collectPost(c *gin.Context) { b, err := json.Marshal(obj.Data) if err != nil { - errors.Bomb("marshal body %s err:%v", obj, err) + bomb("marshal body %s err:%v", obj, err) } err = json.Unmarshal(b, collect) if err != nil { - errors.Bomb("unmarshal body %s err:%v", string(b), err) + bomb("unmarshal body %s err:%v", string(b), err) } can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_create", collect.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } collect.Creator = creator @@ -56,7 +56,7 @@ func collectPost(c *gin.Context) { old, err := models.GetCollectByNameAndNid(obj.Type, name, nid) errors.Dangerous(err) if old != nil { - errors.Bomb("同节点下策略名称 %s 已存在", name) + bomb("same stra name %s in node", name) } errors.Dangerous(models.CreateCollect(obj.Type, creator, collect)) @@ -65,18 +65,18 @@ func collectPost(c *gin.Context) { b, err := json.Marshal(obj.Data) if err != nil { - errors.Bomb("marshal body %s err:%v", obj, err) + bomb("marshal body %s err:%v", obj, err) } err = json.Unmarshal(b, collect) if err != nil { - errors.Bomb("unmarshal body %s err:%v", string(b), err) + bomb("unmarshal body %s err:%v", string(b), err) } can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_create", collect.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } collect.Creator = creator @@ -88,7 +88,7 @@ func collectPost(c *gin.Context) { old, err := models.GetCollectByNameAndNid(obj.Type, name, nid) errors.Dangerous(err) if old != nil { - errors.Bomb("同节点下策略名称 %s 已存在", name) + bomb("same stra name %s in node", name) } errors.Dangerous(models.CreateCollect(obj.Type, creator, collect)) case "log": @@ -96,18 +96,18 @@ func collectPost(c *gin.Context) { b, err := json.Marshal(obj.Data) if err != nil { - errors.Bomb("marshal body %s err:%v", obj, err) + bomb("marshal body %s err:%v", obj, err) } err = json.Unmarshal(b, collect) if err != nil { - errors.Bomb("unmarshal body %s err:%v", string(b), err) + bomb("unmarshal body %s err:%v", string(b), err) } can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_create", collect.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } collect.Encode() @@ -120,7 +120,7 @@ func collectPost(c *gin.Context) { old, err := models.GetCollectByNameAndNid(obj.Type, name, nid) errors.Dangerous(err) if old != nil { - errors.Bomb("同节点下策略名称 %s 已存在", name) + bomb("same stra name %s in node", name) } errors.Dangerous(models.CreateCollect(obj.Type, creator, collect)) case "plugin": @@ -128,18 +128,18 @@ func collectPost(c *gin.Context) { b, err := json.Marshal(obj.Data) if err != nil { - errors.Bomb("marshal body %s err:%v", obj, err) + bomb("marshal body %s err:%v", obj, err) } err = json.Unmarshal(b, collect) if err != nil { - errors.Bomb("unmarshal body %s err:%v", string(b), err) + bomb("unmarshal body %s err:%v", string(b), err) } can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_create", collect.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } collect.Creator = creator @@ -151,7 +151,7 @@ func collectPost(c *gin.Context) { old, err := models.GetCollectByNameAndNid(obj.Type, name, nid) errors.Dangerous(err) if old != nil { - errors.Bomb("同节点下策略名称 %s 已存在", name) + bomb("same stra name %s in node", name) } errors.Dangerous(models.CreateCollect(obj.Type, creator, collect)) @@ -160,18 +160,18 @@ func collectPost(c *gin.Context) { b, err := json.Marshal(obj.Data) if err != nil { - errors.Bomb("marshal body %s err:%v", obj, err) + bomb("marshal body %s err:%v", obj, err) } err = json.Unmarshal(b, collect) if err != nil { - errors.Bomb("unmarshal body %s err:%v", string(b), err) + bomb("unmarshal body %s err:%v", string(b), err) } can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_create", collect.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } collect.Encode() @@ -184,12 +184,12 @@ func collectPost(c *gin.Context) { old, err := models.GetCollectByNameAndNid(obj.Type, name, nid) errors.Dangerous(err) if old != nil { - errors.Bomb("同节点下策略名称 %s 已存在", name) + bomb("same stra name %s in node", name) } errors.Dangerous(models.CreateCollect(obj.Type, creator, collect)) default: - errors.Bomb("采集类型不合法") + bomb("collect type not support") } } @@ -258,18 +258,18 @@ func collectPut(c *gin.Context) { b, err := json.Marshal(recv.Data) if err != nil { - errors.Bomb("marshal body %s err:%v", recv, err) + bomb("marshal body %s err:%v", recv, err) } err = json.Unmarshal(b, collect) if err != nil { - errors.Bomb("unmarshal body %s err:%v", string(b), err) + bomb("unmarshal body %s err:%v", string(b), err) } can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_modify", collect.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } nid := collect.Nid @@ -278,12 +278,12 @@ func collectPut(c *gin.Context) { //校验采集是否存在 obj, err := models.GetCollectById(recv.Type, collect.Id) //id找不到的情况 if err != nil { - errors.Bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) + bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) } tmpId := obj.(*models.PortCollect).Id if tmpId == 0 { - errors.Bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) + bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) } collect.Creator = creator @@ -292,7 +292,7 @@ func collectPut(c *gin.Context) { old, err := models.GetCollectByNameAndNid(recv.Type, name, nid) errors.Dangerous(err) if old != nil && tmpId != old.(*models.PortCollect).Id { - errors.Bomb("同节点下策略名称 %s 已存在", name) + bomb("same stra name %s in node", name) } errors.Dangerous(collect.Update()) @@ -303,18 +303,18 @@ func collectPut(c *gin.Context) { b, err := json.Marshal(recv.Data) if err != nil { - errors.Bomb("marshal body %s err:%v", recv, err) + bomb("marshal body %s err:%v", recv, err) } err = json.Unmarshal(b, collect) if err != nil { - errors.Bomb("unmarshal body %s err:%v", string(b), err) + bomb("unmarshal body %s err:%v", string(b), err) } can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_modify", collect.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } nid := collect.Nid @@ -323,18 +323,18 @@ func collectPut(c *gin.Context) { //校验采集是否存在 obj, err := models.GetCollectById(recv.Type, collect.Id) //id找不到的情况 if err != nil { - errors.Bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) + bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) } tmpId := obj.(*models.ProcCollect).Id if tmpId == 0 { - errors.Bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) + bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) } can, err = models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_modify", collect.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } collect.Creator = creator @@ -343,7 +343,7 @@ func collectPut(c *gin.Context) { old, err := models.GetCollectByNameAndNid(recv.Type, name, nid) errors.Dangerous(err) if old != nil && tmpId != old.(*models.ProcCollect).Id { - errors.Bomb("同节点下策略名称 %s 已存在", name) + bomb("same stra name %s in node", name) } errors.Dangerous(collect.Update()) @@ -354,18 +354,18 @@ func collectPut(c *gin.Context) { b, err := json.Marshal(recv.Data) if err != nil { - errors.Bomb("marshal body %s err:%v", recv, err) + bomb("marshal body %s err:%v", recv, err) } err = json.Unmarshal(b, collect) if err != nil { - errors.Bomb("unmarshal body %s err:%v", string(b), err) + bomb("unmarshal body %s err:%v", string(b), err) } can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_modify", collect.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } collect.Encode() @@ -375,12 +375,12 @@ func collectPut(c *gin.Context) { //校验采集是否存在 obj, err := models.GetCollectById(recv.Type, collect.Id) //id找不到的情况 if err != nil { - errors.Bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) + bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) } tmpId := obj.(*models.LogCollect).Id if tmpId == 0 { - errors.Bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) + bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) } collect.Creator = creator @@ -389,7 +389,7 @@ func collectPut(c *gin.Context) { old, err := models.GetCollectByNameAndNid(recv.Type, name, nid) errors.Dangerous(err) if old != nil && tmpId != old.(*models.LogCollect).Id { - errors.Bomb("同节点下策略名称 %s 已存在", name) + bomb("same stra name %s in node", name) } errors.Dangerous(collect.Update()) @@ -400,18 +400,18 @@ func collectPut(c *gin.Context) { b, err := json.Marshal(recv.Data) if err != nil { - errors.Bomb("marshal body %s err:%v", recv, err) + bomb("marshal body %s err:%v", recv, err) } err = json.Unmarshal(b, collect) if err != nil { - errors.Bomb("unmarshal body %s err:%v", string(b), err) + bomb("unmarshal body %s err:%v", string(b), err) } can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_modify", collect.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } nid := collect.Nid @@ -420,12 +420,12 @@ func collectPut(c *gin.Context) { //校验采集是否存在 obj, err := models.GetCollectById(recv.Type, collect.Id) //id找不到的情况 if err != nil { - errors.Bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) + bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) } tmpId := obj.(*models.PluginCollect).Id if tmpId == 0 { - errors.Bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) + bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) } collect.Creator = creator @@ -434,7 +434,7 @@ func collectPut(c *gin.Context) { old, err := models.GetCollectByNameAndNid(recv.Type, name, nid) errors.Dangerous(err) if old != nil && tmpId != old.(*models.PluginCollect).Id { - errors.Bomb("同节点下策略名称 %s 已存在", name) + bomb("same stra name %s in node", name) } errors.Dangerous(collect.Update()) @@ -445,18 +445,18 @@ func collectPut(c *gin.Context) { b, err := json.Marshal(recv.Data) if err != nil { - errors.Bomb("marshal body %s err:%v", recv, err) + bomb("marshal body %s err:%v", recv, err) } err = json.Unmarshal(b, collect) if err != nil { - errors.Bomb("unmarshal body %s err:%v", string(b), err) + bomb("unmarshal body %s err:%v", string(b), err) } can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_modify", collect.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } collect.Encode() @@ -466,12 +466,12 @@ func collectPut(c *gin.Context) { //校验采集是否存在 obj, err := models.GetCollectById(recv.Type, collect.Id) //id找不到的情况 if err != nil { - errors.Bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) + bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) } tmpId := obj.(*models.ApiCollect).Id if tmpId == 0 { - errors.Bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) + bomb("采集不存在 type:%s id:%d", recv.Type, collect.Id) } collect.Creator = creator @@ -480,7 +480,7 @@ func collectPut(c *gin.Context) { old, err := models.GetCollectByNameAndNid(recv.Type, name, nid) errors.Dangerous(err) if old != nil && tmpId != old.(*models.ApiCollect).Id { - errors.Bomb("同节点下策略名称 %s 已存在", name) + bomb("same stra name %s in node", name) } errors.Dangerous(collect.Update()) @@ -488,7 +488,7 @@ func collectPut(c *gin.Context) { return default: - errors.Bomb("采集类型不合法") + bomb("采集类型不合法") } renderData(c, "ok", nil) @@ -508,11 +508,11 @@ func collectsDel(c *gin.Context) { for i := 0; i < len(obj.Ids); i++ { tmp, err := models.GetCollectById(obj.Type, obj.Ids[i]) //id找不到的情况 if err != nil { - errors.Bomb("采集不存在 type:%s id:%d", obj.Type, obj.Ids[i]) + bomb("采集不存在 type:%s id:%d", obj.Type, obj.Ids[i]) } if tmp == nil { - errors.Bomb("采集不存在 type:%s id:%d", obj.Type, obj.Ids[i]) + bomb("采集不存在 type:%s id:%d", obj.Type, obj.Ids[i]) } var nid int64 @@ -530,7 +530,7 @@ func collectsDel(c *gin.Context) { can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_collect_delete", int64(nid)) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } } diff --git a/src/modules/monapi/http/router_funcs.go b/src/modules/monapi/http/router_funcs.go index 43447b1d..8ebaab2d 100644 --- a/src/modules/monapi/http/router_funcs.go +++ b/src/modules/monapi/http/router_funcs.go @@ -4,16 +4,25 @@ import ( "strconv" "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/toolkits/i18n" "github.com/gin-gonic/gin" "github.com/toolkits/pkg/errors" ) +func dangerous(v interface{}) { + errors.Dangerous(v) +} + +func bomb(format string, a ...interface{}) { + errors.Bomb(i18n.Sprintf(format, a...)) +} + func urlParamStr(c *gin.Context, field string) string { val := c.Param(field) if val == "" { - errors.Bomb("[%s] is blank", field) + bomb("[%s] is blank", field) } return val @@ -23,7 +32,7 @@ func urlParamInt64(c *gin.Context, field string) int64 { strval := urlParamStr(c, field) intval, err := strconv.ParseInt(strval, 10, 64) if err != nil { - errors.Bomb("cannot convert %s to int64", strval) + bomb("cannot convert %s to int64", strval) } return intval @@ -45,7 +54,7 @@ func queryStr(c *gin.Context, key string, defaultVal string) string { func mustQueryStr(c *gin.Context, key string) string { val := c.Query(key) if val == "" { - errors.Bomb("arg[%s] not found", key) + bomb("arg[%s] not found", key) } return val @@ -56,7 +65,7 @@ func mustQueryInt(c *gin.Context, key string) int { intv, err := strconv.Atoi(strv) if err != nil { - errors.Bomb("cannot convert [%s] to int", strv) + bomb("cannot convert [%s] to int", strv) } return intv @@ -67,7 +76,7 @@ func mustQueryInt64(c *gin.Context, key string) int64 { intv, err := strconv.ParseInt(strv, 10, 64) if err != nil { - errors.Bomb("cannot convert [%s] to int64", strv) + bomb("cannot convert [%s] to int64", strv) } return intv @@ -81,7 +90,7 @@ func queryInt(c *gin.Context, key string, defaultVal int) int { intv, err := strconv.Atoi(strv) if err != nil { - errors.Bomb("cannot convert [%s] to int", strv) + bomb("cannot convert [%s] to int", strv) } return intv @@ -95,7 +104,7 @@ func queryInt64(c *gin.Context, key string, defaultVal int64) int64 { intv, err := strconv.ParseInt(strv, 10, 64) if err != nil { - errors.Bomb("cannot convert [%s] to int64", strv) + bomb("cannot convert [%s] to int64", strv) } return intv @@ -150,7 +159,7 @@ func loginUsername(c *gin.Context) string { username2 := cookieUsername(c) if username2 == "" { - errors.Bomb("unauthorized") + bomb("unauthorized") } return username2 @@ -159,11 +168,11 @@ func loginUsername(c *gin.Context) string { func mustNode(id int64) *models.Node { node, err := models.NodeGet("id=?", id) if err != nil { - errors.Bomb("cannot retrieve node[%d]: %v", id, err) + bomb("cannot retrieve node[%d]: %v", id, err) } if node == nil { - errors.Bomb("no such node[%d]", id) + bomb("no such node[%d]", id) } return node @@ -172,11 +181,11 @@ func mustNode(id int64) *models.Node { func mustScreen(id int64) *models.Screen { screen, err := models.ScreenGet("id", id) if err != nil { - errors.Bomb("cannot retrieve screen[%d]: %v", id, err) + bomb("cannot retrieve screen[%d]: %v", id, err) } if screen == nil { - errors.Bomb("no such screen[%d]", id) + bomb("no such screen[%d]", id) } return screen @@ -185,11 +194,11 @@ func mustScreen(id int64) *models.Screen { func mustScreenSubclass(id int64) *models.ScreenSubclass { subclass, err := models.ScreenSubclassGet("id", id) if err != nil { - errors.Bomb("cannot retrieve subclass[%d]: %v", id, err) + bomb("cannot retrieve subclass[%d]: %v", id, err) } if subclass == nil { - errors.Bomb("no such subclass[%d]", id) + bomb("no such subclass[%d]", id) } return subclass @@ -198,11 +207,11 @@ func mustScreenSubclass(id int64) *models.ScreenSubclass { func mustChart(id int64) *models.Chart { chart, err := models.ChartGet("id", id) if err != nil { - errors.Bomb("cannot retrieve chart[%d]: %v", id, err) + bomb("cannot retrieve chart[%d]: %v", id, err) } if chart == nil { - errors.Bomb("no such chart[%d]", id) + bomb("no such chart[%d]", id) } return chart @@ -211,11 +220,11 @@ func mustChart(id int64) *models.Chart { func mustEventCur(id int64) *models.EventCur { eventCur, err := models.EventCurGet("id", id) if err != nil { - errors.Bomb("cannot retrieve eventCur[%d]: %v", id, err) + bomb("cannot retrieve eventCur[%d]: %v", id, err) } if eventCur == nil { - errors.Bomb("no such eventCur[%d]", id) + bomb("no such eventCur[%d]", id) } return eventCur @@ -224,11 +233,11 @@ func mustEventCur(id int64) *models.EventCur { func mustEvent(id int64) *models.Event { eventCur, err := models.EventGet("id", id) if err != nil { - errors.Bomb("cannot retrieve event[%d]: %v", id, err) + bomb("cannot retrieve event[%d]: %v", id, err) } if eventCur == nil { - errors.Bomb("no such event[%d]", id) + bomb("no such event[%d]", id) } return eventCur diff --git a/src/modules/monapi/http/router_maskconf.go b/src/modules/monapi/http/router_maskconf.go index 5ce982be..fc5206ee 100644 --- a/src/modules/monapi/http/router_maskconf.go +++ b/src/modules/monapi/http/router_maskconf.go @@ -25,15 +25,15 @@ func (f MaskconfForm) Validate() { mustNode(f.Nid) if f.Category == 1 && (f.Endpoints == nil || len(f.Endpoints) == 0) { - errors.Bomb("arg[endpoints] empty") + bomb("arg[endpoints] empty") } if f.Category == 2 && len(f.CurNidPaths) == 0 { - errors.Bomb("arg[cur_nid_paths] empty") + bomb("arg[cur_nid_paths] empty") } if f.Btime >= f.Etime { - errors.Bomb("args[btime,etime] invalid") + bomb("args[btime,etime] invalid") } if f.Tags == "" { @@ -44,7 +44,7 @@ func (f MaskconfForm) Validate() { for i := 0; i < len(tagsList); i++ { kv := strings.Split(tagsList[i], "=") if len(kv) != 2 { - errors.Bomb("arg[tags] invalid") + bomb("arg[tags] invalid") } } } @@ -55,7 +55,7 @@ func maskconfPost(c *gin.Context) { can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_maskconf_create", f.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } f.Validate() @@ -106,7 +106,7 @@ func maskconfDel(c *gin.Context) { can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_maskconf_delete", mask.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } renderMessage(c, models.MaskconfDel(id)) @@ -117,13 +117,13 @@ func maskconfPut(c *gin.Context) { errors.Dangerous(err) if mc == nil { - errors.Bomb("maskconf is nil") + bomb("maskconf is nil") } can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_maskconf_modify", mc.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } var f MaskconfForm diff --git a/src/modules/monapi/http/router_screen.go b/src/modules/monapi/http/router_screen.go index 7c4a7973..6bf7e28f 100644 --- a/src/modules/monapi/http/router_screen.go +++ b/src/modules/monapi/http/router_screen.go @@ -17,19 +17,19 @@ type ScreenForm struct { func screenNameValidate(name string) { if str.Dangerous(name) { - errors.Bomb("arg[name] is dangerous") + bomb("arg[name] is dangerous") } if len(name) > 250 { - errors.Bomb("arg[name] too long") + bomb("arg[name] too long") } if len(name) == 0 { - errors.Bomb("arg[name] is blank") + bomb("arg[name] is blank") } if strings.ContainsAny(name, "/%") { - errors.Bomb("arg[name] invalid") + bomb("arg[name] invalid") } } @@ -42,7 +42,7 @@ func screenPost(c *gin.Context) { can, err := models.UsernameCandoNodeOp(username, "mon_screen_create", node.Id) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } screenNameValidate(f.Name) @@ -64,7 +64,7 @@ func screenGets(c *gin.Context) { can, err := models.UsernameCandoNodeOp(username, "mon_screen_view", nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } objs, err := models.ScreenGets(nid) @@ -79,7 +79,7 @@ func screenGet(c *gin.Context) { can, err := models.UsernameCandoNodeOp(username, "mon_screen_view", obj.NodeId) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } obj.NodePath = node.Path @@ -102,7 +102,7 @@ func screenPut(c *gin.Context) { can, err := models.UsernameCandoNodeOp(username, "mon_screen_modify", screen.NodeId) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } node := mustNode(f.NodeId) @@ -121,7 +121,7 @@ func screenDel(c *gin.Context) { can, err := models.UsernameCandoNodeOp(username, "mon_screen_delete", screen.NodeId) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } errors.Dangerous(screen.Del()) @@ -142,11 +142,11 @@ type ScreenSubclassForm struct { func (f ScreenSubclassForm) Validate() { if str.Dangerous(f.Name) { - errors.Bomb("name invalid") + bomb("arg[name] invalid") } if strings.ContainsAny(f.Name, "/%") { - errors.Bomb("name invalid") + bomb("arg[name] invalid") } } @@ -160,7 +160,7 @@ func screenSubclassPost(c *gin.Context) { can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_screen_create", screen.NodeId) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } screenNameValidate(f.Name) @@ -188,7 +188,7 @@ func screenSubclassPut(c *gin.Context) { can, err := models.UsernameCandoNodeOp(username, "mon_screen_modify", screen.NodeId) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } } @@ -213,7 +213,7 @@ func screenSubclassLocPut(c *gin.Context) { can, err := models.UsernameCandoNodeOp(username, "mon_screen_modify", screen.NodeId) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } } @@ -232,7 +232,7 @@ func screenSubclassDel(c *gin.Context) { can, err := models.UsernameCandoNodeOp(loginUsername(c), "mon_screen_delete", screen.NodeId) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } if subclass == nil { diff --git a/src/modules/monapi/http/router_stra.go b/src/modules/monapi/http/router_stra.go index ecfee747..b80fa11f 100644 --- a/src/modules/monapi/http/router_stra.go +++ b/src/modules/monapi/http/router_stra.go @@ -16,7 +16,7 @@ func straPost(c *gin.Context) { can, err := models.UsernameCandoNodeOp(username, "mon_stra_create", stra.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } stra.Creator = username @@ -26,7 +26,7 @@ func straPost(c *gin.Context) { oldStra, _ := models.StraGet("name", stra.Name) if oldStra != nil && oldStra.Nid == stra.Nid { - errors.Bomb("同节点下策略名称 %s 已存在", stra.Name) + bomb("同节点下策略名称 %s 已存在", stra.Name) } errors.Dangerous(stra.Save()) @@ -48,7 +48,7 @@ func straPut(c *gin.Context) { can, err := models.UsernameCandoNodeOp(username, "mon_stra_modify", stra.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } stra.LastUpdator = username @@ -56,7 +56,7 @@ func straPut(c *gin.Context) { oldStra, _ := models.StraGet("name", stra.Name) if oldStra != nil && oldStra.Id != stra.Id && oldStra.Nid == stra.Nid { - errors.Bomb("同节点下策略名称 %s 已存在", stra.Name) + bomb("同节点下策略名称 %s 已存在", stra.Name) } s, err := models.StraGet("id", stra.Id) @@ -83,7 +83,7 @@ func strasDel(c *gin.Context) { can, err := models.UsernameCandoNodeOp(username, "mon_stra_delete", stra.Nid) errors.Dangerous(err) if !can { - errors.Bomb("permission deny") + bomb("permission deny") } } @@ -100,7 +100,7 @@ func straGet(c *gin.Context) { stra, err := models.StraGet("id", sid) errors.Dangerous(err) if stra == nil { - errors.Bomb("stra not found") + bomb("stra not found") } err = stra.Decode() diff --git a/src/modules/monapi/monapi.go b/src/modules/monapi/monapi.go index fcf92577..cea61f15 100644 --- a/src/modules/monapi/monapi.go +++ b/src/modules/monapi/monapi.go @@ -16,6 +16,7 @@ import ( "github.com/didi/nightingale/src/modules/monapi/http" "github.com/didi/nightingale/src/modules/monapi/redisc" "github.com/didi/nightingale/src/modules/monapi/scache" + "github.com/didi/nightingale/src/toolkits/i18n" _ "github.com/go-sql-driver/mysql" @@ -65,6 +66,8 @@ func main() { scache.Init() + i18n.Init(config.Get().I18n) + if err := scache.CheckJudge(); err != nil { logger.Errorf("check judge fail: %v", err) } diff --git a/src/modules/rdb/config/yaml.go b/src/modules/rdb/config/yaml.go index f70ad502..faf52cea 100644 --- a/src/modules/rdb/config/yaml.go +++ b/src/modules/rdb/config/yaml.go @@ -6,6 +6,7 @@ import ( "github.com/toolkits/pkg/file" "github.com/didi/nightingale/src/common/loggeri" + "github.com/didi/nightingale/src/toolkits/i18n" ) type ConfigT struct { @@ -19,6 +20,7 @@ type ConfigT struct { RabbitMQ rabbitmqSection `yaml:"rabbitmq"` WeChat wechatSection `yaml:"wechat"` Captcha bool `yaml:"captcha"` + I18n i18n.I18nSection `yaml:"i18n"` } type wechatSection struct { @@ -115,6 +117,14 @@ func Parse() error { Config = &c fmt.Println("config.file:", ymlFile) + if Config.I18n.DictPath == "" { + Config.I18n.DictPath = "etc/dict.json" + } + + if Config.I18n.Lang == "" { + Config.I18n.Lang = "zh" + } + if err = parseOps(); err != nil { return err } diff --git a/src/modules/rdb/http/router_auth.go b/src/modules/rdb/http/router_auth.go index 60034fbf..1e8233f9 100644 --- a/src/modules/rdb/http/router_auth.go +++ b/src/modules/rdb/http/router_auth.go @@ -230,7 +230,7 @@ func (f *loginInput) validate() { bomb("%s invalid", f.Username) } if len(f.Username) > 64 { - bomb("%s too long", f.Username) + bomb("%s too long > 64", f.Username) } } } diff --git a/src/modules/rdb/http/router_funcs.go b/src/modules/rdb/http/router_funcs.go index 99c906ec..2c9afe5d 100644 --- a/src/modules/rdb/http/router_funcs.go +++ b/src/modules/rdb/http/router_funcs.go @@ -8,6 +8,7 @@ import ( "github.com/toolkits/pkg/errors" "github.com/didi/nightingale/src/models" + "github.com/didi/nightingale/src/toolkits/i18n" ) func dangerous(v interface{}) { @@ -15,7 +16,7 @@ func dangerous(v interface{}) { } func bomb(format string, a ...interface{}) { - errors.Bomb(format, a...) + errors.Bomb(i18n.Sprintf(format, a...)) } func bind(c *gin.Context, ptr interface{}) { @@ -110,7 +111,7 @@ func renderMessage(c *gin.Context, v interface{}) { switch t := v.(type) { case string: - c.JSON(200, gin.H{"err": t}) + c.JSON(200, gin.H{"err": i18n.Sprintf(t)}) case error: c.JSON(200, gin.H{"err": t.Error()}) } @@ -274,7 +275,7 @@ func Node(id int64) *models.Node { dangerous(err) if node == nil { - bomb("no such node[id:%d]", id) + bomb("no such node[%d]", id) } return node diff --git a/src/modules/rdb/http/router_node_cate.go b/src/modules/rdb/http/router_node_cate.go index 0f131be4..ea1b0306 100644 --- a/src/modules/rdb/http/router_node_cate.go +++ b/src/modules/rdb/http/router_node_cate.go @@ -20,23 +20,23 @@ type nodeCatePostForm struct { func (f nodeCatePostForm) Validate() { if f.Ident == "" { - bomb("ident is blank") + bomb("[%s] is blank", "ident") } if f.Name == "" { - bomb("name is blank") + bomb("[%s] is blank", "name") } if len(f.Ident) > 32 { - bomb("arg[ident] too long") + bomb("arg[%s] too long > %d", "ident", 32) } if len(f.Name) > 255 { - bomb("arg[name] too long") + bomb("arg[%s] too long > %d", "name", 255) } if !str.IsMatch(f.Ident, "[a-z]+") { - bomb("arg[ident] invalid") + bomb("arg[%s] invalid", "ident") } if str.Dangerous(f.Name) { @@ -65,7 +65,7 @@ type nodeCatePutForm struct { func (f nodeCatePutForm) Validate() { if len(f.Name) > 255 { - bomb("arg[name] too long") + bomb("arg[%s] too long > %d", "name", 255) } if str.Dangerous(f.Name) { diff --git a/src/modules/rdb/http/router_resource.go b/src/modules/rdb/http/router_resource.go index 4e89f617..e9c69003 100644 --- a/src/modules/rdb/http/router_resource.go +++ b/src/modules/rdb/http/router_resource.go @@ -337,13 +337,13 @@ func resourceBindNode(c *gin.Context) { ids, err = models.ResourceIdsByUUIDs(f.Items) dangerous(err) if len(ids) == 0 { - bomb("resources not found by uuid") + bomb("resources not found by %s", "uuic") } } else if f.Field == "ident" { ids, err = models.ResourceIdsByIdents(f.Items) dangerous(err) if len(ids) == 0 { - bomb("resources not found by ident") + bomb("resources not found by %s", "ident") } } else { bomb("field[%s] not supported", f.Field) diff --git a/src/modules/rdb/http/router_team.go b/src/modules/rdb/http/router_team.go index 8f1b9840..b1454b22 100644 --- a/src/modules/rdb/http/router_team.go +++ b/src/modules/rdb/http/router_team.go @@ -213,7 +213,7 @@ func belongTeamsGet(c *gin.Context) { dangerous(err) if user == nil { - bomb("no such username[%s]", username) + bomb("no such user: %s", username) } var ids []int64 @@ -246,7 +246,7 @@ func isTeamMember(c *gin.Context) { dangerous(err) if user == nil { - bomb("no such username[%s]", username) + bomb("no such user: %s", username) } team, err := models.TeamGet("ident=?", teamIdent) diff --git a/src/modules/rdb/rdb.go b/src/modules/rdb/rdb.go index 21af7719..e2973cf2 100644 --- a/src/modules/rdb/rdb.go +++ b/src/modules/rdb/rdb.go @@ -20,6 +20,7 @@ import ( "github.com/didi/nightingale/src/modules/rdb/rabbitmq" "github.com/didi/nightingale/src/modules/rdb/redisc" "github.com/didi/nightingale/src/modules/rdb/ssoc" + "github.com/didi/nightingale/src/toolkits/i18n" ) var ( @@ -66,6 +67,7 @@ func main() { // 初始化 redis 用来发送邮件短信等 redisc.InitRedis() cron.InitWorker() + i18n.Init(config.Config.I18n) // 初始化 rabbitmq 处理部分异步逻辑 rabbitmq.Init() diff --git a/src/toolkits/i18n/i18n.go b/src/toolkits/i18n/i18n.go new file mode 100644 index 00000000..f369e487 --- /dev/null +++ b/src/toolkits/i18n/i18n.go @@ -0,0 +1,141 @@ +package i18n + +import ( + "encoding/json" + "io" + "log" + + "golang.org/x/text/language" + "golang.org/x/text/message" + + "github.com/toolkits/pkg/file" +) + +type I18nSection struct { + DictPath string `yaml:"dictPath"` + Lang string `yaml:"lang"` +} + +// Init will init i18n support via input language. +func Init(config ...I18nSection) { + l := "zh" + fpath := "etc/dict.json" + + if len(config) > 0 { + l = config[0].Lang + fpath = config[0].DictPath + } + + lang := language.Chinese + switch l { + case "en": + lang = language.English + case "zh": + lang = language.Chinese + } + + tag, _, _ := supported.Match(lang) + switch tag { + case language.AmericanEnglish, language.English: + initEnUS(lang) + case language.SimplifiedChinese, language.Chinese: + initZhCN(lang, fpath) + default: + initZhCN(lang, fpath) + } + + p = message.NewPrinter(lang) +} + +func initEnUS(tag language.Tag) { +} + +func initZhCN(tag language.Tag, fpath string) { + + content, err := file.ToTrimString(fpath) + if err != nil { + log.Printf("read configuration file %s fail %s", fpath, err.Error()) + return + } + + m := make(map[string]map[string]string) + + err = json.Unmarshal([]byte(content), &m) + if err != nil { + log.Println("parse config file:", fpath, "fail:", err) + return + } + + if dict, exists := m["zh"]; exists { + for k, v := range dict { + _ = message.SetString(tag, k, v) + } + } + +} + +var p *message.Printer + +func newMatcher(t []language.Tag) *matcher { + tags := &matcher{make(map[language.Tag]int)} + for i, tag := range t { + ct, err := language.All.Canonicalize(tag) + if err != nil { + ct = tag + } + tags.index[ct] = i + } + return tags +} + +type matcher struct { + index map[language.Tag]int +} + +func (m matcher) Match(want ...language.Tag) (language.Tag, int, language.Confidence) { + for _, t := range want { + ct, err := language.All.Canonicalize(t) + if err != nil { + ct = t + } + conf := language.Exact + for { + if index, ok := m.index[ct]; ok { + return ct, index, conf + } + if ct == language.Und { + break + } + ct = ct.Parent() + conf = language.High + } + } + return language.Und, 0, language.No +} + +var supported = newMatcher([]language.Tag{ + language.AmericanEnglish, + language.English, + language.SimplifiedChinese, + language.Chinese, +}) + +// Fprintf is like fmt.Fprintf, but using language-specific formatting. +func Fprintf(w io.Writer, key message.Reference, a ...interface{}) (n int, err error) { + return p.Fprintf(w, key, a...) +} + +// Printf is like fmt.Printf, but using language-specific formatting. +func Printf(format string, a ...interface{}) { + _, _ = p.Printf(format, a...) +} + +// Sprintf formats according to a format specifier and returns the resulting string. +func Sprintf(format string, a ...interface{}) string { + return p.Sprintf(format, a...) +} + +// Sprint is like fmt.Sprint, but using language-specific formatting. +func Sprint(a ...interface{}) string { + return p.Sprint(a...) +}