logaretm / vee-validate

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

@vee-validate/i18n locale messages incompatible with vue-18n #3684

Open kokhoor opened 2 years ago

kokhoor commented 2 years ago

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

1. 2. 3. ...

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

-

Code of Conduct

logaretm commented 2 years ago

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.

kokhoor commented 2 years ago

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}')
  }
}
martinhipp commented 1 year ago

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}...

devCodeHub-star commented 10 months ago

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?

martinhipp commented 10 months ago

@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,
});
3dyuval commented 10 months ago

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.

devCodeHub-star commented 9 months ago

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

Jamshidbekdev commented 4 months ago

Hello @logaretm. How to overwrite error message of an existing rule for a specific field not like global? in 3.x version it has customMessages prop.