use embed dict for i18n (#492)

* add validate for some plugins

* restore getSessionUserWithCache method

* add validate for i18n.config

* use embed dict for i18n
This commit is contained in:
yubo 2021-01-08 15:11:41 +08:00 committed by GitHub
parent 733da1ea94
commit 7289636d35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 197 additions and 25 deletions

View File

@ -102,3 +102,16 @@ func SessionGetWithCache(sid string) (*Session, error) {
return sess, nil
}
func SessionGetUserWithCache(sid string) (*User, error) {
s, err := SessionGetWithCache(sid)
if err != nil {
return nil, err
}
if s.Username == "" {
return nil, fmt.Errorf("user not found")
}
return UserMustGet("username=?", s.Username)
}

View File

@ -1,6 +1,8 @@
package mongodb
import (
"fmt"
"github.com/didi/nightingale/src/modules/monapi/collector"
"github.com/didi/nightingale/src/modules/monapi/plugins/mongodb/mongodb"
"github.com/didi/nightingale/src/toolkits/i18n"
@ -52,6 +54,9 @@ type MongodbRule struct {
}
func (p *MongodbRule) Validate() error {
if len(p.Servers) == 0 || p.Servers[0] == "" {
return fmt.Errorf("mongodb.rule.servers must be set")
}
return nil
}

View File

@ -1,6 +1,8 @@
package redis
import (
"fmt"
"github.com/didi/nightingale/src/modules/monapi/collector"
"github.com/didi/nightingale/src/toolkits/i18n"
"github.com/influxdata/telegraf"
@ -41,7 +43,7 @@ var (
type RedisCommand struct {
Command []string `label:"Command" json:"command,required" description:"" `
Field string `label:"Field" json:"field,required" description:"metric name"`
Type string `label:"Type" json:"type" description:"integer|string|float"`
Type string `label:"Type" json:"type" description:"integer|string|float(default)"`
}
type RedisRule struct {
@ -51,6 +53,20 @@ type RedisRule struct {
}
func (p *RedisRule) Validate() error {
if len(p.Servers) == 0 || p.Servers[0] == "" {
return fmt.Errorf("redis.rule.servers must be set")
}
for i, cmd := range p.Commands {
if len(cmd.Command) == 0 {
return fmt.Errorf("redis.rule.commands[%d].command must be set", i)
}
if cmd.Field == "" {
return fmt.Errorf("redis.rule.commands[%d].field must be set", i)
}
if cmd.Type == "" {
cmd.Type = "float"
}
}
return nil
}

View File

@ -0,0 +1,132 @@
package config
import "github.com/didi/nightingale/src/toolkits/i18n"
var (
langDict = map[string]map[string]string{
"zh": map[string]string{
"stra not found": "聚合策略为找到",
"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": "用户未找到",
"Repositories": "Repositories",
"List of repositories to monitor": "List of repositories to monitor",
"Access token": "Access token",
"Github API access token. Unauthenticated requests are limited to 60 per hour": "Github API access token. Unauthenticated requests are limited to 60 per hour",
"Enterprise base url": "Enterprise base url",
"Github API enterprise url. Github Enterprise accounts must specify their base url": "Github API enterprise url. Github Enterprise accounts must specify their base url",
"HTTP timeout": "HTTP timeout",
"Timeout for HTTP requests": "Timeout for HTTP requests",
"Unable to get captcha": "无法获得验证码",
"Invalid captcha answer": "错误的验证码",
"Username %s is invalid": "用户名 %s 不符合规范",
"Username %s too long > 64": "用户名 %s 太长(64)",
"Unable to get login arguments": "无法获得登陆参数",
"Deny Access from %s with whitelist control": "来自 %s 的访问被白名单规则拒绝",
"Invalid login type %s": "不支持的登陆类型 %s",
"Unable to get type, sms-code | email-code": "无法获得验证码类型",
"Unable to get code arg": "无法获得验证码类型",
"sms/email sender is disabled": "无法发送 短信/邮件 验证码",
"Invalid code type %s": "不支持的验证码类型 %s",
"Cannot find the user by %s": "无法用 %s 找到相关用户",
"Unable to get password": "无法获取密码",
"Invalid code": "不符合规范的验证码",
"The code is incorrect": "无效的验证码",
"The code has expired": "失效的验证码",
"Invalid arguments %s": "不合法的参数 %s",
"Login fail, check your username and password": "登陆失败,请检查用户名/密码",
"User dose not exist": "用户不存在",
"Username %s already exists": "用户名 %s 已存在",
"Upper char": "大写字母",
"Lower char": "小写字母",
"Number": "数字",
"Special char": "特殊字符",
"Must include %s": "必须包含 %s",
"Invalid Password, %s": "密码不符合规范, %s",
"character: %s not supported": "不支持的字符 %s",
"Incorrect login/password %s times, you still have %s chances": "登陆失败%d次你还有%d次机会",
"The limited sessions %d": "会话数量限制,最多%d个会话",
"Password has been expired": "密码已过期,请重置密码",
"User is inactive": "用户已禁用",
"User is locked": "用户已锁定",
"User is frozen": "用户已休眠",
"User is writen off": "用户已注销",
"Minimum password length %d": "密码最小长度 %d",
"Password too short (min:%d) %s": "密码太短 (最小 %d) %s",
"%s format error": "%s 所填内容不符合规范",
"%s %s format error": "%s %s 所填内容不符合规范",
"username too long (max:%d)": "用户名太长 (最长:%d)",
"dispname too long (max:%d)": "昵称太长 (最长:%d)",
"email %s or phone %s is exists": "邮箱 %s 或者 手机号 %s 已存在",
"Password is not set": "密码未设置",
"Incorrect old password": "密码错误",
"The password is the same as the old password": "密码与历史密码重复",
"phone": "手机号",
"email": "邮箱",
"username": "用户名",
"dispname": "昵称",
"Temporary user has expired": "临时账户,已过有效期",
"Invalid user status %d": "异常的用户状态 %d",
"Password expired, please change the password in time": "密码过期,请及时修改密码",
"First Login, please change the password in time": "初始登陆,请及时修改密码",
},
}
)
func init() {
i18n.DictRegister(langDict)
}

View File

@ -685,19 +685,7 @@ func v1SessionGet(c *gin.Context) {
func v1SessionGetUser(c *gin.Context) {
sid := urlParamStr(c, "sid")
user, err := func() (*models.User, error) {
s, err := models.SessionGetWithCache(sid)
if err != nil {
return nil, err
}
if s.Username == "" {
return nil, fmt.Errorf("user not found")
}
return models.UserMustGet("username=?", s.Username)
}()
user, err := models.SessionGetUserWithCache(sid)
renderData(c, user, err)
}

View File

@ -15,22 +15,40 @@ import (
var p *message.Printer
type I18nSection struct {
DictPath string `yaml:"dictPath"`
Lang string `yaml:"lang"`
DictPath string `yaml:"dictPath"`
}
// 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
func (p *I18nSection) Validate() error {
if p.Lang == "" {
p.Lang = defaultConfig.Lang
}
if p.DictPath == "" {
p.DictPath = defaultConfig.DictPath
}
DictFileRegister(fpath)
p = message.NewPrinter(langTag(l))
return nil
}
var (
defaultConfig = I18nSection{"zh", "etc/dict.json"}
)
// Init will init i18n support via input language.
func Init(configs ...I18nSection) error {
config := defaultConfig
if len(configs) > 0 {
config = configs[0]
}
if err := config.Validate(); err != nil {
return err
}
DictFileRegister(config.DictPath)
p = message.NewPrinter(langTag(config.Lang))
return nil
}
func DictFileRegister(files ...string) {