asaskevich / govalidator

[Go] Package of validators and sanitizers for strings, numerics, slices and structs
MIT License
6.02k stars 556 forks source link

how to return message with multile languages ? #312

Closed clearcodecn closed 5 years ago

clearcodecn commented 5 years ago

the scenes is that I hav many languages that will return for validate failed . such as :


type exampleStruct struct {
  Name  string ``
  Email string `valid:"email"`  //    email is not correct || email 格式错误 
}

how can I defined it .

wadtech commented 5 years ago

Personally I'd use a custom validation message that is some identifier for your i18n translations.


type exampleStruct struct {
  Name  string ``
  Email string `valid:"email~EMAIL_INVALID"`  //    email is not correct || email 格式错误 
}

Then you can use EMAIL_INVALID to look up the correct multi language strings you need.

clearcodecn commented 5 years ago

can you show me a deep demo ? I am a little rookie ... I know that I can use reflect package to find the struct comment that I need . I did not know how to use custom validation message to impl it

wadtech commented 5 years ago

@clearcodecn I've put together a quick gist with what I mean.

https://gist.github.com/wadtech/c8a62a42a0ff618aa95f4c0b854b52f6

No promises that it's good Go, but hopefully that helps understand my thinking.

edit: I've guessed at the correct locale from what google translate tells me, so sorry if that's incorrect.

clearcodecn commented 5 years ago

Yeah . The translation is correct . 😄 I understand . thank you . Because of my lazy . I want to write like this :

type person struct {
    Name  string `valid:"-"`
    Email string `valid:"email"` //    en: email is not correct || zh_CN: email 格式错误
}

but just It seems impossible. I have to defined too much code like : EMAIL_INVALID In our project we defined Thousands of code , but when the struct changed , we have to change the code . And the file always like: zh_CN.go, en.go , always conflict in vcs . The code is difficult to maintain 😢 . If you have any idea like this using comment to write error msg with i18n translations , tell me please .

wadtech commented 5 years ago

In my experience translated content is independent from the calling code, so having a module specifically designed to provide translations means you can inject the dependency and have one code path that does validation etc.

Personally I'd create a module to handle translations that can be set to a locale. Then I'd refactor your validations to use some constant that can be passed to your translation service.

That way each language will be separate in VCS without having to rewrite actual code or keep locale-specific copies of business logic.

I don't think there's a lazy way unfortuntely!

I updated my gist with an example of what that would look like at high level.

clearcodecn commented 5 years ago

I just say by chinese and english translate by google translate .

chinese

我非常明白你的做法,应该是目前为止最正确的做法,我想讨论的问题是:

我们现在的代码根据语言独立成单个文件,例如:

|- language |---- zh_CN.go |---- en.go |---- code.go

每个文件里面的写法像下面这样:

// code.go
const (
    EMAIL_INVALID = 100001   // some error code 
)

func Translate(lang string, code int) string {
      if lang == "zh_CN" {
            return zhCN[code] 
      }
}

// zh_CN.go
var zhCN = map[int]string {
     EMAIL_INVALID: "email 格式错误",
}

// en.go 
var en = map[int]string {
     EMAIL_INVALID: "invalid email",
}

但是当错误的变量变得异常多的时候,版本更新迭代了很长的一段周期, 导致的情况是请求的结构体随着版本发生变化,很多较老的code由于是写在tag里面, 到了现在具体有没有使用它我们一概不知,并且我们不敢轻易删除变量即使看起来没有被使用 毕竟tag的引用code码得不到IDE的支持.

因此我想能否使用注释来作为多语言支持,可问题是在语言很多的时候 注释将会写的非常的长 也不太好维护。

也就是说我们现在实际上使用的是你推荐的方法,但是我感觉维护挺困难的。

english by google translate


I understand your approach very well. It should be the most correct practice so far. The questions I want to discuss are:

Our current code is independent of the language into some single file, for example:

|- language |---- zh_CN.go |---- en.go |---- code.go

The way each file is written like this:

// code.go
Const (
    EMAIL_INVALID = 100001 // some error code
)

Func Translate(lang string, code int) string {
      If lang == "zh_CN" {
            Return zhCN[code]
      }
}

// zh_CN.go
Var zhCN = map[int]string {
     EMAIL_INVALID: "email 格式错误",
}

// en.go
Var en = map[int]string {
     EMAIL_INVALID: "invalid email",
}

But when the wrong variable becomes abnormally much, the version update iterates over a long period of time. The situation is that the structure of the request changes with the version, and many older codes are written in the tag. We don’t know if we have used it now, and we don’t dare to delete the variables easily even if they don’t seem to be used. After all, tag reference code is not supported by IDE.

So I want to be able to use comments as multi-language support, but the problem is that when the language is very large, the comments will be written very long. Not too good to maintain.

In other words, we are actually using the method you recommended, but I feel that maintenance is very difficult.

wadtech commented 5 years ago

Ah I see!

I think that it's one of those areas that is never very satisfying, no matter the model you choose for storing translations it always ends up being a bit of a mess.

The only thing I could suggest is to prefix your constants in the language file so LANG_EMAIL_INVALID or something then you can use your IDE to find instances of the constant not in the translation files.

As you say, even if you could do this with the struct tags they would end up being enormous and unweildy.

Maybe a task in your build process or CI that looks for unused translations could be an option, that way team members can be responsible for ensuring that they don't leave behind unused strings - or fail to include ones they need!

clearcodecn commented 5 years ago

Thanks for your answer . CI sounds like a good idea . 😄