Localize your FastAPI validation message

whchi

whchi

Posted on August 17, 2023

Localize your FastAPI validation message

When working on website frontend development, a significant amount of time is spent on handling form validation. If your service caters to a global audience (not only within your country), it's advisable to incorporate internationalization (i18n) error messages into it.

If the situation involves frontend-backend separation, managing the validation i18n message communication becomes a troublesome task.

The question arises: should the validation messages be managed on the backend or the frontend?

Generally, messages should be managed closer to the rendering (frontend mostly) interface for better organization. A common approach is to use error codes paired with corresponding messages.

However, there are numerous validation methods for forms, and if error codes were applied to forms, maintenance would become quite challenging. Therefore, it's best to centrally manage form-type validation by either the frontend or the backend.

FastAPI employs Pydantic to encapsulate most scenarios of form validation and provides corresponding error messages. This works well in a purely English environment, but if we require i18n, it's clear that this approach falls short.

This article will work through the whole concept of how to integrate i18n into FastAPI's request validation error messages with pseudo code.

middleware

Firstly we need to know what locale it is, we can archive this using middleware

class LocaleMiddleware:
   async def dispatch(req, call_next):
       req.locale = incoming_locale
       return await call_next(req)
app.add_middleware(LocaleMiddleware)
Enter fullscreen mode Exit fullscreen mode

exception handler

Then we register our own exception handler to handle RequestValidationError

async def i18n_exception_handler(request, exc):
     msg = make_i18n_msg(exc, request.state.locale)
     return JSONResponse({"errors": exc}, status=422)

app.add_exception_handler(RequestValidationError, i18n_exception_handler)
Enter fullscreen mode Exit fullscreen mode

translate

Finally, we make translate action, the exc of FastAPI is object of object, so we need to extract the message recursively

def make_i18n_msg(exc, locale):
   if isinstanceof(exc, wrapper):
     return make_i18n_msg(exc, locale)
   return Translator(locale).t('trans.file.key')
Enter fullscreen mode Exit fullscreen mode

And we need a translation file looks like

// zh-TW.json
{
  "field required": "欄位必填",
  "extra fields not permitted": "不允許額外的欄位",
}
// ja-JP.json
{
  "field required": "フィールドは必須です",
  "extra fields not permitted": "余分なフィールドは許可されていません",
}
Enter fullscreen mode Exit fullscreen mode

Then when you send with locale to your API pydantic validation, you will get something like

{
  "errors": [
    {
      "loc": [
        "body",
        "string"
      ],
      "msg": "フィールドは必須です",
      "type": "value_missing.field_required"
    },
   {
      "loc": [
        "body",
        "string"
      ],
      "msg": "余分なフィールドは許可されていません",
      "type": "value_error.extra_fields_are_not_permitted"
    }
}
Enter fullscreen mode Exit fullscreen mode

That's the essence of it, and I've written a package for it.

fastapi-validation-i18n

You can accomplish this by adhering to the documentation, contributions are also encouraged and welcome.

💖 💪 🙅 🚩
whchi
whchi

Posted on August 17, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related