📑
【Go】go-playground/validator日本語化の方法
はじめに
Laravelではlocale
をja
に設定し、言語ファイルを作成することで簡単にバリデーションメッセージを日本語化することができます。
一方、Goではgo-playground/validatorを使うことで簡単にバリデーションを実行することができますが、日本語化するにはどうすれば良いかわかっていませんでした。
今回は日本語化する方法を調べ実装してみたので、備忘録的に書いていこうと思います。
環境
- go version go1.24.0 darwin/arm64
前提
- ユーザー情報を登録することを想定
- キーバリュー形式でエラーメッセージを出力する
方法1:mapの活用
全体像は以下の通りです。
main.go
package main
import (
"net/http"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
"golang.org/x/crypto/bcrypt"
)
type RequestUser struct {
Name string `json:"name" validate:"required,max=255"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,max=40"`
ConfirmPassword string `json:"confirm_password" validate:"required,max=40,eqfield=Password"`
}
type User struct {
Name string `json:"name" validate:"required,max=255"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,max=40"`
}
// DB接続に関する処理は省略
func main() {
e := echo.New()
e.POST("/create", func(c echo.Context) error {
req := RequestUser{}
if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, err)
}
validation := validator.New()
if err := validation.Struct(req); err != nil {
errors := map[string]string{}
for _, err := range err.(validator.ValidationErrors) {
errors[err.Field()] = err.Tag()
}
return c.JSON(http.StatusBadRequest, errors)
}
// 登録処理は省略
})
e.Logger.Fatal(e.Start(":8080"))
}
ここでエラーが発生すると、以下のようにエラーメッセージが出力されます。
{
"ConfirmPassword": "eqfield",
"Name": "required"
}
ここでは「構造体のフィールド」をキーに、「validationタグに付与されたルール名」がバリューになっています。
これらを、それぞれmapを使い、日本語化していこうと思います。
フィールドとメッセージのmap
main.go
// フィールド名用マップ
var fieldNameMap = map[string]string{
"Name": "お名前",
"Email": "メールアドレス",
"Password": "パスワード",
"ConfirmPassword": "確認用パスワード",
}
// エラーメッセージ用マップ
var errorMessages = map[string]string{
"required": "この項目は必須です。",
"email": "有効なメールアドレスを入力してください。",
"eqfield": "パスワードが一致しません。",
}
ここから、フィールドとメッセージそれぞれのmapのキーを紐づくバリューを取り出します。
main.go
fieldName := fieldNameMap[err.Field()]
msg := errorMessages[err.Tag()]
errors[fieldName] = msg
ここでエラーが発生すると、以下のようにエラーメッセージが出力されます。
{
"お名前": "この項目は必須です。",
"確認用パスワード": "パスワードが一致しません。"
}
無事に日本語化することができました。
メリット・デメリット
-
メリット
- カスタマイズしやすい
- 追加のライブラリが不要
-
デメリット
- フィールドが増えるたびに、日本語のマッピングを手動で追加する必要がある
- 同じフィールド名の置換処理を複数の場所で書くと、管理が面倒になる
go-playground
の翻訳機能活用
方法2:go-playgroundには、翻訳機能を提供するライブラリが存在します。
それらを使い、例えば以下のように実装することができます。(一部抜粋)
main.go
e.POST("/create", func(c echo.Context) error {
req := RequestUser{}
if err := c.Bind(&req); err != nil {
return c.JSON(http.StatusBadRequest, err)
}
validation := validator.New()
japanese := ja.New()
uni := ut.New(japanese, japanese)
trans, _ := uni.GetTranslator("ja")
validation.RegisterTranslation("required", trans, func(ut ut.Translator) error {
return ut.Add("required", "{0}は必須項目です。", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
field := fieldNameMap[fe.Field()]
t, _ := ut.T("required", field)
return t
})
validation.RegisterTranslation("email", trans, func(ut ut.Translator) error {
return ut.Add("email", "{0}は有効なメールアドレスを入力してください。", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
field := fieldNameMap[fe.Field()]
t, _ := ut.T("email", field)
return t
})
validation.RegisterTranslation("max", trans, func(ut ut.Translator) error {
return ut.Add("max", "{0}は最大{1}文字までです。", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
field := fieldNameMap[fe.Field()]
t, _ := ut.T("max", field, fe.Param())
return t
})
validation.RegisterTranslation("eqfield", trans, func(ut ut.Translator) error {
return ut.Add("eqfield", "{0}は{1}と一致しなければなりません。", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
field := fieldNameMap[fe.Field()]
t, _ := ut.T("eqfield", field, fe.Param())
return t
})
if err := validation.Struct(req); err != nil {
errors := map[string]string{}
for _, err := range err.(validator.ValidationErrors) {
errors[fieldNameMap[err.Field()]] = err.Translate(trans)
}
return c.JSON(http.StatusBadRequest, errors)
}
// 登録処理は省略
})
処理解説
-
日本語ロケールを設定
japanese := ja.New()
- github.com/go-playground/locales/jaを利用
-
翻訳管理オブジェクトを作成
uni := ut.New(japanese, japanese)
- 第一引数にはデフォルトの言語を、第二引数にはサポートする言語を設定(複数設定可能)
- github.com/go-playground/universal-translatorを利用
-
日本語翻訳オブジェクトを作成
trans, _ := uni.GetTranslator("ja")
- バリデーションエラーの翻訳に使う
-
カスタムバリデーションの設定
-
日本語のカスタムバリデーションメッセージを登録
validation.RegisterTranslation("バリデーションルール", 翻訳オブジェクト, 登録処理, 翻訳処理)
-
登録処理
ut.Add("ルール名", "メッセージテンプレート", 上書きフラグ)
-
{0}
や{1}
はプレースホルダー - 上書きフラグを
true
にすることで、上書きをすることができる
-
翻訳処理
ut.T("ルール名", パラメータ)
- 先ほどのプレースホルダーにパラメータ(複数設定可)を設定
- 今回はフィールド名をmapから取得するよう設定
-
日本語のカスタムバリデーションメッセージを登録
main.go
validation.RegisterTranslation("required", trans, func(ut ut.Translator) error {
return ut.Add("required", "{0}は必須項目です。", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
field := fieldNameMap[fe.Field()]
t, _ := ut.T("required", field)
return t
})
validation.RegisterTranslation("email", trans, func(ut ut.Translator) error {
return ut.Add("email", "{0}は有効なメールアドレスを入力してください。", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
field := fieldNameMap[fe.Field()]
t, _ := ut.T("email", field)
return t
})
validation.RegisterTranslation("max", trans, func(ut ut.Translator) error {
return ut.Add("max", "{0}は最大{1}文字までです。", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
field := fieldNameMap[fe.Field()]
t, _ := ut.T("max", field, fe.Param())
return t
})
validation.RegisterTranslation("eqfield", trans, func(ut ut.Translator) error {
return ut.Add("eqfield", "{0}は{1}と一致しなければなりません。", true)
}, func(ut ut.Translator, fe validator.FieldError) string {
firstParam := fieldNameMap[fe.Field()]
secondParam := fieldNameMap[fe.Param()]
t, _ := ut.T("eqfield", firstParam, secondParam)
return t
})
-
エラーメッセージの出力
- Translateメソッドを使うことで、翻訳されたエラーメッセージを出力するよう設定することができる
- 翻訳するための言語とそのメッセージを管理している
main.go
if err := validation.Struct(req); err != nil {
errors := map[string]string{}
for _, err := range err.(validator.ValidationErrors) {
errors[fieldNameMap[err.Field()]] = err.Translate(trans)
}
return c.JSON(http.StatusBadRequest, errors)
}
ここでエラーが発生すると、以下のようにエラーメッセージが出力されます。
{
"お名前": "お名前は必須項目です。",
"確認用パスワード": "確認用パスワードはパスワードと一致しなければなりません。"
}
無事に日本語化することができました。
メリット・デメリット
-
メリット
- カスタマイズしやすい
- 国際化対応がしやすい
-
デメリット
- メンテナンスに手間がかかる
- 翻訳処理をしているのでパフォーマンスへの影響が考えられる
まとめ
今回はgo-playground/validator日本語化の方法を2つ紹介しました。
他にも翻訳できる方法がないか、調べてみたいと思います!
Discussion