src/main/java/br/com/rooting/roxana/translator/Translator.java
public interface Translator {

    String INTERPOLATION_PREFIX = "{";

    String INTERPOLATION_SUFFIX = "}";

    String PARAMETER_INTERPOLATION_PREFIX = "[";

    String PARAMETER_INTERPOLATION_SUFFIX = "]";

    String NOT_FOUND_DELIMITER = "???";

    static String getInterpolatedKeyOf(String key) {
        return INTERPOLATION_PREFIX + key + INTERPOLATION_SUFFIX;
    }

    String translate(final String key, final Locale locale, final List<Parameter> parameters);

    default String translate(final String key, final Locale locale, final Parameter... parameters) {
        return this.translate(key, locale, Arrays.asList(parameters));
    }

    default String translate(final String key, final List<Parameter> parameters) {
        return this.translate(key, this.getLocale(), parameters);
    }

    default String translate(final String key, final Parameter... parameters) {
        return this.translate(key, this.getLocale(), parameters);
    }

    Locale getLocale();

}
src/test/java/br/com/rooting/roxana/message/mapper/MockedMessageMapper.java
public class MockedMessageMapper implements MessageMapper {

    private final MessageSeverity severity;

    private final String key;

    public MockedMessageMapper(final String key, final MessageSeverity severity) {
        this.key = key;
        this.severity = severity;
    }

    @Override
    public MessageSeverity getSeverity() {
        return this.severity;
    }

    @Override
    public String getKey() {
        return this.key;
    }

}
src/test/java/br/com/rooting/roxana/UnitTest.java
public abstract class UnitTest<T> {

    @SuppressWarnings("unchecked")
    protected Class<T> getUnitTestClass() {
        ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
        Type actualArgument = parameterizedType.getActualTypeArguments()[0];

        if (ParameterizedType.class.isAssignableFrom(actualArgument.getClass())) {
            return (Class<T>) ((ParameterizedType) actualArgument).getRawType();
        }

        return (Class<T>) actualArgument;
    }

}
src/test/java/br/com/rooting/roxana/utils/ReflectionUtils.java
public static boolean isPackagePrivate(int modifier) {
    return !Modifier.isPrivate(modifier) &&
            !Modifier.isProtected(modifier) &&
            !Modifier.isPublic(modifier);
}
src/test/java/br/com/rooting/roxana/translator/MockedTranslator.java
public class MockedTranslator implements Translator {

    @Override
    public String translate(String key, Locale locale, List<Parameter> parameters) {
        StringBuilder keyBuilder = new StringBuilder(key);
        for (Parameter parameter : parameters) {
            keyBuilder.append(parameter.getFormattedValue(locale));
        }
        return keyBuilder.toString();
    }

    @Override
    public Locale getLocale() {
        return Locale.getDefault();
    }

}
src/main/java/br/com/rooting/roxana/parameter/finder/MessageMapperEnumParameterFinder.java
public class MessageMapperEnumParameterFinder implements ParameterFinderStrategy {

    private static final String EXCEPTION_WHEN_PASSING_PARAMETER_OF_ENUM_MAPPER = "Exception when passing the parameters of the Enum message mapper: ";

    private final List<Object> values;

    private final List<Annotation> parametersMappers;

    public MessageMapperEnumParameterFinder(final MessageMapperEnum enumMapper, final List<Object> values)
            throws FailToFindParameterException, IllegalArgumentException {

        if (enumMapper == null || values == null || !enumMapper.getClass().isEnum()) {
            throw new IllegalArgumentException();
        }

        List<Annotation> parametersMappers = getParameterMapperAnnotationList(enumMapper);
        validParameters(enumMapper, parametersMappers, values);

        this.values = values;
        this.parametersMappers = parametersMappers;
    }

    private static void validParameters(final MessageMapperEnum messageEnumMapper,
                                        final List<Annotation> parametersMappers,
                                        final List<Object> values)
            throws MissingParametersValuesException,
            OverflowingParametersValuesException {

        if (parametersMappers.size() > values.size()) {
            throw new MissingParametersValuesException(messageEnumMapper.getKey(), parametersMappers.size(), values.size());
        } else if (parametersMappers.size() < values.size()) {
            throw new OverflowingParametersValuesException(messageEnumMapper.getKey(), parametersMappers.size(), values.size());
        }
    }

    private static List<Annotation> getParameterMapperAnnotationList(final MessageMapperEnum messageMapperEnum) {
        try {
            Annotation[] annotations = messageMapperEnum.getClass().getField(messageMapperEnum.name()).getAnnotations();
            List<Annotation> parametersMappers = new ArrayList<>();

            Stream.of(annotations).forEach(a -> {
                if (isParameterMapper(a)) {
                    parametersMappers.add(a);
                } else {
                    parametersMappers.addAll(extractParameterMapperList(a));
                }
            });

            return parametersMappers;
        } catch (NoSuchFieldException | SecurityException e) {
            // It is not going to enter here, because this class only accepts enums as mappers
            // and the method name of enum can not be overwritten.
            throw new FailToFindParameterException(EXCEPTION_WHEN_PASSING_PARAMETER_OF_ENUM_MAPPER + messageMapperEnum.name(), e);
        }
    }

    private static boolean isParameterMapper(final Annotation annotation) {
        return annotation instanceof Param ||
                annotation instanceof DateParam ||
                annotation instanceof CurrencyParam;
    }

    private static List<Annotation> extractParameterMapperList(final Annotation annotation) {
        if (annotation instanceof Params) {
            return Arrays.asList(((Params) annotation).value());
        } else if (annotation instanceof DateParams) {
            return Arrays.asList(((DateParams) annotation).value());
        } else if (annotation instanceof CurrencyParams) {
            return Arrays.asList(((CurrencyParams) annotation).value());
        }
        return new ArrayList<>(0);
    }

    @Override
    public List<Parameter> findParameters() {

        List<Parameter> parameters = new ArrayList<>(this.getValues().size());
        Iterator<Annotation> parametersMappersIterator = this.getParametersMappersIterator();
        Iterator<Object> valuesIterator = this.getValuesIterator();

        Integer index = 0;
        while (parametersMappersIterator.hasNext()) {
            parameters.add(this.createParameter(parametersMappersIterator.next(), valuesIterator.next(), index.toString()));
            index++;
        }
        return parameters;
    }

    private Parameter createParameter(final Annotation annotation, final Object value, final String defaultKey) {
        if (annotation instanceof Param) {
            return this.createParameterBaseOn((Param) annotation, value, defaultKey);

        } else if (annotation instanceof DateParam) {
            DateParam dateParameter = (DateParam) annotation;
            return this.createParameterBaseOn(dateParameter, value, defaultKey);

        } else {
            CurrencyParam currencyParameter = (CurrencyParam) annotation;
            return this.createParameterBaseOn(currencyParameter, value, defaultKey);
        }
    }

    private Parameter createParameterBaseOn(final Param param, final Object value, final String defaultKey) {
        String key = Param.DEFAULT_VALUE.equals(param.value()) ? defaultKey : param.value();
        return Parameter.create(key, value);
    }

    private Parameter createParameterBaseOn(final DateParam dateParam, final Object value, final String defaultKey) {
        String key = DateParam.DEFAULT_VALUE.equals(dateParam.value()) ? defaultKey : dateParam.value();
        return Parameter.createDateParameter(key, value, dateParam.style(), dateParam.considerTime(), dateParam.pattern());
    }

    private Parameter createParameterBaseOn(final CurrencyParam currencyParam, final Object value, final String defaultKey) {
        String key = CurrencyParam.DEFAULT_VALUE.equals(currencyParam.value()) ? defaultKey : currencyParam.value();
        return Parameter.createCurrencyParameter(key, value);
    }

    private List<Object> getValues() {
        return this.values;
    }

    private List<Annotation> getParametersMappers() {
        return this.parametersMappers;
    }

    private Iterator<Annotation> getParametersMappersIterator() {
        return this.getParametersMappers().iterator();
    }

    private Iterator<Object> getValuesIterator() {
        return this.getValues().iterator();
    }

}
src/main/java/br/com/rooting/roxana/parameter/Parameter.java
public final class Parameter {

    private final ParameterType type;

    private final String name;

    private final Object value;

    private Parameter(final ParameterType type, final String name, final Object value) {
        if (type == null || name == null || value == null) {
            throw new IllegalArgumentException();
        }
        this.type = type;
        this.name = name;
        this.value = value;
    }

    public static Parameter create(final String name, final Object value) {
        return new Parameter(STRING, name, value);
    }

    public static Parameter createDateParameter(final String name,
                                                final Object o,
                                                final DateStyle style,
                                                final Boolean considerTime,
                                                final String pattern)
            throws UnsupportedParameterConversionException {

        if (o instanceof Date) {
            return Parameter.createDateParameter(name, (Date) o, style, considerTime, pattern);
        } else if (o instanceof Calendar) {
            return Parameter.createDateParameter(name, (Calendar) o, style, considerTime, pattern);
        } else if (o instanceof LocalDate) {
            return Parameter.createDateParameter(name, (LocalDate) o, style, considerTime, pattern);
        } else if (o instanceof LocalDateTime) {
            return Parameter.createDateParameter(name, (LocalDateTime) o, style, considerTime, pattern);
        }
        throw new UnsupportedParameterConversionException(DATE, o);
    }

    public static Parameter createDateParameter(final String name,
                                                final Date date,
                                                final DateStyle style,
                                                final Boolean considerTime,
                                                final String pattern) {

        return new Parameter(DATE, name, DateTimeParameterObject.create(date, style, considerTime, pattern));
    }

    public static Parameter createDateParameter(final String name,
                                                final Calendar calendar,
                                                final DateStyle style,
                                                final Boolean considerTime,
                                                final String pattern) {

        return new Parameter(DATE, name, DateTimeParameterObject.create(calendar, style, considerTime, pattern));
    }

    public static Parameter createDateParameter(final String name,
                                                final LocalDate localDate,
                                                final DateStyle style,
                                                final Boolean considerTime,
                                                final String pattern) {

        return new Parameter(DATE, name, DateTimeParameterObject.create(localDate, style, considerTime, pattern));
    }

    public static Parameter createDateParameter(final String name,
                                                final LocalDateTime localDateTime,
                                                final DateStyle style,
                                                final Boolean considerTime,
                                                final String pattern) {

        return new Parameter(DATE, name, DateTimeParameterObject.create(localDateTime, style, considerTime, pattern));
    }

    public static Parameter createCurrencyParameter(final String name, final Object o) throws UnsupportedParameterConversionException {
        if (o instanceof BigDecimal) {
            return Parameter.createCurrencyParameter(name, (BigDecimal) o);
        } else if (o instanceof Double) {
            return Parameter.createCurrencyParameter(name, (Double) o);
        } else if (o instanceof Float) {
            return Parameter.createCurrencyParameter(name, (Float) o);
        }
        throw new UnsupportedParameterConversionException(CURRENCY, o);
    }

    public static Parameter createCurrencyParameter(final String name, final BigDecimal bigDecimalParam) {
        Double moneyValue = Parameter.treatBigDecimal(bigDecimalParam).doubleValue();
        return new Parameter(CURRENCY, name, moneyValue);
    }

    public static Parameter createCurrencyParameter(final String name, final Double doubleParam) {
        return Parameter.createCurrencyParameter(name, BigDecimal.valueOf(doubleParam));
    }

    public static Parameter createCurrencyParameter(final String name, final Float floatParam) {
        return Parameter.createCurrencyParameter(name, BigDecimal.valueOf(floatParam));
    }

    public static BigDecimal treatBigDecimal(BigDecimal bigDecimal) {
        return bigDecimal.setScale(2, BigDecimal.ROUND_HALF_EVEN);
    }

    public String getFormattedValue(Locale locale) {
        switch (this.getType()) {
            case STRING: {
                return this.getValue().toString();
            }

            case DATE: {
                return ((DateTimeParameterObject) this.getValue()).getFormatterDate(locale);
            }

            case CURRENCY: {
                return NumberFormat.getCurrencyInstance(locale).format(this.getValue());
            }

            default:
                return this.getValue().toString();
        }
    }

    public ParameterType getType() {
        return this.type;
    }

    public String getName() {
        return this.name;
    }

    public Object getValue() {
        return this.value;
    }

    @Override
    public boolean equals(Object object) {
        if (object == null) {
            return false;
        } else if (!(object instanceof Parameter)) {
            return false;
        }

        Parameter parameter = (Parameter) object;
        return this.getType().equals(parameter.getType())
                && this.getName().equals(parameter.getName())
                && this.getValue().equals(parameter.getValue());
    }

    @Override
    public int hashCode() {
        return 7 * (this.getType().hashCode() + this.getName().hashCode());
    }

}
src/main/java/br/com/rooting/roxana/message/MessageSeverity.java
public enum MessageSeverity {

    ERROR, SUCCESS, INFO

}
src/main/java/br/com/rooting/roxana/message/mapper/MessageMapper.java
public interface MessageMapper {

    MessageSeverity getSeverity();

    String getKey();

}
src/main/java/br/com/rooting/roxana/message/mapper/MessageMapperEnum.java
public interface MessageMapperEnum extends MessageMapper {

    String name();

    @Override
    default String getKey() {
        return Translator.getInterpolatedKeyOf(this.getClass().getName() + "." + this.name());
    }

}
src/main/java/br/com/rooting/roxana/parameter/finder/ParameterFinderStrategy.java
public interface ParameterFinderStrategy {

    List<Parameter> findParameters();

}


Question: package br.com.rooting.roxana.message;
import br.com.rooting.roxana.UnitTest;
import br.com.rooting.roxana.message.mapper.MessageMapper;
import br.com.rooting.roxana.message.mapper.MessageMapperEnum;
import br.com.rooting.roxana.message.mapper.MockedMessageMapper;
import br.com.rooting.roxana.parameter.Parameter;
import br.com.rooting.roxana.parameter.finder.MessageMapperEnumParameterFinder;
import br.com.rooting.roxana.parameter.finder.ParameterFinderStrategy;
import br.com.rooting.roxana.parameter.mapper.Param;
import br.com.rooting.roxana.translator.MockedTranslator;
import br.com.rooting.roxana.translator.Translator;
import org.junit.jupiter.api.Test;
import org.springframework.stereotype.Component;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import static br.com.rooting.roxana.message.MessageSeverity.*;
import static br.com.rooting.roxana.utils.ReflectionUtils.isPackagePrivate;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.mock;




class MessageTranslatedCreatorTest extends UnitTest<MessageTranslatedCreator> {

    private static final String STRING_PARAMETER_NAME_01 = "StringParameterName01";
    private static final String STRING_PARAMETER_NAME_02 = "StringParameterName02";

    private static final String RANDOM_STRING_PARAMETER_VALUE_01 = "parameter01";
    private static final String RANDOM_STRING_PARAMETER_VALUE_02 = "parameter02";

    private static final String KEY = "key";

    @Test
    void testClassIsPackagePrivateTest() {
        assertTrue(isPackagePrivate(this.getUnitTestClass().getModifiers()));
    }

    @Test
    void testClassExtendsMessageCreatorTest() {
        assertTrue(MessageCreator.class.isAssignableFrom(this.getUnitTestClass()));
    }

    @Test
    void testClassIsASpringComponentTest() {
        assertTrue(this.getUnitTestClass().isAnnotationPresent(Component.class));
    }

    @Test
    void testClassWasOnlyOnePackagePrivateConstructorTest() {
        Constructor<?>[] constructors = this.getUnitTestClass().getDeclaredConstructors();
        assertEquals(1, constructors.length);
        assertTrue(isPackagePrivate(constructors[0].getModifiers()));
    }

    @Test
    void translatorCanNotBeNullTest() {
        assertThrows(IllegalArgumentException.class, () -> new MessageTranslatedCreator(null));
    }

    @Test
    void createTest() {
        Translator translator = new MockedTranslator();
        MessageTranslatedCreator creator = new MessageTranslatedCreator(translator);

Answer:         MessageMapper mapper = new MockedMessageMapper(KEY, INFO);