amannn / next-intl

🌐 Internationalization (i18n) for Next.js
https://next-intl-docs.vercel.app
MIT License
2.34k stars 209 forks source link

form validation error message translation / using the translate function outside of React #96

Closed BernardA closed 2 years ago

BernardA commented 2 years ago

Is your feature request related to a problem? Please describe. There may be a simple solution for this but I could not figure it out. Found some applicable to other libraries and using Yup, but nothing straight forward.

When using Formix or redux-form, the validation is done by functions attached to each field by the validate key, as below:

  <Field
       component={InputText}
        name="email"
        type="email"
        label={t('email')}
        variant="outlined"
        validate={composeValidators(required, isEmail)}
         autoFocus
     />

In the case above, required and isEmail are functions that are placed on a separate file, as they are used elsewhere on the app:

on /src/tools/validator.ts

     export const required: ValidatorFunction = (value) => {
          return value ? undefined : 'Required field';
      };

     export const isEmail: ValidatorFunction = (value) =>
value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)
    ? 'invalidEmail'
    : undefined;

Formix and redux-form automatically call those functions and provide them with value. The functions then return a string message or undefined, as the case may be.

So the question is how to translate those validation error messages and, more generally, how to translate strings returning from functions and not React components.

Describe the solution you'd like Be able to translate the validation error messages within next-intl set up.

Describe alternatives you've considered As mentioned some other libraries seem to solution for that, but I would like to stick around.

Additional context.

amannn commented 2 years ago

Hi @BernardA and thanks for the question!

The usage of the translate function is really coupled to React, because it needs global configuration that is received from the provider. So there's currently no way to use it completely outside of the context of React. IMO that is a necessary restriction if we want to avoid modifying a shared global state.

That said, I think you can get this to work by generating your validators on the React side.

E.g. an implementation like this:

function useValidators() {
  const t = useTranslations(...);

  return {
    required(value) {
      return value ? undefined : t('required');
    },

    isEmail(value) {
      return value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)
        ? t('invalidEmail')
        : undefined;
    }
  };
}

… can be used like this in a shared way in your components:

const {required, isEmail} = useValidators();

If code splitting is a concern, you can of course also create several hooks with validators.

Let me know if this helps!

BernardA commented 2 years ago

Works great! Much appreciated, thanks!

amannn commented 1 year ago

As this question has come up more often since then, I've now written a blog post on the design background of this: How (not) to use translations outside of React components.

Hope this helps future readers that stumble over this issue!