logaretm / vee-validate

✅ Painless Vue forms
https://vee-validate.logaretm.com/v4
MIT License
10.71k stars 1.25k forks source link

Error message localization is not reactive (`setLocale`, `@vee-validate/i18n`, and `vue-i18n`) #4672

Open Robo-Rin opened 6 months ago

Robo-Rin commented 6 months ago

What happened?

Error messages are not reactive to locale changes like they used to be in v2. We need this functionality because we have a global locale selector button that changes the app locale. When the form is dirty and they change the locale, we need to still show the validation error messages without resetting the validation state.

There was a similar closed issue: https://github.com/logaretm/vee-validate/issues/3876 , but I don't agree with the conclusion that it's a rare scenario and our application needs this as per our product's requirements.

It would be ideal if there was a way to pass the reactive locale from vue-i18n or any source really and have vee-validate watch that ref for changes, and when it changes it would switch locales. Currently, the setLocale function from @vee-validate/i18n does not suffice.

Reproduction steps

  1. Install dependencies dependencies: @vee-validate/i18n, @vee-validate/rules, vue, vue-i18n, vee-validate devDependencies: @vitejs/plugin-vue, typescript, vite, vue-tsc

  2. main.ts

    import { createApp } from 'vue';
    import { configure, defineRule } from 'vee-validate';
    import * as rules from '@vee-validate/rules';
    import { localize } from '@vee-validate/i18n';
    import { setLocale } from '@vee-validate/i18n';
    import en from '@vee-validate/i18n/dist/locale/en.json';
    import es from '@vee-validate/i18n/dist/locale/es.json';
    import App from './App.vue';
    import i18n from './i18n';
    
    defineRule('required', rules.required);
    
    configure({
      generateMessage: localize({
        en,
        es,
      }),
    });
    
    setLocale('en');
    
    const app = createApp(App);
    app.use(i18n);
    
    app.mount('#app');
  3. i18n.ts

    import { createI18n } from 'vue-i18n';
    import messages from './content';
    
    const i18n = createI18n({
      legacy: false,
      locale: 'en-US',
      fallbackLocale: 'en',
      messages,
      fallbackWarn: false,
      missingWarn: false,
    });
    
    export default i18n;
  4. App.vue [...] (For more app code, etc., see the Stackblitz demo)

Version

Vue.js 3.x and vee-validate 4.x

What browsers are you seeing the problem on?

Relevant log output

No response

Demo link

https://stackblitz.com/edit/vee-validate-issue-template-tg6i43?file=src%2FApp.vue

Code of Conduct

Robo-Rin commented 5 months ago

@logaretm hoping to escalate this if possible. Or if you can help point me in the right direction that you want to take this library then I might be able to work on this myself even.

blouflashdb commented 1 month ago

Same here.

blouflashdb commented 1 month ago

I am not even able to change the locale using setLocale. I tried to put it in the main.ts but changing the locale to 'de' seems to have no effect. Maybe someone can point me to the issue. Here is a demo: https://stackblitz.com/edit/vee-validate-i18n?file=src%2FApp.vue

Sector6759 commented 1 month ago

@blouflashdb If you consult the vee-validate localization guide, you can see that

This guide only addresses generating error messages for globally defined validators using vee-validate’s own @vee-validate/i18n plugin.

If you are using vee-validate with yup, then you should check out yup’s localization guide. If you are using another library for data validation, check their i18n capabilities.

There is a localization library for zod which you can use.

I forked your StackBlitz demo and adjusted it to use that library.

Note: changing the locale by calling changeLocale() does not reactively update already rendered error messages. The form has to be re-validated to show the new localized error messages, but maybe you can just programmatically call the validation after changing the locale.

Robo-Rin commented 4 weeks ago

@logaretm Can we please get an update on this issue? Or could you point me in the right direction and I can potentially work on the changes myself? Thanks!

Sector6759 commented 4 weeks ago

@Robo-Rin Maybe you could programmatically trigger the validation again after changing the locale.

Robo-Rin commented 3 days ago

@Sector6759 yeah that's the idea, but I wanted to understand if that follows the correct architecture that @logaretm envisioned.

logaretm commented 3 days ago

Sorry for the delay on addressing this. While I think this is nice to have, I don't think it can be done in all cases especially with the flexible validation options in v4.

To elaborate:

So seeing that most users of this library would require a re-validation on locale change, then I think it is viable to leave it to be user-land and recommend that you re-validate for now.

Alternatively, you can make sure that the localization is outside vee-validate system by letting vee-validate generate locale message keys instead of actual messages. Then pipe those messages using vue-i18n or other localization solutions and it should work perfectly. I followed that approach for localizing zod and yup schemas in some projects I had.

const form = useForm({
  validationSchema: {
    name: yup.string().required('validation.required'),
  },
})
<template>
  {{ t(errorMessage) }}
</template>

Given that you use @vee-validate/rules, maybe you could create a locale file that has the string keys for the messages instead of the message itself and pass vee-validate's locale JSONs to i18n.

Something like that:

// keyed dictionary
cons dict = {
  "messages": {
    "_default": "validation.messages._default",
    "alpha": "validation.messages.alpha",
   // and so on...
};

localize('en', dict);

Do you think this would work better for you? It would even wouldn't require you to change the locale on vee-validate's side ever again.