support i18n (#431)

This commit is contained in:
qinyening 2020-11-30 21:11:46 +08:00 committed by GitHub
parent 82dadb31b5
commit 74e85cdadc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 412 additions and 145 deletions

63
etc/dict.json Normal file
View File

@ -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": "用户未找到"
}
}

1
go.mod
View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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{}) {

View File

@ -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 == "" {

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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,

View File

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

View File

@ -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()

View File

@ -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())

View File

@ -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")
}
}

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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()

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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

View File

@ -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) {

View File

@ -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)

View File

@ -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)

View File

@ -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()

141
src/toolkits/i18n/i18n.go Normal file
View File

@ -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...)
}