How to internationalize a Yup validation schema in a React, Formik and react-i18next app

Internationalization of Yup validation schema with Formik and react-18next

Anybody that had to deal with internationalization (shortened as i18n) knows that it can be a real pain if not handled properly, but thankfully in the modern Javascript stack, we have libraries that remove a lot of that pain and actually make it a breeze. One of these libraries is i18next. If you work with React, you can’t do better than its react-i18next port for the way it seamless exposes the i18next api using all of the major React constructs (Hooks, HOCs, Render Props and Components). Formik is form management library that promises to allow you to “build forms in React, without the tears” and I can say that so far indeed, my eyes have remained quite dry. Who says form also says validation, and Formik integrates seamlessly with Yup, an JavaScript object schema validator and object parser. Yup supports localization, but you have to provide it with a custom locale object, and I felt that the translation functionality should be the single responsibility of i18next. This article will explore how you can sync the translation of the validation errors in Yup when the user changes the selected page language.

The Bug

In the CodeSandbox below, we have a basic setup of React, react-i18next, Formik and Yup. We display a form with a required email field defined in a Yup schema. This form can also be translated in French. To observe the bug in question in syncing the translation:

Click in the email field

Click outside

You should see a Email is required error.

Now click on the Francaislink.

Everything on the page changes to the French translation except for the validation error.

I suspect this happens because validation schema is initiated when the component renders with the language set to English initially, and on changing the language, the validation is not re-ran, causing the message to stay in English.

The Fix

Luckily the i18next exposes events, in particular the languageChanged event that we can listen to and update the validation so the validation message can be translated. I initially implemented in a global useEffect hook (that I will share later in this article) that listened for this event and re-ran the form validation , but this issue on Github had a more elegant solution, setting all field with an errors to be touched, which should trigger the validation. The code in question is here:

The errors object and setFieldTouched function here are the form.errors and form.setFieldTouched properties of the Formik form object passed in a prop. My requirements were different as I only wanted to show an error if the field had indeed been touched.

Use a hook

In my particular case, I was dealing not only with field level validation bugs, but I also had nested forms where the same was happening. I created a global hook that takes in a Formik form object and sets only the fields that have an error as touched. Here is the code:

This way if the user had previously interacted with the field and gotten a validation error, the translation will re-render the form only with the existing errors translated. You can adjust depending on your business requirements.

WithTranslateFormErrors HOC

I’ve created an HOC that you can add to your Formik form that will set this up for you. The code is pretty simple:

And you would include it in your Formik form like this:

You don’t need to use the HOC if you’re rendering your Formik form as a component. In that case, you can just use the useTranslateFormErrors hook on its own in your render function.

I am CTO at AKIL Technologies, an Abidjan, Ivory Coast tech startup where I lead a team of Angular and React devs ready to contribute on interesting projects. Give us a toot if you need to beef up your front end teams!