vee-validate
vee-validate copied to clipboard
Error message localization is not reactive (`setLocale`, `@vee-validate/i18n`, and `vue-i18n`)
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
-
Install dependencies dependencies:
@vee-validate/i18n,@vee-validate/rules,vue,vue-i18n,vee-validatedevDependencies:@vitejs/plugin-vue,typescript,vite,vue-tsc -
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'); -
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; -
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?
- [X] Firefox
- [X] Chrome
- [X] Safari
- [X] Microsoft Edge
Relevant log output
No response
Demo link
https://stackblitz.com/edit/vee-validate-issue-template-tg6i43?file=src%2FApp.vue
Code of Conduct
- [X] I agree to follow this project's Code of Conduct
@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.
Same here.
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
@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.
@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!
@Robo-Rin Maybe you could programmatically trigger the validation again after changing the locale.
@Sector6759 yeah that's the idea, but I wanted to understand if that follows the correct architecture that @logaretm envisioned.
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:
- This can work for the
@vee-validate/rulesand@vee-validate/i18nby emitting a simple event that vee-validate is expecting or the other way around. - This won't work for
zod/yup/valibotor any external schema provider because the messages were already generated and in order to re-generate them we would have to re-validate everything.
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.
@logaretm Thank you for responding, just seeing this. Could you clarify if this solution should currently work or is something we should try to develop? I'm happy with a solution that doesn't need to change the vee-validate locale if that's the direction we'd like to go here.
If this solution should already work, do you mind forking my Stackblitz with an example of what you mean? I'm not quite sure I'm following how to implement your suggestion. i.e. do we still use configure(), would I have to set this config for each country, etc.
Thanks again! 🙇