Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

err.Translate(trans) only works if translator passed via param (go-playground/validator) #1274

Open
fasaya opened this issue Jun 4, 2024 · 0 comments

Comments

@fasaya
Copy link

fasaya commented Jun 4, 2024

I'm trying to translate the validation errors message because I don't like the default error message. When I pass the trans variable via param from controllers to a helper (to handle validation error), err.Translate(trans) worked just like what I want.

Here is the controllers/handler

package handler

import (
	"base-trade-rest/api/request"
	"base-trade-rest/core/helpers"
	"base-trade-rest/core/model"
	"base-trade-rest/core/service"
	"net/http"

	"github.com/gin-gonic/gin"
	en "github.com/go-playground/locales/en"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	en_translations "github.com/go-playground/validator/v10/translations/en"
)

type ProductHandler struct {
	ProductService service.IProductService
	Validate       *validator.Validate
	Trans          ut.Translator
}

func NewProductHandler(productService service.IProductService) *ProductHandler {

	// NOTE: omitting allot of error checking for brevity
	en := en.New()
	uni := ut.New(en, en)

	// this is usually know or extracted from http 'Accept-Language' header
	// also see uni.FindTranslator(...)
	trans, _ := uni.GetTranslator("en")

	validate := validator.New()
	en_translations.RegisterDefaultTranslations(validate, trans)

	var productHandler = ProductHandler{
		ProductService: productService,
		Validate:       validate,
		Trans:          trans,
	}
	return &productHandler
}

func (h *ProductHandler) Store(ctx *gin.Context) {
	var request request.ProductCreateRequest

	err := ctx.ShouldBind(&request)
	if err != nil {
		helpers.CreateFailedResponse(ctx, http.StatusBadRequest, err.Error())
		return
	}

	err = h.Validate.Struct(request)
	if err != nil {
		helpers.HandleValidationError(ctx, err, h.Trans)
		return
	}


	// Get authenticated user
	userData := helpers.GetAuthUser(ctx)

	product := model.Product{
		Name:     request.Name,
		ImageURL: nil,
		UserID:   userData.ID,
	}

	newUser, err := h.ProductService.CreateProduct(&product)
	if err != nil {
		helpers.CreateFailedResponse(ctx, http.StatusBadRequest, err.Error())
		return
	}

	helpers.CreateSuccessfulResponse(ctx, http.StatusOK, "Product successfully created", newUser)
}

And the helpers

package helpers

import (
	"strings"

	"github.com/gin-gonic/gin"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
)


func getValidationErrors(err error, trans ut.Translator) map[string]string {
	var errors = make(map[string]string)

	errs := err.(validator.ValidationErrors)
	for _, e := range errs {
		errors[strings.ToLower(e.Field())] = e.Translate(trans)
	}

	return errors
}


func HandleValidationError(ctx *gin.Context, err error, trans ut.Translator) {
	CreateValidationErrorResponse(ctx, getValidationErrors(err, trans))
}

The getValidationErrors() returns just like i expected

{
    "name": "Name is a required field"
}

But because i dont want to repeat the code in every controllers/handlers, i created a helper and init the trans there

package helpers

import (
	"strings"

	"github.com/gin-gonic/gin"
	en "github.com/go-playground/locales/en"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	en_translations "github.com/go-playground/validator/v10/translations/en"
)

// use single instances, it caches struct info
var trans ut.Translator

func init() {
	en := en.New()
	uni := ut.New(en, en)
	trans, _ = uni.GetTranslator("en")

	validate := validator.New()
	en_translations.RegisterDefaultTranslations(validate, trans)
}

func getValidationErrors(err error) map[string]string {
	var errors = make(map[string]string)

	errs := err.(validator.ValidationErrors)
	for _, e := range errs {
		errors[strings.ToLower(e.Field())] = e.Translate(trans)
	}

	return errors
}

func HandleValidationError(ctx *gin.Context, err error) {
	CreateValidationErrorResponse(ctx, getValidationErrorsTest(err))
}

And call it from controllers like this without passing the h.Trans

err = h.Validate.Struct(request)
if err != nil {
        helpers.HandleValidationError(ctx, err)
return
}

But it returns something like this which i don't want

{
    "name": "Key: 'ProductCreateRequest.Name' Error:Field validation for 'Name' failed on the 'required' tag"
}

Is there something i missed or is this a wrong way?

@fasaya fasaya changed the title Questions Tags Saves Users Companies LABS Jobs NEW Discussions COLLECTIVES Communities for your favorite technologies. Explore all Collectives TEAMS Ask questions, find answers and collaborate at work with Stack Overflow for Teams. Explore Teams Looking for your Teams? err.Translate(trans) only works if translator passed via param (go-playground/validator) err.Translate(trans) only works if translator passed via param (go-playground/validator) Jun 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant