-
-
Notifications
You must be signed in to change notification settings - Fork 167
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
Validation errors localization #414
Comments
@lordspinach I have zero experience doing i18n in Go. I've done it in Python programs in the past using gettext, but am not entirely sure where to start, so I'm open to discussion on this one. I think it would be nice:
Thanks! |
@danielgtaylor As an example solution i can suggest something like this from my current app:
Here is an example of usage:
This implementation pretend to use ctx for load any of locale. As we load all locales into map at app start we keeps allocations on request at the same level. It allows us to get locale based on request's |
The way I approached it is that I overrode the error messages that I was using with translation keys: func overrideHumaValidationMessages() {
validation.MsgExpectedMinimumNumber = "#Translate#ErrorExpectedMinimumNumber,%v#" // "expected number >= %v"
validation.MsgExpectedExclusiveMinimumNumber = "#Translate#ErrorExpectedExclusiveMinimumNumber,%v#" // "expected number > %v"
validation.MsgExpectedMaximumNumber = "#Translate#ErrorExpectedMaximumNumber,%v#" // "expected number <= %v"
validation.MsgExpectedExclusiveMaximumNumber = "#Translate#ErrorExpectedExclusiveMaximumNumber,%v#" // "expected number < %v"
validation.MsgExpectedNumberBeMultipleOf = "#Translate#ErrorExpectedNumberBeMultipleOf,%v#" // "expected number to be a multiple of %v"
validation.MsgExpectedMinLength = "#Translate#ErrorExpectedMinLength,%d#" // "expected length >= %d"
validation.MsgExpectedMaxLength = "#Translate#ErrorExpectedMaxLength,%d#" // "expected length <= %d"
validation.MsgExpectedRFC5322Email = "#Translate#ErrorExpectedEmail,%v#" // "expected string to be RFC 5322 email: %v""
} I then created a transformer: func ErrorResponseTranslationTransformer(ctx huma.Context, status string, v any) (any, error) {
statusInt, err := strconv.Atoi(status)
if err != nil {
panic(fmt.Sprintf("invalid status %s", status))
}
// no error? then who cares
if statusInt < 400 {
return v, nil
}
var apiErr *types.ApiErrorModel
var ok bool
if apiErr, ok = v.(*types.ApiErrorModel); !ok {
return v, nil
}
baseCtx := ctx.Context()
// loop through things and replace
{
apiErr.Code = translateErrors(baseCtx, apiErr.Code)
apiErr.Message = translateErrors(baseCtx, apiErr.Message)
apiErr.Detail = translateErrors(baseCtx, apiErr.Detail)
// .Errors
for x, thisErr := range apiErr.Errors {
thisErr.Message = translateErrors(baseCtx, thisErr.Message)
apiErr.Errors[x] = thisErr
}
// .Fields recursively
apiErr.Fields = translateFields(baseCtx, apiErr.Fields)
}
return apiErr, nil
}
var translatePattern = regexp.MustCompile(`#Translate#(\w+)(?:,([^#]+))?#`)
// translateErrors uses the above messages and searches and replaces for them. for example, it will look for
// "#Translate#ErrorExpectedMaxLength,6#" and replace it with the results of
func translateErrors(ctx context.Context, message string) string {
// Find all matches
matches := translatePattern.FindAllStringSubmatch(message, -1)
for _, match := range matches {
var errorKey string
params := map[string]string{}
if len(match) >= 2 {
// Extract the key
errorKey = match[1]
// Extract the parameter, if present
param := ""
if len(match) == 3 && match[2] != "" {
param = match[2]
}
// Prepare the translation params
if param != "" {
params["n"] = param
params["s"] = param
}
// Perform the translation (assuming apitranslator.TranslateWithParams exists)
translatedMessage := apitranslator.TranslateWithParams(ctx, errorKey, params)
// Replace the entire tag with the translated message
message = strings.ReplaceAll(message, match[0], translatedMessage)
}
}
return message
} and then added that transformer to the huma config. config.Transformers = append(config.Transformers, ErrorResponseTranslationTransformer) Felt a bit hacky, but it worked. |
Hello!
I'm facing a problem, I want to localize my application based on HUMA and for that I have to implement Registry, Schema and PathBuffer with almost identical logic in the validation part. Another solution is to disable HUMA validation altogether and implement something else. So my question is. Do you plan to implement internationalization in HUMA? Maybe you already have some thoughts on this and would be willing to share them so I can help you implement this feature. Or, conversely, why you don't want to do it. I think this feature would be awesome and am open to discussing it.
Best regards
The text was updated successfully, but these errors were encountered: