Vue.js+Vuetify.jsを使ったWEBアプリケーション構築 ～多言語対応(VeeValidate)～

# はじめに
前回、Vueアプリケーションの多言語対応として、Vue I18nプラグインの設定および使い方を「[Vue.js+Vuetify.jsを使ったWEBアプリケーション構築 ～多言語対応(Vue I18n)～](https://qiita.com/pmtech-masayuki-miyata/private/bc653832a7293c01de9a)」で、説明しました。
今回はVeeValidate（入力値バリデーションコンポーネントライブラリ）のバリデーションメッセージの多言語対応について、以前投稿した記事「[Vue.js+Vuetify.jsを使ったWEBアプリケーション構築](https://qiita.com/pmtech-masayuki-miyata/items/e884eb3c7608cc266226)」のサンプルアプリケーションを基に説明したいと思います。

# まえおき
VeeValidateのバリデーションメッセージを多言語対応するにあたり、以下の2点を実現したいと考えていました。

* バリデーションメッセージとVueI18nメッセージを一元管理する
* 言語切替の際にバリデーションメッセージもリアクティブに変更される

VeeValidateのバリデーションメッセージの多言語対応方法は、いくつかあるようで公式ページで紹介されています。
https://logaretm.github.io/vee-validate/guide/localization.html

上記公式ページに記載されているVueI18nと連携する以下の方法で実装して動かしてみました。

```javascript:公式ページより引用
configure({
  defaultMessage: (field, values) => {
    // override the field name.
    values._field_ = i18n.t(`fields.${field}`);

    return i18n.t(`validation.${values._rule_}`, values);
  }
});
```
![素材１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/517349/14f8f9a7-3efa-a143-a517-8fadcdbcc439.png)

残念なことに**バリデーションメッセージがリアクティブに変更されません**でした。

ということで、以下、バリデーションメッセージもリアクティブに変更される方法をサンプルアプリケーションを基に説明します。

# 説明
今回行ったVeeValidateのバリデーションメッセージの多言語対応は、一言で言うと
**「バリデーションメッセージとして返却するものをメッセージコードにする」**
という対応になります。

では、具体的にソースコードを用いて説明していきます。
以下の順に説明します。

1. VueI18nメッセージ情報にバリデーションメッセージ追加
2. VeeValidateのバリデーションメッセージ上書き
3. ミックスインオブジェクトの定義
4. バリデーションメッセージの表示

## 1. VueI18nメッセージ情報にバリデーションメッセージ追加

```javascript:src/configs/message.json
{
    "ja": {
        // ・・・省略
        "message": {
            // ・・・省略
            "M00007": "必ず入力してください。",
            "M00008": "{0}文字以内で入力してください。",
            "M00009": "メールアドレスの形式が不正です。入力内容を確認してください。",
            // ・・・省略
        }
    },
    "en": {
        // ・・・省略
        "message": {
            // ・・・省略
            "M00007": "This field must be entered.",
            "M00008": "Maximum {0} characters.",
            "M00009": "E-mail address format is incorrect. Please check and enter again.",
            // ・・・省略
        }
    }
}
```

## 2. VeeValidateのバリデーションメッセージ上書き

```javascript:src/vee-validate.js
import { extend, configure, setInteractionMode } from "vee-validate";
import { required, email, max } from "vee-validate/dist/rules";

setInteractionMode('custom', () => {
  // デフォルトのチェックタイミングをフォーカスアウト時にする
  return { on: ['blur'] };
});

configure({
  mode: 'custom'
});

// バリデーションメッセージを上書きする。
// メッセージにコード値を設定するが、パラメータが必要なメッセージもある為、オブジェクトを定義する。
// VeeValidateではmessageは文字列しか返せない仕様となっているので、
// メッセージ情報オブジェクトをJSON文字列化する。

// requiredをカスタマイズ
required.message = () => JSON.stringify({code: 'message.M00007'});

// maxをカスタマイズ
max.message = (_, values) => JSON.stringify({code: 'message.M00008', args: [values.length]});

// emailをカスタマイズ
email.message = () => JSON.stringify({code: 'message.M00009'});

// ・・・省略

// 有効にする検証処理
extend("required", required);
extend("email", email);
extend("max", max);
extend("phone", phone);

```

## 3. ミックスインオブジェクトの定義
JSON文字列をオブジェクトに戻す処理を独自に実装します。
Vueコンポーネントで使いやすいようにVueのミックスインオブジェクトとして定義します。

```javascript:src/mixins/ValidationHelper.js
const ValidationHelper = {
  methods: {
    // VeeValidateのエラーオブジェクトからメッセージコードを返却する
    getValidateErrorMessageCode(errors) {
      let obj = this.getValidateErrorMessageObject(errors);

      return (obj !== null) ? obj.code : '';
    },
    // VeeValidateのエラーオブジェクトからメッセージパラメータを返却する
    getValidateErrorMessageArgs(errors) {
      let obj = this.getValidateErrorMessageObject(errors);
      return (obj !== null && obj.args) ? obj.args : [];
    },
    // VeeValidateのエラーオブジェクトから取得できるJSON文字列をパースしてメッセージオブジェクトを返却する
    getValidateErrorMessageObject(errors) {
      if (!errors || errors.length < 1) {
        return null;
      }
      // とりあえず、エラーメッセージを１つだけ表示するので、最初のエラーのみ対応
      // エラーメッセージを全て表示する要件がある場合は別途対応が必要。
      return JSON.parse(errors[0]);
    },
  }
}

export default ValidationHelper;
```

## 4. バリデーションメッセージの表示
メッセージ表示の際に上記で定義したValidationHelperを利用します。

```vue:src/components/parts/NyuryokuCard.vue
<template>
  <v-card>
    <v-card-text>
      <div class="subheading">{{ $t(`caption['登録情報']`) }}</div>
      <hr />
      <validationObserver ref="obs">
        <div  class="pt-2">
          <ValidationProvider
            ref="provider"
            mode="lazy"
            rules="required|max:20"
            v-slot="{ errors, valid }"
          >
            <!-- VueI18nの$tメソッドにメッセージコードとパラメータを渡す。その際にValidationHelperを利用する。-->
            <v-text-field
              v-model="torokuJoho.name"
              :label="$t(`caption['名前']`)"
              counter="20"
              class="px-2"
              :placeholder="$t('caption[\'サンプル太郎\']')"
              :error-messages="$t(getValidateErrorMessageCode(errors), getValidateErrorMessageArgs(errors))"
              :success="valid"
            />
          </ValidationProvider>
        </div>
        <!-- 省略 -->
      </validationObserver>
    </v-card-text>
  </v-card>
</template>

<script>
import ValidationHelper from '@/mixins/ValidationHelper';
import {ValidationProvider, ValidationObserver} from 'vee-validate';

export default {
  // ・・・省略
  // ValidationHelperを利用する宣言
  mixins: [ValidationHelper],
  // ・・・省略
}
</script>
```

それでは実行してみましょう。
![素材２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/517349/e58ec99b-7999-f622-15d4-a92d90c6cc70.png)

無事、バリデーションメッセージがリアクティブに変更されました。


# おわりに
今回、バリデーションメッセージをリアクティブに変更する為に独自実装という手段を取りましたが、筆者には情報を見つけられなかっただけでもっとスマートな解決方法が他にあるかもしれません。
どうしても同様なことをやりたくて、この記事に辿り着いた方の参考になれば幸いです。