vee-validate
vee-validate copied to clipboard
@vee-validate/i18n locale messages incompatible with vue-18n
What happened?
@vee-validate/i18n use of params for example in en.json messages.min is: The {field} field must be 0:{width} pixels by 1:{height} pixels
Is incompatible with vue-i18n format for array which is: The {field} field must be {0} pixels by {1} pixels
Is there any transformation tool to make it compatible?
Reproduction steps
...
Version
Vue.js 3.x and vee-validate 4.x
What browsers are you seeing the problem on?
- [ ] Firefox
- [ ] Chrome
- [ ] Safari
- [ ] Microsoft Edge
Relevant log output
No response
Demo link
Code of Conduct
- [X] I agree to follow this project's Code of Conduct
In v3 it used to be compatible since vee-validate tracked the parameter names and order. Now it doesn't make everything a little lighter.
Unfortunately, there is no way to have a locale compatible with i18n out of the box that works with object and string rules at the same time, at least the time of the implementation then. I will take another look this weekend to see if there something I can do.
As a workaround, you can copy the files and re-write them in a way that you are 100% sure to stick with (either string or array format, not both). Or have a regex pattern that strips the indices information before passing them to i18n.
I used following code to convert the 0:{xxx} 1:{yyy} to {param0} and {param1} respectively. Hope it helps anyone who needs this:
import en from '@vee-validate/i18n/dist/locale/en.json'
import ms_MY from '@vee-validate/i18n/dist/locale/ms_MY.json'
import zh_CN from '@vee-validate/i18n/dist/locale/zh_CN.json'
const veeValidateLanguages = {
en: en,
ms_MY: ms_MY,
zh_CN: zh_CN
}
let regexp = /([0-9]?):\{[A-Za-z]+\}/g
for (var lang in veeValidateLanguages) {
for (var key in veeValidateLanguages[lang].messages) {
veeValidateLanguages[lang].messages[key] = veeValidateLanguages[
lang
].messages[key].replace(regexp, '{param$1}')
}
}
Here's my implementation for integrating vue-i18n, this is the generateMessage function I pass to the configure:
const generateMessage = ({ field, rule }) => {
const name = i18n.global.t(`fieldNames.${field}`, field);
return i18n.global.t(`validationMessages.${rule.name}`, [name, ...rule.params]);
};
Since we're using vue-i18n's list interpolation you will need to format your messages like so:
{
"fieldNames": {
"email": "Email",
"password": "Password"
},
"validationMessages": {
"required": "{0} is required.",
"email": "{0} must be a valid email address.",
"min": "{0} must be at least {1} characters."
},
}
The field name translation is passed as {0} and rule parameters {1}, {2}, {3}...
Hello @martinhipp Your implementation looks like solution for my issue. But after implementing it I didn't achieve the fix. I think I did wrong implementation. Can you please create a demo for this?
@devCodeHub-star: Here's my latest version of the implementation in TypeScript:
import { configure } from "vee-validate";
import type { FieldValidationMetaInfo } from "@vee-validate/i18n";
import i18n from "@/plugins/vue-i18n";
function generateI18nMessage({ rule, label, name }: FieldValidationMetaInfo) {
const fieldName = label || i18n.global.t(`fieldLabels.${name}`);
if (!Array.isArray(rule.params)) {
return i18n.global.t(`validationMessages.${rule.name}`, [fieldName]);
}
return i18n.global.t(`validationMessages.${rule.name}`, [
fieldName,
...rule.params,
]);
}
configure({
generateMessage: generateI18nMessage,
});
This is my workaround when using vee-validate with a validation function that returns a tranlsated string:
const form = useForm();
watch(() => vueI18n.locale, () => {
for (const fieldName in form.errorBag.value) {
form.validateField(fieldName);
}
});
I'm not happy with it, but it works. The drawback is you have to apply this fix for every form.
Hello @martinhipp Thanks for previous solution. That solved major problem.
I am upgraded to v4 and I have this use case.
import { defineRule } from 'vee-validate'
import { isEqual } from 'lodash'
defineRule('isDifferent', (value, { target }, ctx) => {
if (isEqual(target, value)) {
return `The ${ctx.field} must be different from ?`
}
return true
})
How I can get access of target field name without defining it manually ? In v3 I am able to get access by message function like below code
import { extend } from 'vee-validate'
import { isEqual } from 'lodash'
extend('isDifferent', {
params: ['target'],
validate: (value, { target }) => {
return !isEqual(target, value)
},
message(field, values) {
return i18n.t('error.notEqaul', values)
},
})
And i18n message was The {_field_} must be different from {target}
Thanks