Hibernate ValidatorでValidationMessages.properties以外のファイルを使う

# Hibernate Validatorは相変わらず便利
こうしきさいと: <a href="http://hibernate.org/validator/" target="_blank">Hibernate Validator</a>

# ValidationMessages.properties一つでは無理ぽ

Hibernate Validatorではライブラリのデフォルトの `ValidationMessages.properties` や、ユーザー指定の `/path/to/resources/ValidationMessages.properties` が<a href="http://docs.jboss.org/hibernate/validator/4.3/reference/en-US/html/validator-usingvalidator.html#section-message-interpolation" target="_blank">デフォルトで使用されます</a>。

というわけで、<a href="http://docs.jboss.org/hibernate/validator/4.3/reference/en-US/html/validator-usingvalidator.html#example-validator-validate" target="_blank">普通に使っていたら</a>他の `*.properties` を使えません。

特段困ることはありませんが、肥大化して `ValidationMessages.properties` だけでは管理が面倒になる未来が見えてくることがあります。全メッセージを（言語別とはいえ）一つのファイルにまとめているとキモい展開に...というのが嫌だったので作りました。下記の例では `/path/to/resources/com/example/hoge/dto/FormData/ValidationMessages.properties` が言語に応じて読み込まれてメッセージに使用されます。

```java:CustomMessageInterpolator.java
package com.example.hoge;

import java.util.Locale;
import javax.validation.MessageInterpolator;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator;

public class CustomMessageInterpolator implements MessageInterpolator {

    private Locale locale;
    private MessageInterpolator mi;

    public CustomMessageInterpolator(String resourceName) {
        this.locale = null;
        this.mi = new ResourceBundleMessageInterpolator(
            new PlatformResourceBundleLocator(resourceName)
        );
    }

    public CustomMessageInterpolator(String resourceName, Locale locale) {
        this.locale = locale;
        this.mi = new ResourceBundleMessageInterpolator(
            new PlatformResourceBundleLocator(resourceName)
        );
    }

    @Override
    public String interpolate(String m, Context c) {
        return this.locale == null ?
            mi.interpolate(m, c) :
            mi.interpolate(m, c, this.locale);
    }

    @Override
    public String interpolate(String m, Context c, Locale l) {
        // 引数のLocaleを使用するとシステムのLocaleが使われてしまい、
        // コンストラクタで指定したLocaleが効かない
        return this.locale == null ?
            mi.interpolate(m, c, l) :
            mi.interpolate(m, c, this.locale);
    }
}
```

こんな感じで使います。言語別にする必要がなければLocale不要版を使用すればOKですね。

```java
import com.example.hoge.CustomMessageInterpolator;
import com.example.hoge.dto.FormData;

import java.util.Locale;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import lombok.extern.slf4j.Slf4j;

(略)

        // lang は ja とか en とかそういうの
        // formData は JavaBean をよしなに用意
        Validator validator = Validation.byDefaultProvider().configure().messageInterpolator(
            new CustomMessageInterpolator(
                "com.example.hoge.dto.FormData.ValidationMessages", new Locale(lang)
            )
        ).buildValidatorFactory().getValidator();

        // validation
        Set<ConstraintViolation<FormData>> constraintViolations =
            validator.validate(formData, formData.validateGroups());
        if (constraintViolations.size() > 0) {
            for (ConstraintViolation<FormData> con : constraintViolations) {
                // getPropertyPath -> プロパティ名, getMessage -> エラーメッセージ
                log.warn(con.getPropertyPath().toString() + "=" + con.getMessage());
            }
        }

(略)
```

※しれっと `formData.validateGroups()` とか書いてますが「項目Aが選択されてたら項目Bは必須」のようなバリデーションに使うアレのためのものです。あんまり見かけないのでまた別の記事にしようかと思います。

参考情報はこの辺りに↓↓
<a href="https://docs.jboss.org/hibernate/validator/4.0.1/reference/en/html_single/#validator-usingvalidator-validationgroups" target="_blank">Validating groups - Hibernate Validate</a>

# 参考
<a href="http://hibinohirokatsu.cocolog-nifty.com/blog/2014/08/hibernate-valid.html" target="_blank">Hibernate Validatorでエラーメッセージを多言語対応する: nonkiのブログ</a>

# おわりに
~~Javaひっさびさすぎてやばい~~