diff --git a/pom.xml b/pom.xml
index 32a90a7900043a2e792307932db55350a94921c3..6c336accd615291b54be651161f0e5e6ff87f10d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -244,10 +244,6 @@
       <artifactId>spring-security-test</artifactId>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>org.zalando</groupId>
-      <artifactId>problem-spring-web</artifactId>
-    </dependency>
     <dependency>
       <groupId>io.jsonwebtoken</groupId>
       <artifactId>jjwt-api</artifactId>
@@ -279,6 +275,11 @@
       <groupId>io.dropwizard.metrics</groupId>
       <artifactId>metrics-core</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.reflections</groupId>
+      <artifactId>reflections</artifactId>
+      <scope>test</scope>
+    </dependency>
     <!-- jhipster-needle-maven-add-dependency -->
   </dependencies>
 
@@ -498,7 +499,7 @@
           <artifactId>jacoco-maven-plugin</artifactId>
           <version>${jacoco-maven-plugin.version}</version>
           <configuration>
-          <excludes>
+            <excludes>
               <exclude>com/ippon/pouet/domain/*</exclude>
             </excludes>
           </configuration>
diff --git a/src/main/java/com/ippon/pouet/common/domain/error/Assert.java b/src/main/java/com/ippon/pouet/common/domain/error/Assert.java
new file mode 100644
index 0000000000000000000000000000000000000000..7add83aafd9de4d6ca97c7f1d2e5b75519c3d446
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/common/domain/error/Assert.java
@@ -0,0 +1,20 @@
+package com.ippon.pouet.common.domain.error;
+
+public final class Assert {
+
+  private Assert() {}
+
+  public static void notNull(String field, Object input) {
+    if (input == null) {
+      throw MissingMandatoryValueException.forNullValue(field);
+    }
+  }
+
+  public static void notBlank(String field, String input) {
+    notNull(field, input);
+
+    if (input.isBlank()) {
+      throw MissingMandatoryValueException.forBlankValue(field);
+    }
+  }
+}
diff --git a/src/main/java/com/ippon/pouet/common/domain/error/ErrorStatus.java b/src/main/java/com/ippon/pouet/common/domain/error/ErrorStatus.java
new file mode 100644
index 0000000000000000000000000000000000000000..f67b2c4c6e9d3d80ba7deddc1e99ed5faf06669d
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/common/domain/error/ErrorStatus.java
@@ -0,0 +1,9 @@
+package com.ippon.pouet.common.domain.error;
+
+public enum ErrorStatus {
+  BAD_REQUEST,
+  UNAUTHORIZED,
+  FORBIDDEN,
+  NOT_FOUND,
+  INTERNAL_SERVER_ERROR,
+}
diff --git a/src/main/java/com/ippon/pouet/common/domain/error/MissingMandatoryValueException.java b/src/main/java/com/ippon/pouet/common/domain/error/MissingMandatoryValueException.java
new file mode 100644
index 0000000000000000000000000000000000000000..3273d035bb5a3d14da538ad24933f50bacceb16a
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/common/domain/error/MissingMandatoryValueException.java
@@ -0,0 +1,28 @@
+package com.ippon.pouet.common.domain.error;
+
+public class MissingMandatoryValueException extends PouetException {
+
+  protected MissingMandatoryValueException(PouetExceptionBuilder builder) {
+    super(builder);
+  }
+
+  public static MissingMandatoryValueException forNullValue(String fieldName) {
+    return new MissingMandatoryValueException(
+      builder(StandardMessage.SERVER_MANDATORY_NULL, fieldName, defaultMessage(fieldName) + " (null)")
+    );
+  }
+
+  public static MissingMandatoryValueException forBlankValue(String fieldName) {
+    return new MissingMandatoryValueException(
+      builder(StandardMessage.SERVER_MANDATORY_BLANK, fieldName, defaultMessage(fieldName) + " (blank)")
+    );
+  }
+
+  private static PouetExceptionBuilder builder(PouetMessage pouetMessage, String fieldName, String message) {
+    return PouetException.builder(pouetMessage).status(ErrorStatus.INTERNAL_SERVER_ERROR).argument("field", fieldName).message(message);
+  }
+
+  private static String defaultMessage(String fieldName) {
+    return "The field \"" + fieldName + "\" is mandatory and wasn't set";
+  }
+}
diff --git a/src/main/java/com/ippon/pouet/common/domain/error/PouetException.java b/src/main/java/com/ippon/pouet/common/domain/error/PouetException.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac393d2cd9a2eaa3ffd2b82bf8db20a76f9f865a
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/common/domain/error/PouetException.java
@@ -0,0 +1,175 @@
+package com.ippon.pouet.common.domain.error;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Parent exception used in Pouet application. Those exceptions will be resolved as human readable errors.
+ *
+ * <p>
+ * You can use this implementation directly:
+ * </p>
+ *
+ * <p>
+ * <code>
+ *     <pre>
+ *       PouetException.builder(StandardMessages.USER_MANDATORY)
+ *          .argument("key", "value")
+ *          .argument("other", "test")
+ *          .status(ErrorsHttpStatus.BAD_REQUEST)
+ *          .message("Error message")
+ *          .cause(new RuntimeException())
+ *          .build();
+ *     </pre>
+ *   </code>
+ * </p>
+ *
+ * <p>
+ * Or make extension exceptions:
+ * </p>
+ *
+ * <p>
+ * <code>
+ *     <pre>
+ *       public class MissingMandatoryValueException extends PouetException {
+ *
+ *         public MissingMandatoryValueException(PouetMessage pouetMessage, String fieldName) {
+ *           this(builder(pouetMessage, fieldName, defaultMessage(fieldName)));
+ *         }
+ *
+ *         protected MissingMandatoryValueException(PouetExceptionBuilder builder) {
+ *           super(builder);
+ *         }
+ *
+ *         private static PouetExceptionBuilder builder(PouetMessage pouetMessage, String fieldName, String message) {
+ *           return PouetException.builder(pouetMessage)
+ *               .status(ErrorsStatus.INTERNAL_SERVER_ERROR)
+ *               .argument("field", fieldName)
+ *               .message(message);
+ *         }
+ *
+ *         private static String defaultMessage(String fieldName) {
+ *           return "The field \"" + fieldName + "\" is mandatory and wasn't set";
+ *         }
+ *       }
+ *     </pre>
+ *   </code>
+ * </p>
+ */
+public class PouetException extends RuntimeException {
+  private final Map<String, String> arguments;
+  private final ErrorStatus status;
+  private final PouetMessage pouetMessage;
+
+  protected PouetException(PouetExceptionBuilder builder) {
+    super(getMessage(builder), getCause(builder));
+    arguments = getArguments(builder);
+    status = getStatus(builder);
+    pouetMessage = getPouetMessage(builder);
+  }
+
+  private static String getMessage(PouetExceptionBuilder builder) {
+    if (builder == null) {
+      return null;
+    }
+
+    return builder.message;
+  }
+
+  private static Throwable getCause(PouetExceptionBuilder builder) {
+    if (builder == null) {
+      return null;
+    }
+
+    return builder.cause;
+  }
+
+  private static Map<String, String> getArguments(PouetExceptionBuilder builder) {
+    if (builder == null) {
+      return null;
+    }
+
+    return Collections.unmodifiableMap(builder.arguments);
+  }
+
+  private static ErrorStatus getStatus(PouetExceptionBuilder builder) {
+    if (builder == null) {
+      return null;
+    }
+
+    return builder.status;
+  }
+
+  private static PouetMessage getPouetMessage(PouetExceptionBuilder builder) {
+    if (builder == null) {
+      return null;
+    }
+
+    return builder.pouetMessage;
+  }
+
+  public static PouetExceptionBuilder builder(PouetMessage message) {
+    return new PouetExceptionBuilder(message);
+  }
+
+  public Map<String, String> getArguments() {
+    return arguments;
+  }
+
+  public ErrorStatus getStatus() {
+    return status;
+  }
+
+  public PouetMessage getPouetMessage() {
+    return pouetMessage;
+  }
+
+  public static class PouetExceptionBuilder {
+    private final Map<String, String> arguments = new HashMap<>();
+    private String message;
+    private ErrorStatus status;
+    private PouetMessage pouetMessage;
+    private Throwable cause;
+
+    public PouetExceptionBuilder(PouetMessage pouetMessage) {
+      this.pouetMessage = pouetMessage;
+    }
+
+    public PouetExceptionBuilder argument(String key, Object value) {
+      arguments.put(key, getStringValue(value));
+
+      return this;
+    }
+
+    private String getStringValue(Object value) {
+      if (value == null) {
+        return "null";
+      }
+
+      return value.toString();
+    }
+
+    public PouetExceptionBuilder status(ErrorStatus status) {
+      this.status = status;
+
+      return this;
+    }
+
+    public PouetExceptionBuilder message(String message) {
+      this.message = message;
+
+      return this;
+    }
+
+    public PouetExceptionBuilder cause(Throwable cause) {
+      this.cause = cause;
+
+      return this;
+    }
+
+    public PouetException build() {
+      return new PouetException(this);
+    }
+  }
+}
diff --git a/src/main/java/com/ippon/pouet/common/domain/error/PouetMessage.java b/src/main/java/com/ippon/pouet/common/domain/error/PouetMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..921ce61de641d0f37ee41848ff1a1424bd5e0a21
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/common/domain/error/PouetMessage.java
@@ -0,0 +1,8 @@
+package com.ippon.pouet.common.domain.error;
+
+import java.io.Serializable;
+
+@FunctionalInterface
+public interface PouetMessage extends Serializable {
+  String getMessageKey();
+}
diff --git a/src/main/java/com/ippon/pouet/common/domain/error/StandardMessage.java b/src/main/java/com/ippon/pouet/common/domain/error/StandardMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6e31eaaaf16ecf301afa2d34f2bacfceae7e4f9
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/common/domain/error/StandardMessage.java
@@ -0,0 +1,23 @@
+package com.ippon.pouet.common.domain.error;
+
+/**
+ * User messages for standard (common) use cases
+ */
+public enum StandardMessage implements PouetMessage {
+  USER_MANDATORY("user.mandatory"),
+  SERVER_MANDATORY_NULL("server.mandatory-null"),
+  SERVER_MANDATORY_BLANK("server.mandatory-blank"),
+  BAD_REQUEST("user.bad-request"),
+  INTERNAL_SERVER_ERROR("server.internal-server-error");
+
+  private final String messageKey;
+
+  private StandardMessage(String code) {
+    this.messageKey = code;
+  }
+
+  @Override
+  public String getMessageKey() {
+    return messageKey;
+  }
+}
diff --git a/src/main/java/com/ippon/pouet/common/infrastructure/primary/ArgumentsReplacer.java b/src/main/java/com/ippon/pouet/common/infrastructure/primary/ArgumentsReplacer.java
new file mode 100644
index 0000000000000000000000000000000000000000..4c03914f722b439ea8fce865f926c757ae09718a
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/common/infrastructure/primary/ArgumentsReplacer.java
@@ -0,0 +1,24 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import java.util.Map;
+
+final class ArgumentsReplacer {
+  private static final String OPEN_MUSTACHE = "\\{\\{\\s*";
+  private static final String CLOSE_MUSTACHE = "\\s*\\}\\}";
+
+  private ArgumentsReplacer() {}
+
+  public static String replaceArguments(String message, Map<String, String> arguments) {
+    if (message == null || arguments == null) {
+      return message;
+    }
+
+    String result = message;
+    for (Map.Entry<String, String> argument : arguments.entrySet()) {
+      String argumentRegex = OPEN_MUSTACHE + argument.getKey() + CLOSE_MUSTACHE;
+      result = result.replaceAll(argumentRegex, argument.getValue());
+    }
+
+    return result;
+  }
+}
diff --git a/src/main/java/com/ippon/pouet/common/infrastructure/primary/AuthenticationMessage.java b/src/main/java/com/ippon/pouet/common/infrastructure/primary/AuthenticationMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..afeef040b7e117db78e3199f02bfdb95f359ee96
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/common/infrastructure/primary/AuthenticationMessage.java
@@ -0,0 +1,18 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import com.ippon.pouet.common.domain.error.PouetMessage;
+
+enum AuthenticationMessage implements PouetMessage {
+  NOT_AUTHENTICATED("user.authentication-not-authenticated");
+
+  private final String messageKey;
+
+  AuthenticationMessage(String messageKey) {
+    this.messageKey = messageKey;
+  }
+
+  @Override
+  public String getMessageKey() {
+    return messageKey;
+  }
+}
diff --git a/src/main/java/com/ippon/pouet/common/infrastructure/primary/PouetError.java b/src/main/java/com/ippon/pouet/common/infrastructure/primary/PouetError.java
new file mode 100644
index 0000000000000000000000000000000000000000..69775ecd174d6ad4d3e4ee3125ce1b3bea5e4e6b
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/common/infrastructure/primary/PouetError.java
@@ -0,0 +1,49 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.Collection;
+import java.util.List;
+
+@ApiModel(description = "Error result for a WebService call")
+class PouetError {
+  @ApiModelProperty(value = "Technical type of this error", example = "user.mandatory", required = true)
+  private final String errorType;
+
+  @ApiModelProperty(
+    value = "Human readable error message",
+    example = "Une erreur technique est survenue lors du traitement de votre demande",
+    required = true
+  )
+  private final String message;
+
+  @ApiModelProperty(value = "Invalid fields", required = false)
+  private final List<PouetFieldError> fieldsErrors;
+
+  public PouetError(String errorType, String message) {
+    this(errorType, message, null);
+  }
+
+  public PouetError(
+    @JsonProperty("errorType") String errorType,
+    @JsonProperty("message") String message,
+    @JsonProperty("fieldsErrors") List<PouetFieldError> fieldsErrors
+  ) {
+    this.errorType = errorType;
+    this.message = message;
+    this.fieldsErrors = fieldsErrors;
+  }
+
+  public String getErrorType() {
+    return errorType;
+  }
+
+  public String getMessage() {
+    return message;
+  }
+
+  public Collection<PouetFieldError> getFieldsErrors() {
+    return fieldsErrors;
+  }
+}
diff --git a/src/main/java/com/ippon/pouet/common/infrastructure/primary/PouetErrorHandler.java b/src/main/java/com/ippon/pouet/common/infrastructure/primary/PouetErrorHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..d152d50415ae951d92ad70b8312cc840778f056c
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/common/infrastructure/primary/PouetErrorHandler.java
@@ -0,0 +1,280 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import com.ippon.pouet.common.domain.error.ErrorStatus;
+import com.ippon.pouet.common.domain.error.PouetException;
+import com.ippon.pouet.common.domain.error.PouetMessage;
+import com.ippon.pouet.common.domain.error.StandardMessage;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.MessageSource;
+import org.springframework.context.NoSuchMessageException;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.client.RestClientResponseException;
+import org.springframework.web.context.request.WebRequest;
+import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
+import org.springframework.web.server.ResponseStatusException;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+@ControllerAdvice
+public class PouetErrorHandler extends ResponseEntityExceptionHandler {
+  private static final String MESSAGE_PREFIX = "pouet.error.";
+  private static final String DEFAULT_KEY = StandardMessage.INTERNAL_SERVER_ERROR.getMessageKey();
+  private static final String BAD_REQUEST_KEY = StandardMessage.BAD_REQUEST.getMessageKey();
+  private static final String STATUS_EXCEPTION_KEY = "status-exception";
+
+  private static final Logger logger = LoggerFactory.getLogger(PouetErrorHandler.class);
+
+  private final MessageSource messages;
+
+  public PouetErrorHandler(MessageSource messages) {
+    Locale.setDefault(Locale.FRANCE);
+    this.messages = messages;
+  }
+
+  @ExceptionHandler
+  public ResponseEntity<PouetError> handlePouetException(PouetException exception) {
+    HttpStatus status = getStatus(exception);
+
+    logError(exception, status);
+
+    String messageKey = getMessageKey(status, exception);
+    PouetError error = new PouetError(messageKey, getMessage(messageKey, exception.getArguments()));
+    return new ResponseEntity<>(error, status);
+  }
+
+  @ExceptionHandler
+  public ResponseEntity<PouetError> handleResponseStatusException(ResponseStatusException exception) {
+    HttpStatus status = exception.getStatus();
+
+    logError(exception, status);
+
+    PouetError error = new PouetError(STATUS_EXCEPTION_KEY, buildErrorStatusMessage(exception));
+    return new ResponseEntity<>(error, status);
+  }
+
+  private String buildErrorStatusMessage(ResponseStatusException exception) {
+    String reason = exception.getReason();
+
+    if (StringUtils.isBlank(reason)) {
+      Map<String, String> statusArgument = Map.of("status", String.valueOf(exception.getStatus().value()));
+
+      return getMessage(STATUS_EXCEPTION_KEY, statusArgument);
+    }
+
+    return reason;
+  }
+
+  @ExceptionHandler(MaxUploadSizeExceededException.class)
+  public ResponseEntity<PouetError> handleFileSizeException(MaxUploadSizeExceededException maxUploadSizeExceededException) {
+    logger.warn("File size limit exceeded: {}", maxUploadSizeExceededException.getMessage(), maxUploadSizeExceededException);
+
+    PouetError error = new PouetError("server.upload-too-big", getMessage("server.upload-too-big", null));
+    return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
+  }
+
+  @ExceptionHandler(AccessDeniedException.class)
+  public ResponseEntity<PouetError> handleAccessDeniedException(AccessDeniedException accessDeniedException) {
+    PouetError error = new PouetError("user.access-denied", getMessage("user.access-denied", null));
+    return new ResponseEntity<>(error, HttpStatus.FORBIDDEN);
+  }
+
+  @ExceptionHandler(MethodArgumentTypeMismatchException.class)
+  public ResponseEntity<PouetError> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException exception) {
+    Throwable rootCause = ExceptionUtils.getRootCause(exception);
+    if (rootCause instanceof PouetException) {
+      return handlePouetException((PouetException) rootCause);
+    }
+
+    PouetError error = new PouetError(BAD_REQUEST_KEY, getMessage(BAD_REQUEST_KEY, null));
+    return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
+  }
+
+  private HttpStatus getStatus(PouetException exception) {
+    ErrorStatus status = exception.getStatus();
+    if (status == null) {
+      return HttpStatus.INTERNAL_SERVER_ERROR;
+    }
+
+    switch (status) {
+      case BAD_REQUEST:
+        return HttpStatus.BAD_REQUEST;
+      case UNAUTHORIZED:
+        return HttpStatus.UNAUTHORIZED;
+      case FORBIDDEN:
+        return HttpStatus.FORBIDDEN;
+      case NOT_FOUND:
+        return HttpStatus.NOT_FOUND;
+      default:
+        return HttpStatus.INTERNAL_SERVER_ERROR;
+    }
+  }
+
+  private void logError(Exception exception, HttpStatus status) {
+    if (status.is5xxServerError()) {
+      logger.error("A server error was sent to a user: {}", exception.getMessage(), exception);
+
+      logErrorBody(exception);
+    } else {
+      logger.warn("An error was sent to a user: {}", exception.getMessage(), exception);
+    }
+  }
+
+  private void logErrorBody(Exception exception) {
+    Throwable cause = exception.getCause();
+    if (cause instanceof RestClientResponseException) {
+      RestClientResponseException restCause = (RestClientResponseException) cause;
+
+      logger.error("Cause body: {}", restCause.getResponseBodyAsString());
+    }
+  }
+
+  private String getMessageKey(HttpStatus status, PouetException exception) {
+    PouetMessage message = exception.getPouetMessage();
+    if (message == null) {
+      return getDefaultMessage(status);
+    }
+
+    return message.getMessageKey();
+  }
+
+  private String getDefaultMessage(HttpStatus status) {
+    if (status.is5xxServerError()) {
+      return DEFAULT_KEY;
+    }
+
+    return BAD_REQUEST_KEY;
+  }
+
+  @Override
+  protected ResponseEntity<Object> handleMethodArgumentNotValid(
+    MethodArgumentNotValidException exception,
+    HttpHeaders headers,
+    HttpStatus status,
+    WebRequest request
+  ) {
+    logger.debug("Bean validation error {}", exception.getMessage(), exception);
+
+    PouetError error = new PouetError(BAD_REQUEST_KEY, getMessage(BAD_REQUEST_KEY, null), getFieldsError(exception));
+    return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
+  }
+
+  @ExceptionHandler
+  public ResponseEntity<PouetError> handleBeanValidationError(ConstraintViolationException exception) {
+    logger.debug("Bean validation error {}", exception.getMessage(), exception);
+
+    PouetError error = new PouetError(BAD_REQUEST_KEY, getMessage(BAD_REQUEST_KEY, null), getFieldsErrors(exception));
+    return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
+  }
+
+  private ConstraintViolation<?> extractSource(FieldError error) {
+    return error.unwrap(ConstraintViolation.class);
+  }
+
+  private String getMessage(String messageKey, Map<String, String> arguments) {
+    String text = getMessageFromSource(messageKey);
+
+    return ArgumentsReplacer.replaceArguments(text, arguments);
+  }
+
+  private String getMessageFromSource(String messageKey) {
+    Locale locale = LocaleContextHolder.getLocale();
+
+    try {
+      return messages.getMessage(MESSAGE_PREFIX + messageKey, null, locale);
+    } catch (NoSuchMessageException e) {
+      return messages.getMessage(MESSAGE_PREFIX + DEFAULT_KEY, null, locale);
+    }
+  }
+
+  private List<PouetFieldError> getFieldsErrors(ConstraintViolationException exception) {
+    return exception.getConstraintViolations().stream().map(toFieldError()).collect(Collectors.toList());
+  }
+
+  private List<PouetFieldError> getFieldsError(MethodArgumentNotValidException exception) {
+    return exception.getBindingResult().getFieldErrors().stream().map(toPouetFieldError()).collect(Collectors.toList());
+  }
+
+  private Function<FieldError, PouetFieldError> toPouetFieldError() {
+    return error ->
+      PouetFieldError
+        .builder()
+        .fieldPath(error.getField())
+        .reason(error.getDefaultMessage())
+        .message(getMessage(error.getDefaultMessage(), buildArguments(extractSource(error))))
+        .build();
+  }
+
+  private Map<String, String> buildArguments(ConstraintViolation<?> violation) {
+    return violation
+      .getConstraintDescriptor()
+      .getAttributes()
+      .entrySet()
+      .stream()
+      .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().toString()));
+  }
+
+  private Function<ConstraintViolation<?>, PouetFieldError> toFieldError() {
+    return violation -> {
+      Map<String, String> arguments = buildArguments(violation);
+
+      String message = violation.getMessage();
+      return PouetFieldError
+        .builder()
+        .fieldPath(violation.getPropertyPath().toString())
+        .reason(message)
+        .message(getMessage(message, arguments))
+        .build();
+    };
+  }
+
+  @Override
+  protected ResponseEntity<Object> handleHttpMessageNotReadable(
+    HttpMessageNotReadableException exception,
+    HttpHeaders headers,
+    HttpStatus status,
+    WebRequest request
+  ) {
+    logger.error("Error reading query information: {}", exception.getMessage(), exception);
+
+    PouetError error = new PouetError(DEFAULT_KEY, getMessage(DEFAULT_KEY, null));
+    return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
+  }
+
+  @ExceptionHandler
+  public ResponseEntity<PouetError> handleAuthenticationException(AuthenticationException exception) {
+    logger.debug("A user tried to do an unauthorized operation: {}", exception.getMessage(), exception);
+
+    String message = AuthenticationMessage.NOT_AUTHENTICATED.getMessageKey();
+    PouetError error = new PouetError(message, getMessage(message, null));
+
+    return new ResponseEntity<>(error, HttpStatus.UNAUTHORIZED);
+  }
+
+  @ExceptionHandler
+  public ResponseEntity<PouetError> handleRuntimeException(Throwable throwable) {
+    logger.error("An unhandled error occurs: {}", throwable.getMessage(), throwable);
+
+    PouetError error = new PouetError(DEFAULT_KEY, getMessage(DEFAULT_KEY, null));
+    return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
+  }
+}
diff --git a/src/main/java/com/ippon/pouet/common/infrastructure/primary/PouetFieldError.java b/src/main/java/com/ippon/pouet/common/infrastructure/primary/PouetFieldError.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9dad547bd7af52326ac01718ee2a4cfef9a31ef
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/common/infrastructure/primary/PouetFieldError.java
@@ -0,0 +1,71 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
+import com.ippon.pouet.common.infrastructure.primary.PouetFieldError.PouetFieldErrorBuilder;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+@JsonDeserialize(builder = PouetFieldErrorBuilder.class)
+@ApiModel(description = "Error for a field validation")
+class PouetFieldError {
+  @ApiModelProperty(value = "Path to the field in error", example = "address.country", required = true)
+  private final String fieldPath;
+
+  @ApiModelProperty(value = "Technical reason for the invalidation", example = "user.mandatory", required = true)
+  private final String reason;
+
+  @ApiModelProperty(value = "Human readable message for the invalidation", example = "Le champ doit être renseigné", required = true)
+  private final String message;
+
+  private PouetFieldError(PouetFieldErrorBuilder builder) {
+    fieldPath = builder.fieldPath;
+    reason = builder.reason;
+    message = builder.message;
+  }
+
+  public static PouetFieldErrorBuilder builder() {
+    return new PouetFieldErrorBuilder();
+  }
+
+  public String getFieldPath() {
+    return fieldPath;
+  }
+
+  public String getReason() {
+    return reason;
+  }
+
+  public String getMessage() {
+    return message;
+  }
+
+  @JsonPOJOBuilder(withPrefix = "")
+  public static class PouetFieldErrorBuilder {
+    private String fieldPath;
+    private String reason;
+    private String message;
+
+    public PouetFieldErrorBuilder fieldPath(String fieldPath) {
+      this.fieldPath = fieldPath;
+
+      return this;
+    }
+
+    public PouetFieldErrorBuilder reason(String reason) {
+      this.reason = reason;
+
+      return this;
+    }
+
+    public PouetFieldErrorBuilder message(String message) {
+      this.message = message;
+
+      return this;
+    }
+
+    public PouetFieldError build() {
+      return new PouetFieldError(this);
+    }
+  }
+}
diff --git a/src/main/java/com/ippon/pouet/common/infrastructure/primary/ValidationMessage.java b/src/main/java/com/ippon/pouet/common/infrastructure/primary/ValidationMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..b7e023056f90ca4bead154f512dbcc587e8421be
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/common/infrastructure/primary/ValidationMessage.java
@@ -0,0 +1,8 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+public final class ValidationMessage {
+  public static final String MANDATORY = "user.mandatory";
+  public static final String WRONG_FORMAT = "user.wrong-format";
+
+  private ValidationMessage() {}
+}
diff --git a/src/main/java/com/ippon/pouet/config/AuthenticationErrorsHandler.java b/src/main/java/com/ippon/pouet/config/AuthenticationErrorsHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..3c444e5645834ee5d406d66d930ff95d990eec92
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/config/AuthenticationErrorsHandler.java
@@ -0,0 +1,38 @@
+package com.ippon.pouet.config;
+
+import com.ippon.pouet.common.domain.Generated;
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.security.web.access.AccessDeniedHandler;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerExceptionResolver;
+
+@Generated
+@Component
+class AuthenticationErrorsHandler implements AuthenticationEntryPoint, AccessDeniedHandler {
+  private final HandlerExceptionResolver resolver;
+
+  @Autowired
+  public AuthenticationErrorsHandler(@Qualifier("handlerExceptionResolver") HandlerExceptionResolver resolver) {
+    this.resolver = resolver;
+  }
+
+  @Override
+  public void commence(final HttpServletRequest request, final HttpServletResponse response, final AuthenticationException exception)
+    throws IOException, ServletException {
+    resolver.resolveException(request, response, null, exception);
+  }
+
+  @Override
+  public void handle(final HttpServletRequest request, final HttpServletResponse response, final AccessDeniedException exception)
+    throws IOException, ServletException {
+    resolver.resolveException(request, response, null, exception);
+  }
+}
diff --git a/src/main/java/com/ippon/pouet/config/JacksonConfiguration.java b/src/main/java/com/ippon/pouet/config/JacksonConfiguration.java
index 59f745dcea476c77585f67cfdc56605a5f19214f..05c5f5ebdea1324ed759c82e2511a3c535bb9058 100644
--- a/src/main/java/com/ippon/pouet/config/JacksonConfiguration.java
+++ b/src/main/java/com/ippon/pouet/config/JacksonConfiguration.java
@@ -6,8 +6,6 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
 import com.ippon.pouet.common.domain.Generated;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.zalando.problem.ProblemModule;
-import org.zalando.problem.violations.ConstraintViolationProblemModule;
 
 @Generated
 @Configuration
@@ -34,20 +32,4 @@ public class JacksonConfiguration {
   public Hibernate5Module hibernate5Module() {
     return new Hibernate5Module();
   }
-
-  /*
-   * Module for serialization/deserialization of RFC7807 Problem.
-   */
-  @Bean
-  public ProblemModule problemModule() {
-    return new ProblemModule();
-  }
-
-  /*
-   * Module for serialization/deserialization of ConstraintViolationProblem.
-   */
-  @Bean
-  public ConstraintViolationProblemModule constraintViolationProblemModule() {
-    return new ConstraintViolationProblemModule();
-  }
 }
diff --git a/src/main/java/com/ippon/pouet/config/SecurityConfiguration.java b/src/main/java/com/ippon/pouet/config/SecurityConfiguration.java
index a3370565801e25692528183f04afff3e10611562..272df669a4cd5134565e4c0bae06aeed867cde66 100644
--- a/src/main/java/com/ippon/pouet/config/SecurityConfiguration.java
+++ b/src/main/java/com/ippon/pouet/config/SecurityConfiguration.java
@@ -1,10 +1,10 @@
 package com.ippon.pouet.config;
 
 import com.ippon.pouet.common.domain.Generated;
-import com.ippon.pouet.security.*;
-import com.ippon.pouet.security.jwt.*;
+import com.ippon.pouet.security.AuthoritiesConstants;
+import com.ippon.pouet.security.jwt.JWTConfigurer;
+import com.ippon.pouet.security.jwt.TokenProvider;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Import;
 import org.springframework.http.HttpMethod;
 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -17,22 +17,21 @@ import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter;
 import org.springframework.web.filter.CorsFilter;
-import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;
 
 @Generated
 @EnableWebSecurity
 @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
-@Import(SecurityProblemSupport.class)
 public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
   private final TokenProvider tokenProvider;
 
   private final CorsFilter corsFilter;
-  private final SecurityProblemSupport problemSupport;
 
-  public SecurityConfiguration(TokenProvider tokenProvider, CorsFilter corsFilter, SecurityProblemSupport problemSupport) {
+  private final AuthenticationErrorsHandler errorsHandler;
+
+  public SecurityConfiguration(TokenProvider tokenProvider, CorsFilter corsFilter, AuthenticationErrorsHandler errorsHandler) {
     this.tokenProvider = tokenProvider;
     this.corsFilter = corsFilter;
-    this.problemSupport = problemSupport;
+    this.errorsHandler = errorsHandler;
   }
 
   @Bean
@@ -61,8 +60,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
             .disable()
             .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
             .exceptionHandling()
-                .authenticationEntryPoint(problemSupport)
-                .accessDeniedHandler(problemSupport)
+                .authenticationEntryPoint(errorsHandler)
+                .accessDeniedHandler(errorsHandler)
         .and()
             .headers()
             .contentSecurityPolicy("default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:")
diff --git a/src/main/java/com/ippon/pouet/service/EmailAlreadyUsedException.java b/src/main/java/com/ippon/pouet/service/EmailAlreadyUsedException.java
index f4559ff53ce8e04e0d80d26396618c8ce205863e..7f4f1609f1b504fed8512dbdb2932f02e0e9d21d 100644
--- a/src/main/java/com/ippon/pouet/service/EmailAlreadyUsedException.java
+++ b/src/main/java/com/ippon/pouet/service/EmailAlreadyUsedException.java
@@ -1,12 +1,19 @@
 package com.ippon.pouet.service;
 
 import com.ippon.pouet.common.domain.Generated;
+import com.ippon.pouet.common.domain.error.ErrorStatus;
+import com.ippon.pouet.common.domain.error.PouetException;
 
 @Generated
-public class EmailAlreadyUsedException extends RuntimeException {
+public class EmailAlreadyUsedException extends PouetException {
   private static final long serialVersionUID = 1L;
 
   public EmailAlreadyUsedException() {
-    super("Email is already in use!");
+    super(
+      PouetException
+        .builder(UserMessage.EMAIL_ALREADY_USED)
+        .message("A user tried to create an account with an used email")
+        .status(ErrorStatus.BAD_REQUEST)
+    );
   }
 }
diff --git a/src/main/java/com/ippon/pouet/service/InvalidPasswordException.java b/src/main/java/com/ippon/pouet/service/InvalidPasswordException.java
index 4871e691a5cc3077b24ee97124ea4c85369e51fd..cd234b95759e82b3026679bb9c4e9b9b67adc94f 100644
--- a/src/main/java/com/ippon/pouet/service/InvalidPasswordException.java
+++ b/src/main/java/com/ippon/pouet/service/InvalidPasswordException.java
@@ -1,12 +1,13 @@
 package com.ippon.pouet.service;
 
-import com.ippon.pouet.common.domain.Generated;
+import com.ippon.pouet.common.domain.error.ErrorStatus;
+import com.ippon.pouet.common.domain.error.PouetException;
 
-@Generated
-public class InvalidPasswordException extends RuntimeException {
-  private static final long serialVersionUID = 1L;
+public class InvalidPasswordException extends PouetException {
 
   public InvalidPasswordException() {
-    super("Incorrect password");
+    super(
+      PouetException.builder(UserMessage.INVALID_PASSWORD).status(ErrorStatus.BAD_REQUEST).message("A user entered an invalid password")
+    );
   }
 }
diff --git a/src/main/java/com/ippon/pouet/service/LoginAlreadyUsedException.java b/src/main/java/com/ippon/pouet/service/LoginAlreadyUsedException.java
new file mode 100644
index 0000000000000000000000000000000000000000..52fe3e39eadf5804c87b2ec979619491e6379b8e
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/service/LoginAlreadyUsedException.java
@@ -0,0 +1,19 @@
+package com.ippon.pouet.service;
+
+import com.ippon.pouet.common.domain.Generated;
+import com.ippon.pouet.common.domain.error.ErrorStatus;
+import com.ippon.pouet.common.domain.error.PouetException;
+
+@Generated
+public class LoginAlreadyUsedException extends PouetException {
+  private static final long serialVersionUID = 1L;
+
+  public LoginAlreadyUsedException() {
+    super(
+      PouetException
+        .builder(UserMessage.LOGIN_ALREADY_USED)
+        .status(ErrorStatus.BAD_REQUEST)
+        .message("A user tried to create an account with an used login")
+    );
+  }
+}
diff --git a/src/main/java/com/ippon/pouet/service/UserMessage.java b/src/main/java/com/ippon/pouet/service/UserMessage.java
new file mode 100644
index 0000000000000000000000000000000000000000..308dd039fb12cdd4108242997b86386ec1add05f
--- /dev/null
+++ b/src/main/java/com/ippon/pouet/service/UserMessage.java
@@ -0,0 +1,20 @@
+package com.ippon.pouet.service;
+
+import com.ippon.pouet.common.domain.error.PouetMessage;
+
+enum UserMessage implements PouetMessage {
+  EMAIL_ALREADY_USED("user.e-mail-already-used"),
+  LOGIN_ALREADY_USED("user.login-already-used"),
+  INVALID_PASSWORD("user.invalid-password");
+
+  private final String messageKey;
+
+  private UserMessage(String messageKey) {
+    this.messageKey = messageKey;
+  }
+
+  @Override
+  public String getMessageKey() {
+    return messageKey;
+  }
+}
diff --git a/src/main/java/com/ippon/pouet/service/UserService.java b/src/main/java/com/ippon/pouet/service/UserService.java
index 5fcb7596cdc530462789c66fb8b2f9f3f0d28905..9d4e99f463962bc9852b3f78dd4ddb868ed899e0 100644
--- a/src/main/java/com/ippon/pouet/service/UserService.java
+++ b/src/main/java/com/ippon/pouet/service/UserService.java
@@ -12,7 +12,10 @@ import com.ippon.pouet.service.dto.UserDTO;
 import io.github.jhipster.security.RandomUtil;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
-import java.util.*;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
 import java.util.stream.Collectors;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -94,7 +97,7 @@ public class UserService {
         existingUser -> {
           boolean removed = removeNonActivatedUser(existingUser);
           if (!removed) {
-            throw new UsernameAlreadyUsedException();
+            throw new LoginAlreadyUsedException();
           }
         }
       );
diff --git a/src/main/java/com/ippon/pouet/service/UsernameAlreadyUsedException.java b/src/main/java/com/ippon/pouet/service/UsernameAlreadyUsedException.java
deleted file mode 100644
index ea8f6db74582fb32d068804f8008f095e8d02058..0000000000000000000000000000000000000000
--- a/src/main/java/com/ippon/pouet/service/UsernameAlreadyUsedException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.ippon.pouet.service;
-
-import com.ippon.pouet.common.domain.Generated;
-
-@Generated
-public class UsernameAlreadyUsedException extends RuntimeException {
-  private static final long serialVersionUID = 1L;
-
-  public UsernameAlreadyUsedException() {
-    super("Login name already used!");
-  }
-}
diff --git a/src/main/java/com/ippon/pouet/web/rest/AccountResource.java b/src/main/java/com/ippon/pouet/web/rest/AccountResource.java
index ef161deb15c2813bcdeb9987479f3dc033e38359..3593c919f0e91c5670acc25acd5e4afe81c7a2bc 100644
--- a/src/main/java/com/ippon/pouet/web/rest/AccountResource.java
+++ b/src/main/java/com/ippon/pouet/web/rest/AccountResource.java
@@ -4,21 +4,29 @@ import com.ippon.pouet.common.domain.Generated;
 import com.ippon.pouet.domain.User;
 import com.ippon.pouet.repository.UserRepository;
 import com.ippon.pouet.security.SecurityUtils;
+import com.ippon.pouet.service.EmailAlreadyUsedException;
+import com.ippon.pouet.service.InvalidPasswordException;
+import com.ippon.pouet.service.LoginAlreadyUsedException;
 import com.ippon.pouet.service.MailService;
 import com.ippon.pouet.service.UserService;
 import com.ippon.pouet.service.dto.PasswordChangeDTO;
 import com.ippon.pouet.service.dto.UserDTO;
-import com.ippon.pouet.web.rest.errors.*;
 import com.ippon.pouet.web.rest.vm.KeyAndPasswordVM;
 import com.ippon.pouet.web.rest.vm.ManagedUserVM;
-import java.util.*;
+import java.util.Optional;
 import javax.servlet.http.HttpServletRequest;
 import javax.validation.Valid;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpStatus;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
 
 /**
  * REST controller for managing the current user's account.
diff --git a/src/main/java/com/ippon/pouet/web/rest/UserResource.java b/src/main/java/com/ippon/pouet/web/rest/UserResource.java
index 051a4a54c7519df3ec00e5d1def8ac05c2b3b0a5..36517449e2d6fd1c6b29b80de9d63a4bf14f6245 100644
--- a/src/main/java/com/ippon/pouet/web/rest/UserResource.java
+++ b/src/main/java/com/ippon/pouet/web/rest/UserResource.java
@@ -1,22 +1,25 @@
 package com.ippon.pouet.web.rest;
 
 import com.ippon.pouet.common.domain.Generated;
+import com.ippon.pouet.common.domain.error.ErrorStatus;
+import com.ippon.pouet.common.domain.error.PouetException;
+import com.ippon.pouet.common.domain.error.StandardMessage;
 import com.ippon.pouet.config.Constants;
 import com.ippon.pouet.domain.User;
 import com.ippon.pouet.repository.UserRepository;
 import com.ippon.pouet.security.AuthoritiesConstants;
+import com.ippon.pouet.service.EmailAlreadyUsedException;
+import com.ippon.pouet.service.LoginAlreadyUsedException;
 import com.ippon.pouet.service.MailService;
 import com.ippon.pouet.service.UserService;
 import com.ippon.pouet.service.dto.UserDTO;
-import com.ippon.pouet.web.rest.errors.BadRequestAlertException;
-import com.ippon.pouet.web.rest.errors.EmailAlreadyUsedException;
-import com.ippon.pouet.web.rest.errors.LoginAlreadyUsedException;
 import io.github.jhipster.web.util.HeaderUtil;
 import io.github.jhipster.web.util.PaginationUtil;
 import io.github.jhipster.web.util.ResponseUtil;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.*;
+import java.util.List;
+import java.util.Optional;
 import javax.validation.Valid;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -27,7 +30,14 @@ import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
 
 /**
@@ -35,21 +45,20 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
  * <p>
  * This class accesses the {@link User} entity, and needs to fetch its collection of authorities.
  * <p>
- * For a normal use-case, it would be better to have an eager relationship between User and Authority,
- * and send everything to the client side: there would be no View Model and DTO, a lot less code, and an outer-join
- * which would be good for performance.
+ * For a normal use-case, it would be better to have an eager relationship between User and Authority, and send
+ * everything to the client side: there would be no View Model and DTO, a lot less code, and an outer-join which would
+ * be good for performance.
  * <p>
  * We use a View Model and a DTO for 3 reasons:
  * <ul>
- * <li>We want to keep a lazy association between the user and the authorities, because people will
- * quite often do relationships with the user, and we don't want them to get the authorities all
- * the time for nothing (for performance reasons). This is the #1 goal: we should not impact our users'
- * application because of this use-case.</li>
- * <li> Not having an outer join causes n+1 requests to the database. This is not a real issue as
- * we have by default a second-level cache. This means on the first HTTP call we do the n+1 requests,
- * but then all authorities come from the cache, so in fact it's much better than doing an outer join
- * (which will get lots of data from the database, for each HTTP call).</li>
- * <li> As this manages users, for security reasons, we'd rather have a DTO layer.</li>
+ * <li>We want to keep a lazy association between the user and the authorities, because people will quite often do
+ * relationships with the user, and we don't want them to get the authorities all the time for nothing (for performance
+ * reasons). This is the #1 goal: we should not impact our users' application because of this use-case.</li>
+ * <li>Not having an outer join causes n+1 requests to the database. This is not a real issue as we have by default a
+ * second-level cache. This means on the first HTTP call we do the n+1 requests, but then all authorities come from the
+ * cache, so in fact it's much better than doing an outer join (which will get lots of data from the database, for each
+ * HTTP call).</li>
+ * <li>As this manages users, for security reasons, we'd rather have a DTO layer.</li>
  * </ul>
  * <p>
  * Another option would be to have a specific JPA entity graph to handle this case.
@@ -76,16 +85,19 @@ public class UserResource {
   }
 
   /**
-   * {@code POST  /users}  : Creates a new user.
+   * {@code POST  /users} : Creates a new user.
    * <p>
-   * Creates a new user if the login and email are not already used, and sends an
-   * mail with an activation link.
-   * The user needs to be activated on creation.
+   * Creates a new user if the login and email are not already used, and sends an mail with an activation link. The user
+   * needs to be activated on creation.
    *
-   * @param userDTO the user to create.
-   * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new user, or with status {@code 400 (Bad Request)} if the login or email is already in use.
-   * @throws URISyntaxException if the Location URI syntax is incorrect.
-   * @throws BadRequestAlertException {@code 400 (Bad Request)} if the login or email is already in use.
+   * @param userDTO
+   *          the user to create.
+   * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new user, or with status
+   *         {@code 400 (Bad Request)} if the login or email is already in use.
+   * @throws URISyntaxException
+   *           if the Location URI syntax is incorrect.
+   * @throws BadRequestAlertException
+   *           {@code 400 (Bad Request)} if the login or email is already in use.
    */
   @PostMapping("/users")
   @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")")
@@ -93,7 +105,11 @@ public class UserResource {
     log.debug("REST request to save User : {}", userDTO);
 
     if (userDTO.getId() != null) {
-      throw new BadRequestAlertException("A new user cannot already have an ID", "userManagement", "idexists");
+      throw PouetException
+        .builder(StandardMessage.INTERNAL_SERVER_ERROR)
+        .message("Can't create a user with a setted login")
+        .status(ErrorStatus.BAD_REQUEST)
+        .build();
       // Lowercase the user login before comparing with database
     } else if (userRepository.findOneByLogin(userDTO.getLogin().toLowerCase()).isPresent()) {
       throw new LoginAlreadyUsedException();
@@ -112,10 +128,13 @@ public class UserResource {
   /**
    * {@code PUT /users} : Updates an existing User.
    *
-   * @param userDTO the user to update.
+   * @param userDTO
+   *          the user to update.
    * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated user.
-   * @throws EmailAlreadyUsedException {@code 400 (Bad Request)} if the email is already in use.
-   * @throws LoginAlreadyUsedException {@code 400 (Bad Request)} if the login is already in use.
+   * @throws EmailAlreadyUsedException
+   *           {@code 400 (Bad Request)} if the email is already in use.
+   * @throws LoginAlreadyUsedException
+   *           {@code 400 (Bad Request)} if the login is already in use.
    */
   @PutMapping("/users")
   @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")")
@@ -137,7 +156,8 @@ public class UserResource {
   /**
    * {@code GET /users} : get all users.
    *
-   * @param pageable the pagination information.
+   * @param pageable
+   *          the pagination information.
    * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body all users.
    */
   @GetMapping("/users")
@@ -149,6 +169,7 @@ public class UserResource {
 
   /**
    * Gets a list of all roles.
+   *
    * @return a string list of all roles.
    */
   @GetMapping("/users/authorities")
@@ -160,8 +181,10 @@ public class UserResource {
   /**
    * {@code GET /users/:login} : get the "login" user.
    *
-   * @param login the login of the user to find.
-   * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the "login" user, or with status {@code 404 (Not Found)}.
+   * @param login
+   *          the login of the user to find.
+   * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the "login" user, or with status
+   *         {@code 404 (Not Found)}.
    */
   @GetMapping("/users/{login:" + Constants.LOGIN_REGEX + "}")
   public ResponseEntity<UserDTO> getUser(@PathVariable String login) {
@@ -172,7 +195,8 @@ public class UserResource {
   /**
    * {@code DELETE /users/:login} : delete the "login" User.
    *
-   * @param login the login of the user to delete.
+   * @param login
+   *          the login of the user to delete.
    * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}.
    */
   @DeleteMapping("/users/{login:" + Constants.LOGIN_REGEX + "}")
diff --git a/src/main/java/com/ippon/pouet/web/rest/errors/BadRequestAlertException.java b/src/main/java/com/ippon/pouet/web/rest/errors/BadRequestAlertException.java
deleted file mode 100644
index 8259bdb1a096ac35a0017054fc07bb00f05952eb..0000000000000000000000000000000000000000
--- a/src/main/java/com/ippon/pouet/web/rest/errors/BadRequestAlertException.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.ippon.pouet.web.rest.errors;
-
-import com.ippon.pouet.common.domain.Generated;
-import java.net.URI;
-import java.util.HashMap;
-import java.util.Map;
-import org.zalando.problem.AbstractThrowableProblem;
-import org.zalando.problem.Status;
-
-@Generated
-public class BadRequestAlertException extends AbstractThrowableProblem {
-  private static final long serialVersionUID = 1L;
-
-  private final String entityName;
-
-  private final String errorKey;
-
-  public BadRequestAlertException(String defaultMessage, String entityName, String errorKey) {
-    this(ErrorConstants.DEFAULT_TYPE, defaultMessage, entityName, errorKey);
-  }
-
-  public BadRequestAlertException(URI type, String defaultMessage, String entityName, String errorKey) {
-    super(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(entityName, errorKey));
-    this.entityName = entityName;
-    this.errorKey = errorKey;
-  }
-
-  public String getEntityName() {
-    return entityName;
-  }
-
-  public String getErrorKey() {
-    return errorKey;
-  }
-
-  private static Map<String, Object> getAlertParameters(String entityName, String errorKey) {
-    Map<String, Object> parameters = new HashMap<>();
-    parameters.put("message", "error." + errorKey);
-    parameters.put("params", entityName);
-    return parameters;
-  }
-}
diff --git a/src/main/java/com/ippon/pouet/web/rest/errors/EmailAlreadyUsedException.java b/src/main/java/com/ippon/pouet/web/rest/errors/EmailAlreadyUsedException.java
deleted file mode 100644
index 187ca67d650a377c9afdd38d57c9fb8ca0bf9979..0000000000000000000000000000000000000000
--- a/src/main/java/com/ippon/pouet/web/rest/errors/EmailAlreadyUsedException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.ippon.pouet.web.rest.errors;
-
-import com.ippon.pouet.common.domain.Generated;
-
-@Generated
-public class EmailAlreadyUsedException extends BadRequestAlertException {
-  private static final long serialVersionUID = 1L;
-
-  public EmailAlreadyUsedException() {
-    super(ErrorConstants.EMAIL_ALREADY_USED_TYPE, "Email is already in use!", "userManagement", "emailexists");
-  }
-}
diff --git a/src/main/java/com/ippon/pouet/web/rest/errors/ErrorConstants.java b/src/main/java/com/ippon/pouet/web/rest/errors/ErrorConstants.java
deleted file mode 100644
index d505d8756bf136d0a370d0ecc4fda10515f1a88e..0000000000000000000000000000000000000000
--- a/src/main/java/com/ippon/pouet/web/rest/errors/ErrorConstants.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.ippon.pouet.web.rest.errors;
-
-import com.ippon.pouet.common.domain.Generated;
-import java.net.URI;
-
-@Generated
-public final class ErrorConstants {
-  public static final String ERR_CONCURRENCY_FAILURE = "error.concurrencyFailure";
-  public static final String ERR_VALIDATION = "error.validation";
-  public static final String PROBLEM_BASE_URL = "https://www.jhipster.tech/problem";
-  public static final URI DEFAULT_TYPE = URI.create(PROBLEM_BASE_URL + "/problem-with-message");
-  public static final URI CONSTRAINT_VIOLATION_TYPE = URI.create(PROBLEM_BASE_URL + "/constraint-violation");
-  public static final URI INVALID_PASSWORD_TYPE = URI.create(PROBLEM_BASE_URL + "/invalid-password");
-  public static final URI EMAIL_ALREADY_USED_TYPE = URI.create(PROBLEM_BASE_URL + "/email-already-used");
-  public static final URI LOGIN_ALREADY_USED_TYPE = URI.create(PROBLEM_BASE_URL + "/login-already-used");
-
-  private ErrorConstants() {}
-}
diff --git a/src/main/java/com/ippon/pouet/web/rest/errors/ExceptionTranslator.java b/src/main/java/com/ippon/pouet/web/rest/errors/ExceptionTranslator.java
deleted file mode 100644
index 467eb8077f6818acd54ced9a19f214fddc942ce0..0000000000000000000000000000000000000000
--- a/src/main/java/com/ippon/pouet/web/rest/errors/ExceptionTranslator.java
+++ /dev/null
@@ -1,136 +0,0 @@
-package com.ippon.pouet.web.rest.errors;
-
-import com.ippon.pouet.common.domain.Generated;
-import io.github.jhipster.web.util.HeaderUtil;
-import java.util.List;
-import java.util.stream.Collectors;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.servlet.http.HttpServletRequest;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.dao.ConcurrencyFailureException;
-import org.springframework.http.ResponseEntity;
-import org.springframework.validation.BindingResult;
-import org.springframework.web.bind.MethodArgumentNotValidException;
-import org.springframework.web.bind.annotation.ControllerAdvice;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-import org.springframework.web.context.request.NativeWebRequest;
-import org.zalando.problem.DefaultProblem;
-import org.zalando.problem.Problem;
-import org.zalando.problem.ProblemBuilder;
-import org.zalando.problem.Status;
-import org.zalando.problem.spring.web.advice.ProblemHandling;
-import org.zalando.problem.spring.web.advice.security.SecurityAdviceTrait;
-import org.zalando.problem.violations.ConstraintViolationProblem;
-
-/**
- * Controller advice to translate the server side exceptions to client-friendly json structures.
- * The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807).
- */
-@Generated
-@ControllerAdvice
-public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait {
-  private static final String FIELD_ERRORS_KEY = "fieldErrors";
-  private static final String MESSAGE_KEY = "message";
-  private static final String PATH_KEY = "path";
-  private static final String VIOLATIONS_KEY = "violations";
-
-  @Value("${jhipster.clientApp.name}")
-  private String applicationName;
-
-  /**
-   * Post-process the Problem payload to add the message key for the front-end if needed.
-   */
-  @Override
-  public ResponseEntity<Problem> process(@Nullable ResponseEntity<Problem> entity, NativeWebRequest request) {
-    if (entity == null) {
-      return entity;
-    }
-    Problem problem = entity.getBody();
-    if (!(problem instanceof ConstraintViolationProblem || problem instanceof DefaultProblem)) {
-      return entity;
-    }
-    ProblemBuilder builder = Problem
-      .builder()
-      .withType(Problem.DEFAULT_TYPE.equals(problem.getType()) ? ErrorConstants.DEFAULT_TYPE : problem.getType())
-      .withStatus(problem.getStatus())
-      .withTitle(problem.getTitle())
-      .with(PATH_KEY, request.getNativeRequest(HttpServletRequest.class).getRequestURI());
-
-    if (problem instanceof ConstraintViolationProblem) {
-      builder.with(VIOLATIONS_KEY, ((ConstraintViolationProblem) problem).getViolations()).with(MESSAGE_KEY, ErrorConstants.ERR_VALIDATION);
-    } else {
-      builder.withCause(((DefaultProblem) problem).getCause()).withDetail(problem.getDetail()).withInstance(problem.getInstance());
-      problem.getParameters().forEach(builder::with);
-      if (!problem.getParameters().containsKey(MESSAGE_KEY) && problem.getStatus() != null) {
-        builder.with(MESSAGE_KEY, "error.http." + problem.getStatus().getStatusCode());
-      }
-    }
-    return new ResponseEntity<>(builder.build(), entity.getHeaders(), entity.getStatusCode());
-  }
-
-  @Override
-  public ResponseEntity<Problem> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, @Nonnull NativeWebRequest request) {
-    BindingResult result = ex.getBindingResult();
-    List<FieldErrorVM> fieldErrors = result
-      .getFieldErrors()
-      .stream()
-      .map(f -> new FieldErrorVM(f.getObjectName().replaceFirst("DTO$", ""), f.getField(), f.getCode()))
-      .collect(Collectors.toList());
-
-    Problem problem = Problem
-      .builder()
-      .withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE)
-      .withTitle("Method argument not valid")
-      .withStatus(defaultConstraintViolationStatus())
-      .with(MESSAGE_KEY, ErrorConstants.ERR_VALIDATION)
-      .with(FIELD_ERRORS_KEY, fieldErrors)
-      .build();
-    return create(ex, problem, request);
-  }
-
-  @ExceptionHandler
-  public ResponseEntity<Problem> handleEmailAlreadyUsedException(
-    com.ippon.pouet.service.EmailAlreadyUsedException ex,
-    NativeWebRequest request
-  ) {
-    EmailAlreadyUsedException problem = new EmailAlreadyUsedException();
-    return create(
-      problem,
-      request,
-      HeaderUtil.createFailureAlert(applicationName, true, problem.getEntityName(), problem.getErrorKey(), problem.getMessage())
-    );
-  }
-
-  @ExceptionHandler
-  public ResponseEntity<Problem> handleUsernameAlreadyUsedException(
-    com.ippon.pouet.service.UsernameAlreadyUsedException ex,
-    NativeWebRequest request
-  ) {
-    LoginAlreadyUsedException problem = new LoginAlreadyUsedException();
-    return create(
-      problem,
-      request,
-      HeaderUtil.createFailureAlert(applicationName, true, problem.getEntityName(), problem.getErrorKey(), problem.getMessage())
-    );
-  }
-
-  @ExceptionHandler
-  public ResponseEntity<Problem> handleInvalidPasswordException(
-    com.ippon.pouet.service.InvalidPasswordException ex,
-    NativeWebRequest request
-  ) {
-    return create(new InvalidPasswordException(), request);
-  }
-
-  @ExceptionHandler
-  public ResponseEntity<Problem> handleBadRequestAlertException(BadRequestAlertException ex, NativeWebRequest request) {
-    return create(ex, request, HeaderUtil.createFailureAlert(applicationName, true, ex.getEntityName(), ex.getErrorKey(), ex.getMessage()));
-  }
-
-  @ExceptionHandler
-  public ResponseEntity<Problem> handleConcurrencyFailure(ConcurrencyFailureException ex, NativeWebRequest request) {
-    Problem problem = Problem.builder().withStatus(Status.CONFLICT).with(MESSAGE_KEY, ErrorConstants.ERR_CONCURRENCY_FAILURE).build();
-    return create(ex, problem, request);
-  }
-}
diff --git a/src/main/java/com/ippon/pouet/web/rest/errors/FieldErrorVM.java b/src/main/java/com/ippon/pouet/web/rest/errors/FieldErrorVM.java
deleted file mode 100644
index 3fcd880cd980664ad38ff07ad09f65fc47563dff..0000000000000000000000000000000000000000
--- a/src/main/java/com/ippon/pouet/web/rest/errors/FieldErrorVM.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.ippon.pouet.web.rest.errors;
-
-import com.ippon.pouet.common.domain.Generated;
-import java.io.Serializable;
-
-@Generated
-public class FieldErrorVM implements Serializable {
-  private static final long serialVersionUID = 1L;
-
-  private final String objectName;
-
-  private final String field;
-
-  private final String message;
-
-  public FieldErrorVM(String dto, String field, String message) {
-    this.objectName = dto;
-    this.field = field;
-    this.message = message;
-  }
-
-  public String getObjectName() {
-    return objectName;
-  }
-
-  public String getField() {
-    return field;
-  }
-
-  public String getMessage() {
-    return message;
-  }
-}
diff --git a/src/main/java/com/ippon/pouet/web/rest/errors/InvalidPasswordException.java b/src/main/java/com/ippon/pouet/web/rest/errors/InvalidPasswordException.java
deleted file mode 100644
index 6c9ed37b72b5fb66a30532ee0923412df0b4d23a..0000000000000000000000000000000000000000
--- a/src/main/java/com/ippon/pouet/web/rest/errors/InvalidPasswordException.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.ippon.pouet.web.rest.errors;
-
-import com.ippon.pouet.common.domain.Generated;
-import org.zalando.problem.AbstractThrowableProblem;
-import org.zalando.problem.Status;
-
-@Generated
-public class InvalidPasswordException extends AbstractThrowableProblem {
-  private static final long serialVersionUID = 1L;
-
-  public InvalidPasswordException() {
-    super(ErrorConstants.INVALID_PASSWORD_TYPE, "Incorrect password", Status.BAD_REQUEST);
-  }
-}
diff --git a/src/main/java/com/ippon/pouet/web/rest/errors/LoginAlreadyUsedException.java b/src/main/java/com/ippon/pouet/web/rest/errors/LoginAlreadyUsedException.java
deleted file mode 100644
index a6c6eae7f444e11c16bcd2c3d10fcad653e24dee..0000000000000000000000000000000000000000
--- a/src/main/java/com/ippon/pouet/web/rest/errors/LoginAlreadyUsedException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.ippon.pouet.web.rest.errors;
-
-import com.ippon.pouet.common.domain.Generated;
-
-@Generated
-public class LoginAlreadyUsedException extends BadRequestAlertException {
-  private static final long serialVersionUID = 1L;
-
-  public LoginAlreadyUsedException() {
-    super(ErrorConstants.LOGIN_ALREADY_USED_TYPE, "Login name already used!", "userManagement", "userexists");
-  }
-}
diff --git a/src/main/java/com/ippon/pouet/web/rest/errors/package-info.java b/src/main/java/com/ippon/pouet/web/rest/errors/package-info.java
deleted file mode 100644
index 8615f5cbc5b236599aeffcadca86e0d8d0aafa86..0000000000000000000000000000000000000000
--- a/src/main/java/com/ippon/pouet/web/rest/errors/package-info.java
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * Specific errors used with Zalando's "problem-spring-web" library.
- *
- * More information on https://github.com/zalando/problem-spring-web
- */
-package com.ippon.pouet.web.rest.errors;
diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties
index ff1e34d95c2ff9012fd14cb73e60d6e8b1564ca8..4be83a16ce5b816a4dbcce5d02262f76f2f29bd3 100644
--- a/src/main/resources/i18n/messages.properties
+++ b/src/main/resources/i18n/messages.properties
@@ -1,21 +1,40 @@
 # Error page
-error.title=Your request cannot be processed
-error.subtitle=Sorry, an error has occurred.
-error.status=Status:
-error.message=Message:
+error.title=Votre demande ne peut être traitée
+error.subtitle=Désolé, une erreur s'est produite.
+error.status=Statut :
+error.message=Message :
 
 # Activation email
-email.activation.title=pouet account activation is required
-email.activation.greeting=Dear {0}
-email.activation.text1=Your pouet account has been created, please click on the URL below to activate it:
-email.activation.text2=Regards,
-email.signature=pouet Team.
+email.activation.title=Activation de votre compte pouet
+email.activation.greeting=Cher {0}
+email.activation.text1=Votre compte pouet a été créé, pour l'activer merci de cliquer sur le lien ci-dessous :
+email.activation.text2=Cordialement,
+email.signature=pouet.
 
 # Creation email
-email.creation.text1=Your pouet account has been created, please click on the URL below to access it:
+email.creation.text1=Votre compte pouet a été créé, merci de cliquer sur le lien ci-dessous pour y accéder :
 
 # Reset email
-email.reset.title=pouet password reset
-email.reset.greeting=Dear {0}
-email.reset.text1=For your pouet account a password reset was requested, please click on the URL below to reset it:
-email.reset.text2=Regards,
+email.reset.title=pouet Réinitialisation de mot de passe
+email.reset.greeting=Cher {0}
+email.reset.text1=Un nouveau mot de passe pour votre compte pouet a été demandé, veuillez cliquer sur le lien ci-dessous pour le réinitialiser :
+email.reset.text2=Cordialement,
+
+
+################################################################
+######################## Error messages ########################
+################################################################
+pouet.error.status-exception=Une erreur {{ status }} est survenue lors du traitement de votre requête.
+
+pouet.error.user.bad-request=Les données que vous avez saisies sont incorrectes.
+pouet.error.user.mandatory=Le champ est obligatoire.
+pouet.error.user.wrong-format=Le format n'est pas correct, il doit respecter "{{ regexp }}".
+pouet.error.user.authentication-not-authenticated=Vous devez être authentifié pour acceder à cette ressource.
+pouet.error.user.access-denied=Vous n'avez pas les droits suffisants pour acceder à cette ressource.
+pouet.error.user.e-mail-already-used=Cette adresse email est déjà utilisée dans pouet.
+pouet.error.user.login-already-used=Ce login est déjà utilsié dans pouet.
+pouet.error.user.invalid-password=Ce mot de passe n'est pas valide.
+
+pouet.error.server.internal-server-error=Une erreur est survenue, notre équipe travaille à sa résolution !
+pouet.error.server.mandatory-null=Une erreur est survenue, un champ obligatoire ({{ field }}) est null dans notre système. Notre équipe travaille à la résolution de ce problème !
+pouet.error.server.mandatory-blank=Une erreur est survenue, un champ obligatoire ({{ field }}) est vide dans notre système. Notre équipe travaille à la résolution de ce problème !
diff --git a/src/main/resources/i18n/messages_en.properties b/src/main/resources/i18n/messages_en.properties
index 80f741a8996547653e020d4fd30051aef50d1030..519e397e00974323215fbfc635a98c0f87e7d0be 100644
--- a/src/main/resources/i18n/messages_en.properties
+++ b/src/main/resources/i18n/messages_en.properties
@@ -19,3 +19,22 @@ email.reset.title=pouet password reset
 email.reset.greeting=Dear {0}
 email.reset.text1=For your pouet account a password reset was requested, please click on the URL below to reset it:
 email.reset.text2=Regards,
+
+
+################################################################
+######################## Error messages ########################
+################################################################
+pouet.error.status-exception=An error {{ status }} occured while handling your request.
+
+pouet.error.user.bad-request=The values you entered are incorrects.
+pouet.error.user.mandatory=The field is mandatory.
+pouet.error.user.wrong-format=The format is incorrect, it has to match "{{ regexp }}".
+pouet.error.user.authentication-not-authenticated=You must be authenticated to access this resource.
+pouet.error.user.access-denied=You don't have sufficient rights to access this resource.
+pouet.error.user.e-mail-already-used=This email address is already used in the pouet.
+pouet.error.user.login-already-used=This login is already used in pouet.
+pouet.error.user.invalid-password=This password is not valid.
+
+pouet.error.server.internal-server-error=An error occurs, our team is working to fix it!
+pouet.error.server.mandatory-null=An error occurs, a mandatory field ({{ field }}) was null in our system. Our team is working to fix it!
+pouet.error.server.mandatory-blank=An error occurs, a mandatory field ({{ field }}) was blank in our system. Our team is working to fix it!
diff --git a/src/main/resources/i18n/messages_fr.properties b/src/main/resources/i18n/messages_fr.properties
deleted file mode 100644
index 92f91cb1718d7ebcbaae476a9ad0501f2f585949..0000000000000000000000000000000000000000
--- a/src/main/resources/i18n/messages_fr.properties
+++ /dev/null
@@ -1,21 +0,0 @@
-# Error page
-error.title=Votre demande ne peut être traitée
-error.subtitle=Désolé, une erreur s'est produite.
-error.status=Statut :
-error.message=Message :
-
-# Activation email
-email.activation.title=Activation de votre compte pouet
-email.activation.greeting=Cher {0}
-email.activation.text1=Votre compte pouet a été créé, pour l'activer merci de cliquer sur le lien ci-dessous :
-email.activation.text2=Cordialement,
-email.signature=pouet.
-
-# Creation email
-email.creation.text1=Votre compte pouet a été créé, merci de cliquer sur le lien ci-dessous pour y accéder :
-
-# Reset email
-email.reset.title=pouet Réinitialisation de mot de passe
-email.reset.greeting=Cher {0}
-email.reset.text1=Un nouveau mot de passe pour votre compte pouet a été demandé, veuillez cliquer sur le lien ci-dessous pour le réinitialiser :
-email.reset.text2=Cordialement,
diff --git a/src/test/java/com/ippon/pouet/common/PouetIntTest.java b/src/test/java/com/ippon/pouet/common/PouetIntTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..75ebde4017efdf9b42b1e8e6bf51e2ec7485e887
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/PouetIntTest.java
@@ -0,0 +1,19 @@
+package com.ippon.pouet.common;
+
+import com.ippon.pouet.PouetApp;
+import io.github.jhipster.config.JHipsterConstants;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.transaction.Transactional;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ActiveProfiles;
+
+@Transactional
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@ActiveProfiles(JHipsterConstants.SPRING_PROFILE_TEST)
+@SpringBootTest(classes = PouetApp.class)
+public @interface PouetIntTest {
+}
diff --git a/src/test/java/com/ippon/pouet/common/domain/error/AssertUnitTest.java b/src/test/java/com/ippon/pouet/common/domain/error/AssertUnitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5315d53404601980ef4aa9fdaed4019f98def0d0
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/domain/error/AssertUnitTest.java
@@ -0,0 +1,50 @@
+package com.ippon.pouet.common.domain.error;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class AssertUnitTest {
+
+  @Test
+  void shouldNotValidateNullInputs() {
+    assertThatThrownBy(() -> Assert.notNull("field", null))
+      .isExactlyInstanceOf(MissingMandatoryValueException.class)
+      .hasMessageContaining("\"field\"");
+  }
+
+  @Test
+  void shouldNotValidateNullString() {
+    assertThatThrownBy(() -> Assert.notBlank("field", null))
+      .isExactlyInstanceOf(MissingMandatoryValueException.class)
+      .hasMessageContaining("\"field\"")
+      .hasMessageContaining("(null)");
+  }
+
+  @Test
+  void shouldNotValidateEmptyString() {
+    assertNotBlankString("");
+  }
+
+  @Test
+  void shouldNotValidateSpaceString() {
+    assertNotBlankString(" ");
+  }
+
+  @Test
+  void shouldNotValidateTabString() {
+    assertNotBlankString("  ");
+  }
+
+  private void assertNotBlankString(String input) {
+    assertThatThrownBy(() -> Assert.notBlank("field", input))
+      .isExactlyInstanceOf(MissingMandatoryValueException.class)
+      .hasMessageContaining("\"field\"")
+      .hasMessageContaining("(blank)");
+  }
+
+  @Test
+  void shouldValidateSettedString() {
+    assertThatCode(() -> Assert.notBlank("field", "hey")).doesNotThrowAnyException();
+  }
+}
diff --git a/src/test/java/com/ippon/pouet/common/domain/error/ErrorMessagesUnitTest.java b/src/test/java/com/ippon/pouet/common/domain/error/ErrorMessagesUnitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..db7c84eb0b032c3022dea0c9059d89d23e0fb413
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/domain/error/ErrorMessagesUnitTest.java
@@ -0,0 +1,83 @@
+package com.ippon.pouet.common.domain.error;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.Test;
+import org.reflections.Reflections;
+import org.reflections.scanners.SubTypesScanner;
+import org.reflections.util.ClasspathHelper;
+import org.reflections.util.ConfigurationBuilder;
+import org.reflections.util.FilterBuilder;
+
+public class ErrorMessagesUnitTest {
+  private static final String BASE_PACKAGE = "com.ippon.pouet";
+
+  private static final Set<Class<? extends PouetMessage>> errors = new Reflections(
+    new ConfigurationBuilder()
+      .setUrls(ClasspathHelper.forPackage(BASE_PACKAGE))
+      .setScanners(new SubTypesScanner())
+      .filterInputsBy(new FilterBuilder().includePackage(BASE_PACKAGE))
+  )
+  .getSubTypesOf(PouetMessage.class);
+
+  @Test
+  public void shouldHaveOnlyEnumImplementations() {
+    errors.forEach(
+      error ->
+        assertThat(error.isEnum() || error.isInterface())
+          .as("Implementations of " + PouetMessage.class.getName() + " must be enums and " + error.getName() + " wasn't")
+          .isTrue()
+    );
+  }
+
+  @Test
+  public void shouldHaveMessagesForAllKeys() {
+    Collection<Properties> messages = loadMessages();
+
+    errors
+      .stream()
+      .filter(error -> error.isEnum())
+      .forEach(error -> Arrays.stream(error.getEnumConstants()).forEach(value -> messages.forEach(assertMessageExist(value))));
+  }
+
+  private List<Properties> loadMessages() {
+    try {
+      return Files.list(Paths.get("src/main/resources/i18n")).map(toProperties()).collect(Collectors.toList());
+    } catch (IOException e) {
+      throw new AssertionError();
+    }
+  }
+
+  private Function<Path, Properties> toProperties() {
+    return file -> {
+      Properties properties = new Properties();
+      try {
+        properties.load(Files.newInputStream(file));
+      } catch (IOException e) {
+        throw new AssertionError();
+      }
+
+      return properties;
+    };
+  }
+
+  private Consumer<Properties> assertMessageExist(PouetMessage value) {
+    return currentMessages -> {
+      assertThat(currentMessages.getProperty("pouet.error." + value.getMessageKey()))
+        .as("Can't find message for " + value.getMessageKey() + " in all files, check your configuration")
+        .isNotBlank();
+    };
+  }
+}
diff --git a/src/test/java/com/ippon/pouet/common/domain/error/PouetExceptionUnitTest.java b/src/test/java/com/ippon/pouet/common/domain/error/PouetExceptionUnitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6532a357ef5cc852e3ea8442a9d38a5d2e129ef8
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/domain/error/PouetExceptionUnitTest.java
@@ -0,0 +1,56 @@
+package com.ippon.pouet.common.domain.error;
+
+import static org.assertj.core.api.Assertions.*;
+
+import com.ippon.pouet.common.domain.error.PouetException.PouetExceptionBuilder;
+import org.junit.jupiter.api.Test;
+
+class PouetExceptionUnitTest {
+
+  @Test
+  public void shouldGetUnmodifiableArguments() {
+    assertThatThrownBy(() -> fullBuilder().build().getArguments().clear()).isExactlyInstanceOf(UnsupportedOperationException.class);
+  }
+
+  @Test
+  public void shouldBuildWithoutBuilder() {
+    PouetException exception = new PouetException(null);
+
+    assertThat(exception.getCause()).isNull();
+    assertThat(exception.getMessage()).isNull();
+    assertThat(exception.getStatus()).isNull();
+    assertThat(exception.getArguments()).isNull();
+    assertThat(exception.getPouetMessage()).isNull();
+  }
+
+  @Test
+  public void shouldGetExceptionInformation() {
+    PouetException exception = fullBuilder().build();
+
+    assertThat(exception.getArguments()).hasSize(2).contains(entry("key", "value"), entry("other", "test"));
+    assertThat(exception.getStatus()).isEqualTo(ErrorStatus.BAD_REQUEST);
+    assertThat(exception.getMessage()).isEqualTo("Error message");
+    assertThat(exception.getPouetMessage()).isEqualTo(StandardMessage.INTERNAL_SERVER_ERROR);
+    assertThat(exception.getCause()).isExactlyInstanceOf(RuntimeException.class);
+  }
+
+  @Test
+  void shouldMapNullArgumentAsNullString() {
+    assertThat(fullBuilder().argument("nullable", null).build().getArguments().get("nullable")).isEqualTo("null");
+  }
+
+  @Test
+  void shouldGetObjectsToString() {
+    assertThat(fullBuilder().argument("object", 4).build().getArguments().get("object")).isEqualTo("4");
+  }
+
+  private PouetExceptionBuilder fullBuilder() {
+    return PouetException
+      .builder(StandardMessage.INTERNAL_SERVER_ERROR)
+      .argument("key", "value")
+      .argument("other", "test")
+      .status(ErrorStatus.BAD_REQUEST)
+      .message("Error message")
+      .cause(new RuntimeException());
+  }
+}
diff --git a/src/test/java/com/ippon/pouet/common/infrastructure/primary/ArgumentsReplacerUnitTest.java b/src/test/java/com/ippon/pouet/common/infrastructure/primary/ArgumentsReplacerUnitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0119176b40e6e9159bc6a4d79b2fef062c73fc18
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/infrastructure/primary/ArgumentsReplacerUnitTest.java
@@ -0,0 +1,25 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Map;
+import org.junit.jupiter.api.Test;
+
+class ArgumentsReplacerUnitTest {
+
+  @Test
+  public void shouldNotReplaceArgumentsInNullMessage() {
+    assertThat(ArgumentsReplacer.replaceArguments(null, Map.of("key", "value"))).isNull();
+  }
+
+  @Test
+  public void shouldNotReplaceArgumentsWithoutArguments() {
+    assertThat(ArgumentsReplacer.replaceArguments("Hey {{ user }}", null)).isEqualTo("Hey {{ user }}");
+  }
+
+  @Test
+  public void shouldReplaceKnownArguments() {
+    assertThat(ArgumentsReplacer.replaceArguments("Hey {{ user }}, how's {{ friend }} doing? Say {{user}}", Map.of("user", "Joe")))
+      .isEqualTo("Hey Joe, how's {{ friend }} doing? Say Joe");
+  }
+}
diff --git a/src/test/java/com/ippon/pouet/common/infrastructure/primary/ComplicatedRequest.java b/src/test/java/com/ippon/pouet/common/infrastructure/primary/ComplicatedRequest.java
new file mode 100644
index 0000000000000000000000000000000000000000..718c0b5e1049bab18984a418ec9e69907227d3ef
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/infrastructure/primary/ComplicatedRequest.java
@@ -0,0 +1,17 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import javax.validation.constraints.Pattern;
+
+public class ComplicatedRequest {
+  private String value;
+
+  public ComplicatedRequest(@JsonProperty("value") String value) {
+    this.value = value;
+  }
+
+  @Pattern(message = ValidationMessage.WRONG_FORMAT, regexp = "complicated")
+  public String getValue() {
+    return value;
+  }
+}
diff --git a/src/test/java/com/ippon/pouet/common/infrastructure/primary/ErrorsResource.java b/src/test/java/com/ippon/pouet/common/infrastructure/primary/ErrorsResource.java
new file mode 100644
index 0000000000000000000000000000000000000000..4baa1f0fd7dcf258ca2b8265126e28bc52d8cbdb
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/infrastructure/primary/ErrorsResource.java
@@ -0,0 +1,74 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import com.ippon.pouet.common.domain.error.ErrorStatus;
+import com.ippon.pouet.common.domain.error.PouetException;
+import com.ippon.pouet.common.domain.error.StandardMessage;
+import javax.validation.constraints.Pattern;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.server.ResponseStatusException;
+
+/**
+ * Resource to expose errors endpoints
+ */
+@Validated
+@RestController
+@RequestMapping("/errors")
+class ErrorsResource {
+  private static final Logger logger = LoggerFactory.getLogger(ErrorsResource.class);
+
+  @GetMapping("/runtime-exceptions")
+  public void runtimeException() {
+    throw new RuntimeException();
+  }
+
+  @GetMapping("/access-denied")
+  public void accessDeniedException() {
+    throw new AccessDeniedException("You shall not pass!");
+  }
+
+  @PostMapping("/portailpro-exceptions")
+  public void portailProException() {
+    throw PouetException
+      .builder(StandardMessage.INTERNAL_SERVER_ERROR)
+      .cause(new RuntimeException())
+      .status(ErrorStatus.INTERNAL_SERVER_ERROR)
+      .message("Oops")
+      .argument("key", "value")
+      .build();
+  }
+
+  @PostMapping("/responsestatus-with-message-exceptions")
+  public void responseStatusWithMessageException() {
+    throw new ResponseStatusException(HttpStatus.NOT_FOUND, "oops");
+  }
+
+  @PostMapping("/responsestatus-without-message-exceptions")
+  public void responseStatusWithoutMessageException() {
+    throw new ResponseStatusException(HttpStatus.NOT_FOUND);
+  }
+
+  @GetMapping("/{complicated}")
+  public void complicatedArg(
+    @Validated @Pattern(message = ValidationMessage.WRONG_FORMAT, regexp = "complicated") @PathVariable("complicated") String complicated
+  ) {
+    logger.info("Congratulations you got it right!");
+  }
+
+  @PostMapping("/oops")
+  public void complicatedBody(@Validated @RequestBody ComplicatedRequest request) {
+    logger.info("You got it right!");
+  }
+
+  @PostMapping("/not-deserializables")
+  public void notDeserializable(@RequestBody NotDeserializable request) {}
+}
diff --git a/src/test/java/com/ippon/pouet/common/infrastructure/primary/LogSpy.java b/src/test/java/com/ippon/pouet/common/infrastructure/primary/LogSpy.java
new file mode 100644
index 0000000000000000000000000000000000000000..16f1fcb99c413722ae3de29f57cdec9d80c2d870
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/infrastructure/primary/LogSpy.java
@@ -0,0 +1,62 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import static org.assertj.core.api.Assertions.*;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
+import java.util.function.Predicate;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+import org.slf4j.LoggerFactory;
+
+public final class LogSpy implements BeforeEachCallback, AfterEachCallback, ParameterResolver {
+  private Logger logger;
+  private ListAppender<ILoggingEvent> appender;
+
+  @Override
+  public void beforeEach(ExtensionContext context) throws Exception {
+    appender = new ListAppender<>();
+    logger = (Logger) LoggerFactory.getLogger("com.ippon.pouet");
+    logger.addAppender(appender);
+    logger.setLevel(Level.TRACE);
+    appender.start();
+  }
+
+  @Override
+  public void afterEach(ExtensionContext context) throws Exception {
+    logger.detachAppender(appender);
+  }
+
+  public void assertLogged(Level level, String content) {
+    assertThat(appender.list.stream().anyMatch(withLog(level, content))).isTrue();
+  }
+
+  public void assertLogged(Level level, String content, int count) {
+    assertThat(appender.list.stream().filter(withLog(level, content)).count()).isEqualTo(count);
+  }
+
+  public void assertNotLogged(Level level, String content) {
+    assertThat(appender.list.stream().noneMatch(withLog(level, content))).isTrue();
+  }
+
+  private Predicate<ILoggingEvent> withLog(Level level, String content) {
+    return event -> level.equals(event.getLevel()) && event.toString().contains(content);
+  }
+
+  @Override
+  public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+    throws ParameterResolutionException {
+    return parameterContext.getParameter().getType().equals(LogSpy.class);
+  }
+
+  @Override
+  public LogSpy resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
+    return this;
+  }
+}
diff --git a/src/test/java/com/ippon/pouet/common/infrastructure/primary/NotDeserializable.java b/src/test/java/com/ippon/pouet/common/infrastructure/primary/NotDeserializable.java
new file mode 100644
index 0000000000000000000000000000000000000000..604978ed7190c0d5aa3a8acfe95fb4726c42bbd8
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/infrastructure/primary/NotDeserializable.java
@@ -0,0 +1,13 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+public class NotDeserializable {
+  private String value;
+
+  public NotDeserializable(String value) {
+    this.value = value;
+  }
+
+  public String getValue() {
+    return value;
+  }
+}
diff --git a/src/test/java/com/ippon/pouet/common/infrastructure/primary/PouetErrorHandlerIntTest.java b/src/test/java/com/ippon/pouet/common/infrastructure/primary/PouetErrorHandlerIntTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..12c223e39b2ff6fd4b1292106568a1039a7b08c5
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/infrastructure/primary/PouetErrorHandlerIntTest.java
@@ -0,0 +1,181 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import com.ippon.pouet.common.PouetIntTest;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.util.Locale;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+@PouetIntTest
+class PouetErrorHandlerIntTest {
+  private static final Charset UTF_8 = Charset.forName("UTF-8");
+
+  private static final String FRANCE_TAG = Locale.FRANCE.toLanguageTag();
+
+  @Autowired
+  private PouetErrorHandler exceptionTranslator;
+
+  @Autowired
+  private ErrorsResource resource;
+
+  private MockMvc mockMvc;
+
+  @BeforeEach
+  public void setup() {
+    mockMvc = MockMvcBuilders.standaloneSetup(resource).setControllerAdvice(exceptionTranslator).build();
+  }
+
+  @Test
+  public void shouldMapPortailProExceptions() throws Exception {
+    String response = mockMvc
+      .perform(post(errorEndpoint("portailpro-exceptions")).header(HttpHeaders.ACCEPT_LANGUAGE, FRANCE_TAG))
+      .andExpect(status().isInternalServerError())
+      .andReturn()
+      .getResponse()
+      .getContentAsString(UTF_8);
+
+    assertThat(response).contains("Une erreur est survenue, notre équipe travaille à sa résolution !");
+  }
+
+  @Test
+  void shouldMapResponseStatusWithMessageExceptions() throws UnsupportedEncodingException, Exception {
+    String response = mockMvc
+      .perform(post(errorEndpoint("responsestatus-with-message-exceptions")).header(HttpHeaders.ACCEPT_LANGUAGE, FRANCE_TAG))
+      .andExpect(status().isNotFound())
+      .andReturn()
+      .getResponse()
+      .getContentAsString(UTF_8);
+
+    assertThat(response).contains("oops");
+  }
+
+  @Test
+  void shouldMapResponseStatusWithoutMessageExceptions() throws UnsupportedEncodingException, Exception {
+    String response = mockMvc
+      .perform(post(errorEndpoint("responsestatus-without-message-exceptions")).header(HttpHeaders.ACCEPT_LANGUAGE, FRANCE_TAG))
+      .andExpect(status().isNotFound())
+      .andReturn()
+      .getResponse()
+      .getContentAsString(UTF_8);
+
+    assertThat(response).contains("est survenue").contains("404");
+  }
+
+  @Test
+  public void shouldGetMessagesInOtherLanguage() throws Exception {
+    String response = mockMvc
+      .perform(post(errorEndpoint("portailpro-exceptions")).header(HttpHeaders.ACCEPT_LANGUAGE, Locale.UK.toLanguageTag()))
+      .andExpect(status().isInternalServerError())
+      .andReturn()
+      .getResponse()
+      .getContentAsString(UTF_8);
+
+    assertThat(response).contains("An error occurs, our team is working to fix it!");
+  }
+
+  @Test
+  public void shouldGetMessagesInDefaultLanguage() throws Exception {
+    String response = mockMvc
+      .perform(post(errorEndpoint("portailpro-exceptions")).header(HttpHeaders.ACCEPT_LANGUAGE, Locale.CHINESE.toLanguageTag()))
+      .andExpect(status().isInternalServerError())
+      .andReturn()
+      .getResponse()
+      .getContentAsString(UTF_8);
+
+    assertThat(response).contains("Une erreur est survenue, notre équipe travaille à sa résolution !");
+  }
+
+  @Test
+  public void shouldMapParametersBeanValidationErrors() throws Exception {
+    String response = mockMvc
+      .perform(get(errorEndpoint("oops")).header(HttpHeaders.ACCEPT_LANGUAGE, FRANCE_TAG))
+      .andExpect(status().isBadRequest())
+      .andReturn()
+      .getResponse()
+      .getContentAsString(UTF_8);
+
+    assertThat(response)
+      .contains("Les données que vous avez saisies sont incorrectes.")
+      .contains("Le format n'est pas correct, il doit respecter \\\"complicated\\\".");
+  }
+
+  @Test
+  public void shouldMapBodyBeanValidationErrors() throws Exception {
+    String response = mockMvc
+      .perform(
+        post(errorEndpoint("oops"))
+          .header(HttpHeaders.ACCEPT_LANGUAGE, FRANCE_TAG)
+          .contentType(MediaType.APPLICATION_JSON)
+          .content(TestJson.writeAsString(new ComplicatedRequest("value")))
+      )
+      .andExpect(status().isBadRequest())
+      .andReturn()
+      .getResponse()
+      .getContentAsString(UTF_8);
+
+    assertThat(response)
+      .contains("Les données que vous avez saisies sont incorrectes.")
+      .contains("Le format n'est pas correct, il doit respecter \\\"complicated\\\".");
+  }
+
+  @Test
+  public void shouldMapTechnicalError() throws Exception {
+    String response = mockMvc
+      .perform(
+        post(errorEndpoint("not-deserializables"))
+          .header(HttpHeaders.ACCEPT_LANGUAGE, FRANCE_TAG)
+          .contentType(MediaType.APPLICATION_JSON)
+          .content(TestJson.writeAsString(new NotDeserializable("value")))
+      )
+      .andExpect(status().isInternalServerError())
+      .andReturn()
+      .getResponse()
+      .getContentAsString(UTF_8);
+
+    assertThat(response).contains("Une erreur est survenue, notre équipe travaille à sa résolution !");
+  }
+
+  @Test
+  public void shouldHandleAccessDeniedException() throws Exception {
+    String response = mockMvc
+      .perform(get(errorEndpoint("access-denied")).header(HttpHeaders.ACCEPT_LANGUAGE, FRANCE_TAG))
+      .andExpect(status().isForbidden())
+      .andReturn()
+      .getResponse()
+      .getContentAsString(UTF_8);
+
+    assertThat(response).contains("Vous n'avez pas les droits suffisants");
+  }
+
+  @Test
+  public void shouldHandleRuntimeException() throws Exception {
+    String response = mockMvc
+      .perform(get(errorEndpoint("runtime-exceptions")).header(HttpHeaders.ACCEPT_LANGUAGE, FRANCE_TAG))
+      .andExpect(status().isInternalServerError())
+      .andReturn()
+      .getResponse()
+      .getContentAsString(UTF_8);
+
+    assertThat(response).contains("Une erreur est survenue, notre équipe travaille à sa résolution !");
+  }
+
+  private URI errorEndpoint(String path) {
+    try {
+      return new URI("/errors/" + path);
+    } catch (URISyntaxException e) {
+      throw new AssertionError();
+    }
+  }
+}
diff --git a/src/test/java/com/ippon/pouet/common/infrastructure/primary/PouetErrorHandlerUnitTest.java b/src/test/java/com/ippon/pouet/common/infrastructure/primary/PouetErrorHandlerUnitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1266a8d1abbec25bd5d277dcd55035b7368fb8ea
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/infrastructure/primary/PouetErrorHandlerUnitTest.java
@@ -0,0 +1,214 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import ch.qos.logback.classic.Level;
+import com.ippon.pouet.common.domain.error.ErrorStatus;
+import com.ippon.pouet.common.domain.error.PouetException;
+import com.ippon.pouet.common.domain.error.StandardMessage;
+import java.nio.charset.Charset;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+import org.junit.BeforeClass;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.springframework.context.MessageSource;
+import org.springframework.context.NoSuchMessageException;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.InsufficientAuthenticationException;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.web.client.RestClientResponseException;
+import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
+
+@ExtendWith({ SpringExtension.class, LogSpy.class })
+class PouetErrorHandlerUnitTest {
+  @Mock
+  private MessageSource messages;
+
+  @InjectMocks
+  private PouetErrorHandler handler;
+
+  private final LogSpy logs;
+
+  public PouetErrorHandlerUnitTest(LogSpy logs) {
+    this.logs = logs;
+  }
+
+  @BeforeClass
+  public void loadUserLocale() {
+    LocaleContextHolder.setLocale(Locale.FRANCE);
+  }
+
+  @Test
+  public void shouldHandleAsServerErrorWithoutStatus() {
+    ResponseEntity<PouetError> response = handler.handlePouetException(PouetException.builder(StandardMessage.USER_MANDATORY).build());
+
+    assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
+  }
+
+  @Test
+  void shouldHandleAllErrorsAsUniqueHttpCode() {
+    Set<HttpStatus> statuses = new HashSet<>();
+
+    for (ErrorStatus status : ErrorStatus.values()) {
+      ResponseEntity<PouetError> response = handler.handlePouetException(
+        PouetException.builder(StandardMessage.USER_MANDATORY).status(status).build()
+      );
+
+      statuses.add(response.getStatusCode());
+    }
+
+    assertThat(statuses.size()).isEqualTo(ErrorStatus.values().length);
+  }
+
+  @Test
+  public void shouldGetDefaultPouetMessageWithoutMessageForBadRequest() {
+    ResponseEntity<PouetError> response = handler.handlePouetException(
+      PouetException.builder(null).status(ErrorStatus.BAD_REQUEST).build()
+    );
+
+    assertThat(response.getBody().getErrorType()).isEqualTo("user.bad-request");
+  }
+
+  @Test
+  public void shouldGetDefaultPouetMessageWithoutMessageForServerError() {
+    ResponseEntity<PouetError> response = handler.handlePouetException(PouetException.builder(null).build());
+
+    assertThat(response.getBody().getErrorType()).isEqualTo("server.internal-server-error");
+  }
+
+  @Test
+  public void shouldGetDefaultMessageForUnknownMessage() {
+    when(messages.getMessage("pouet.error.hey", null, Locale.FRANCE)).thenThrow(new NoSuchMessageException("hey"));
+    when(messages.getMessage("pouet.error.server.internal-server-error", null, Locale.FRANCE)).thenReturn("User message");
+    ResponseEntity<PouetError> response = handler.handlePouetException(PouetException.builder(() -> "hey").build());
+
+    PouetError body = response.getBody();
+    assertThat(body.getErrorType()).isEqualTo("hey");
+    assertThat(body.getMessage()).isEqualTo("User message");
+  }
+
+  @Test
+  public void shouldHandleUserPouetExceptionWithMessageWithoutArguments() {
+    RuntimeException cause = new RuntimeException();
+    PouetException exception = PouetException
+      .builder(StandardMessage.USER_MANDATORY)
+      .cause(cause)
+      .status(ErrorStatus.BAD_REQUEST)
+      .message("Hum")
+      .build();
+
+    when(messages.getMessage("pouet.error.user.mandatory", null, Locale.FRANCE)).thenReturn("User message");
+    ResponseEntity<PouetError> response = handler.handlePouetException(exception);
+
+    assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
+
+    PouetError body = response.getBody();
+    assertThat(body.getErrorType()).isEqualTo("user.mandatory");
+    assertThat(body.getMessage()).isEqualTo("User message");
+    assertThat(body.getFieldsErrors()).isNull();
+  }
+
+  @Test
+  public void shouldReplaceArgumentsValueFromPouetException() {
+    when(messages.getMessage("pouet.error.user.mandatory", null, Locale.FRANCE)).thenReturn("User {{ firstName }} {{ lastName}} message");
+
+    ResponseEntity<PouetError> response = handler.handlePouetException(
+      PouetException.builder(StandardMessage.USER_MANDATORY).argument("firstName", "Joe").argument("lastName", "Dalton").build()
+    );
+
+    assertThat(response.getBody().getMessage()).isEqualTo("User Joe Dalton message");
+  }
+
+  @Test
+  public void shouldHandleAsBadRequestForExceededSizeFileUpload() {
+    when(messages.getMessage("pouet.error.server.upload-too-big", null, Locale.FRANCE))
+      .thenReturn("The file is too big to be send to the server");
+
+    ResponseEntity<PouetError> response = handler.handleFileSizeException(new MaxUploadSizeExceededException(1024 * 1024 * 30));
+
+    assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
+    assertThat(response.getBody().getErrorType()).isEqualTo("server.upload-too-big");
+    assertThat(response.getBody().getMessage()).isEqualTo("The file is too big to be send to the server");
+  }
+
+  @Test
+  public void shouldHandleMethodArgumentTypeMismatchException() {
+    MethodArgumentTypeMismatchException mismatchException = new MethodArgumentTypeMismatchException(null, null, null, null, null);
+
+    ResponseEntity<PouetError> response = handler.handleMethodArgumentTypeMismatchException(mismatchException);
+
+    assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
+    assertThat(response.getBody().getErrorType()).isEqualTo("user.bad-request");
+  }
+
+  @Test
+  public void shouldHandleMethodArgumentTypeMismatchExceptionWithPouetError() {
+    RuntimeException cause = PouetException.builder(StandardMessage.USER_MANDATORY).build();
+    MethodArgumentTypeMismatchException mismatchException = new MethodArgumentTypeMismatchException(null, null, null, null, cause);
+
+    ResponseEntity<PouetError> response = handler.handleMethodArgumentTypeMismatchException(mismatchException);
+
+    assertThat(response.getBody().getErrorType()).isEqualTo("user.mandatory");
+  }
+
+  @Test
+  public void shouldLogUserErrorAsWarn() {
+    handler.handlePouetException(
+      PouetException.builder(StandardMessage.BAD_REQUEST).status(ErrorStatus.BAD_REQUEST).message("error message").build()
+    );
+
+    logs.assertLogged(Level.WARN, "error message");
+  }
+
+  @Test
+  public void shouldLogAuthenticationExceptionAsDebug() {
+    handler.handleAuthenticationException(new InsufficientAuthenticationException("oops"));
+
+    logs.assertLogged(Level.DEBUG, "oops");
+  }
+
+  @Test
+  public void shouldLogServerErrorAsError() {
+    handler.handlePouetException(
+      PouetException
+        .builder(StandardMessage.INTERNAL_SERVER_ERROR)
+        .status(ErrorStatus.INTERNAL_SERVER_ERROR)
+        .message("error message")
+        .build()
+    );
+
+    logs.assertLogged(Level.ERROR, "error message");
+  }
+
+  @Test
+  public void shouldLogErrorResponseBody() {
+    RestClientResponseException cause = new RestClientResponseException(
+      "error",
+      400,
+      "status",
+      null,
+      "service error response".getBytes(),
+      Charset.defaultCharset()
+    );
+
+    handler.handlePouetException(
+      PouetException
+        .builder(StandardMessage.INTERNAL_SERVER_ERROR)
+        .status(ErrorStatus.INTERNAL_SERVER_ERROR)
+        .message("error message")
+        .cause(cause)
+        .build()
+    );
+
+    logs.assertLogged(Level.ERROR, "error message");
+    logs.assertLogged(Level.ERROR, "service error response");
+  }
+}
diff --git a/src/test/java/com/ippon/pouet/common/infrastructure/primary/PouetErrorUnitTest.java b/src/test/java/com/ippon/pouet/common/infrastructure/primary/PouetErrorUnitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d7750dfd49fb2bd6511fbd4f4217cf68f4f3c81
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/infrastructure/primary/PouetErrorUnitTest.java
@@ -0,0 +1,39 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+class PouetErrorUnitTest {
+
+  @Test
+  public void shouldGetErrorInformation() {
+    PouetFieldError fieldError = PouetFieldErrorUnitTest.defaultFieldError();
+    PouetError error = defaultError(fieldError);
+
+    assertThat(error.getErrorType()).isEqualTo("type");
+    assertThat(error.getMessage()).isEqualTo("message");
+    assertThat(error.getFieldsErrors()).containsExactly(fieldError);
+  }
+
+  @Test
+  public void shouldSerializeToJson() {
+    assertThat(TestJson.writeAsString(defaultError(PouetFieldErrorUnitTest.defaultFieldError()))).isEqualTo(defaultJson());
+  }
+
+  @Test
+  void shouldDeserializeFromJson() {
+    assertThat(TestJson.readFromJson(defaultJson(), PouetError.class))
+      .usingRecursiveComparison()
+      .isEqualTo(defaultError(PouetFieldErrorUnitTest.defaultFieldError()));
+  }
+
+  private String defaultJson() {
+    return "{\"errorType\":\"type\",\"message\":\"message\",\"fieldsErrors\":[" + PouetFieldErrorUnitTest.defaultJson() + "]}";
+  }
+
+  private PouetError defaultError(PouetFieldError fieldError) {
+    return new PouetError("type", "message", List.of(fieldError));
+  }
+}
diff --git a/src/test/java/com/ippon/pouet/common/infrastructure/primary/PouetFieldErrorUnitTest.java b/src/test/java/com/ippon/pouet/common/infrastructure/primary/PouetFieldErrorUnitTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f195e4fcea7b1fa37f15e2cfa1932ae41c33d966
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/infrastructure/primary/PouetFieldErrorUnitTest.java
@@ -0,0 +1,30 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+class PouetFieldErrorUnitTest {
+
+  @Test
+  public void shouldGetFieldErrorInformation() {
+    PouetFieldError fieldError = defaultFieldError();
+
+    assertThat(fieldError.getFieldPath()).isEqualTo("path");
+    assertThat(fieldError.getReason()).isEqualTo("reason");
+    assertThat(fieldError.getMessage()).isEqualTo("message");
+  }
+
+  @Test
+  public void shouldSerializeToJson() {
+    assertThat(TestJson.writeAsString(defaultFieldError())).isEqualTo(defaultJson());
+  }
+
+  static PouetFieldError defaultFieldError() {
+    return PouetFieldError.builder().fieldPath("path").reason("reason").message("message").build();
+  }
+
+  static String defaultJson() {
+    return "{\"fieldPath\":\"path\",\"reason\":\"reason\",\"message\":\"message\"}";
+  }
+}
diff --git a/src/test/java/com/ippon/pouet/common/infrastructure/primary/TestJson.java b/src/test/java/com/ippon/pouet/common/infrastructure/primary/TestJson.java
new file mode 100644
index 0000000000000000000000000000000000000000..738004f4530ef6f86007a9df95bd0254c2bab322
--- /dev/null
+++ b/src/test/java/com/ippon/pouet/common/infrastructure/primary/TestJson.java
@@ -0,0 +1,45 @@
+package com.ippon.pouet.common.infrastructure.primary;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import java.io.IOException;
+
+/**
+ * Utility class to manage serialization and deserialization tests
+ */
+public final class TestJson {
+  private static final ObjectMapper jsonMapper = jsonMapper();
+
+  private TestJson() {}
+
+  public static ObjectMapper jsonMapper() {
+    return new ObjectMapper()
+      .setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
+      .registerModule(new JavaTimeModule())
+      .registerModule(new Jdk8Module())
+      .disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES)
+      .disable(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY)
+      .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+  }
+
+  public static <T> String writeAsString(T object) {
+    try {
+      return jsonMapper.writeValueAsString(object);
+    } catch (JsonProcessingException e) {
+      throw new AssertionError("Error serializing object: " + e.getMessage(), e);
+    }
+  }
+
+  public static <T> T readFromJson(String json, Class<T> clazz) {
+    try {
+      return jsonMapper.readValue(json, clazz);
+    } catch (IOException e) {
+      throw new AssertionError("Error reading value from json: " + e.getMessage(), e);
+    }
+  }
+}
diff --git a/src/test/java/com/ippon/pouet/service/MailServiceIT.java b/src/test/java/com/ippon/pouet/service/MailServiceIT.java
index 1201b7d97de96f871aed651f12ced288b1101854..ec06de1575cb367796bc41373a3b6aef24c33a4f 100644
--- a/src/test/java/com/ippon/pouet/service/MailServiceIT.java
+++ b/src/test/java/com/ippon/pouet/service/MailServiceIT.java
@@ -1,7 +1,7 @@
 package com.ippon.pouet.service;
 
 import static org.assertj.core.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
 import com.ippon.pouet.PouetApp;
@@ -9,15 +9,6 @@ import com.ippon.pouet.config.Constants;
 import com.ippon.pouet.domain.User;
 import io.github.jhipster.config.JHipsterProperties;
 import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.InputStreamReader;
-import java.net.URI;
-import java.net.URL;
-import java.nio.charset.Charset;
-import java.util.Properties;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 import javax.mail.Multipart;
 import javax.mail.internet.MimeBodyPart;
 import javax.mail.internet.MimeMessage;
@@ -40,14 +31,6 @@ import org.thymeleaf.spring5.SpringTemplateEngine;
  */
 @SpringBootTest(classes = PouetApp.class)
 public class MailServiceIT {
-  private static final String[] languages = {
-    "fr",
-    "en",
-    // jhipster-needle-i18n-language-constant - JHipster will add/remove languages in this array
-  };
-  private static final Pattern PATTERN_LOCALE_3 = Pattern.compile("([a-z]{2})-([a-zA-Z]{4})-([a-z]{2})");
-  private static final Pattern PATTERN_LOCALE_2 = Pattern.compile("([a-z]{2})-([a-z]{2})");
-
   @Autowired
   private JHipsterProperties jHipsterProperties;
 
@@ -132,22 +115,6 @@ public class MailServiceIT {
     assertThat(part.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8");
   }
 
-  @Test
-  public void testSendEmailFromTemplate() throws Exception {
-    User user = new User();
-    user.setLogin("john");
-    user.setEmail("john.doe@example.com");
-    user.setLangKey("en");
-    mailService.sendEmailFromTemplate(user, "mail/testEmail", "email.test.title");
-    verify(javaMailSender).send(messageCaptor.capture());
-    MimeMessage message = messageCaptor.getValue();
-    assertThat(message.getSubject()).isEqualTo("test title");
-    assertThat(message.getAllRecipients()[0].toString()).isEqualTo(user.getEmail());
-    assertThat(message.getFrom()[0].toString()).isEqualTo(jHipsterProperties.getMail().getFrom());
-    assertThat(message.getContent().toString()).isEqualToNormalizingNewlines("<html>test title, http://127.0.0.1:8080, john</html>\n");
-    assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8");
-  }
-
   @Test
   public void testSendActivationEmail() throws Exception {
     User user = new User();
@@ -202,44 +169,4 @@ public class MailServiceIT {
       fail("Exception shouldn't have been thrown");
     }
   }
-
-  @Test
-  public void testSendLocalizedEmailForAllSupportedLanguages() throws Exception {
-    User user = new User();
-    user.setLogin("john");
-    user.setEmail("john.doe@example.com");
-    for (String langKey : languages) {
-      user.setLangKey(langKey);
-      mailService.sendEmailFromTemplate(user, "mail/testEmail", "email.test.title");
-      verify(javaMailSender, atLeastOnce()).send(messageCaptor.capture());
-      MimeMessage message = messageCaptor.getValue();
-
-      String propertyFilePath = "i18n/messages_" + getJavaLocale(langKey) + ".properties";
-      URL resource = this.getClass().getClassLoader().getResource(propertyFilePath);
-      File file = new File(new URI(resource.getFile()).getPath());
-      Properties properties = new Properties();
-      properties.load(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")));
-
-      String emailTitle = (String) properties.get("email.test.title");
-      assertThat(message.getSubject()).isEqualTo(emailTitle);
-      assertThat(message.getContent().toString())
-        .isEqualToNormalizingNewlines("<html>" + emailTitle + ", http://127.0.0.1:8080, john</html>\n");
-    }
-  }
-
-  /**
-   * Convert a lang key to the Java locale.
-   */
-  private String getJavaLocale(String langKey) {
-    String javaLangKey = langKey;
-    Matcher matcher2 = PATTERN_LOCALE_2.matcher(langKey);
-    if (matcher2.matches()) {
-      javaLangKey = matcher2.group(1) + "_" + matcher2.group(2).toUpperCase();
-    }
-    Matcher matcher3 = PATTERN_LOCALE_3.matcher(langKey);
-    if (matcher3.matches()) {
-      javaLangKey = matcher3.group(1) + "_" + matcher3.group(2) + "_" + matcher3.group(3).toUpperCase();
-    }
-    return javaLangKey;
-  }
 }
diff --git a/src/test/java/com/ippon/pouet/web/rest/AccountResourceIT.java b/src/test/java/com/ippon/pouet/web/rest/AccountResourceIT.java
index c3967667f7d69508b1f1ce28fb88a3cec0a321ec..af6fdfa0e21543b5b13441909db8a9b11a8c0603 100644
--- a/src/test/java/com/ippon/pouet/web/rest/AccountResourceIT.java
+++ b/src/test/java/com/ippon/pouet/web/rest/AccountResourceIT.java
@@ -6,6 +6,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 
 import com.ippon.pouet.PouetApp;
+import com.ippon.pouet.common.infrastructure.primary.PouetErrorHandler;
 import com.ippon.pouet.config.Constants;
 import com.ippon.pouet.domain.User;
 import com.ippon.pouet.repository.AuthorityRepository;
@@ -19,6 +20,7 @@ import com.ippon.pouet.web.rest.vm.ManagedUserVM;
 import java.time.Instant;
 import java.util.*;
 import org.apache.commons.lang3.RandomStringUtils;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
@@ -27,6 +29,7 @@ import org.springframework.http.MediaType;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.transaction.annotation.Transactional;
 
 /**
@@ -44,6 +47,9 @@ public class AccountResourceIT {
   @Autowired
   private AuthorityRepository authorityRepository;
 
+  @Autowired
+  private PouetErrorHandler exceptionTranslator;
+
   @Autowired
   private UserService userService;
 
@@ -51,20 +57,24 @@ public class AccountResourceIT {
   private PasswordEncoder passwordEncoder;
 
   @Autowired
-  private MockMvc restAccountMockMvc;
+  private AccountResource accountResource;
+
+  private MockMvc mockMvc;
+
+  @BeforeEach
+  public void setup() {
+    mockMvc = MockMvcBuilders.standaloneSetup(accountResource).setControllerAdvice(exceptionTranslator).build();
+  }
 
   @Test
   @WithUnauthenticatedMockUser
   public void testNonAuthenticatedUser() throws Exception {
-    restAccountMockMvc
-      .perform(get("/api/authenticate").accept(MediaType.APPLICATION_JSON))
-      .andExpect(status().isOk())
-      .andExpect(content().string(""));
+    mockMvc.perform(get("/api/authenticate").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andExpect(content().string(""));
   }
 
   @Test
   public void testAuthenticatedUser() throws Exception {
-    restAccountMockMvc
+    mockMvc
       .perform(
         get("/api/authenticate")
           .with(
@@ -94,7 +104,7 @@ public class AccountResourceIT {
     user.setAuthorities(authorities);
     userService.createUser(user);
 
-    restAccountMockMvc
+    mockMvc
       .perform(get("/api/account").accept(MediaType.APPLICATION_JSON))
       .andExpect(status().isOk())
       .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
@@ -109,7 +119,7 @@ public class AccountResourceIT {
 
   @Test
   public void testGetUnknownAccount() throws Exception {
-    restAccountMockMvc.perform(get("/api/account").accept(MediaType.APPLICATION_PROBLEM_JSON)).andExpect(status().isInternalServerError());
+    mockMvc.perform(get("/api/account").accept(MediaType.APPLICATION_PROBLEM_JSON)).andExpect(status().isInternalServerError());
   }
 
   @Test
@@ -126,7 +136,7 @@ public class AccountResourceIT {
     validUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER));
     assertThat(userRepository.findOneByLogin("test-register-valid").isPresent()).isFalse();
 
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(validUser)))
       .andExpect(status().isCreated());
 
@@ -147,7 +157,7 @@ public class AccountResourceIT {
     invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE);
     invalidUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER));
 
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(invalidUser)))
       .andExpect(status().isBadRequest());
 
@@ -169,7 +179,7 @@ public class AccountResourceIT {
     invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE);
     invalidUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER));
 
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(invalidUser)))
       .andExpect(status().isBadRequest());
 
@@ -191,7 +201,7 @@ public class AccountResourceIT {
     invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE);
     invalidUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER));
 
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(invalidUser)))
       .andExpect(status().isBadRequest());
 
@@ -213,7 +223,7 @@ public class AccountResourceIT {
     invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE);
     invalidUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER));
 
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(invalidUser)))
       .andExpect(status().isBadRequest());
 
@@ -251,12 +261,12 @@ public class AccountResourceIT {
     secondUser.setAuthorities(new HashSet<>(firstUser.getAuthorities()));
 
     // First user
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(firstUser)))
       .andExpect(status().isCreated());
 
     // Second (non activated) user
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(secondUser)))
       .andExpect(status().isCreated());
 
@@ -266,7 +276,7 @@ public class AccountResourceIT {
     userRepository.save(testUser.get());
 
     // Second (already activated) user
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(secondUser)))
       .andExpect(status().is4xxClientError());
   }
@@ -286,7 +296,7 @@ public class AccountResourceIT {
     firstUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER));
 
     // Register first user
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(firstUser)))
       .andExpect(status().isCreated());
 
@@ -305,7 +315,7 @@ public class AccountResourceIT {
     secondUser.setAuthorities(new HashSet<>(firstUser.getAuthorities()));
 
     // Register second (non activated) user
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(secondUser)))
       .andExpect(status().isCreated());
 
@@ -328,7 +338,7 @@ public class AccountResourceIT {
     userWithUpperCaseEmail.setAuthorities(new HashSet<>(firstUser.getAuthorities()));
 
     // Register third (not activated) user
-    restAccountMockMvc
+    mockMvc
       .perform(
         post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(userWithUpperCaseEmail))
       )
@@ -342,7 +352,7 @@ public class AccountResourceIT {
     userService.updateUser((new UserDTO(testUser4.get())));
 
     // Register 4th (already activated) user
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(secondUser)))
       .andExpect(status().is4xxClientError());
   }
@@ -361,7 +371,7 @@ public class AccountResourceIT {
     validUser.setLangKey(Constants.DEFAULT_LANGUAGE);
     validUser.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN));
 
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(validUser)))
       .andExpect(status().isCreated());
 
@@ -383,7 +393,7 @@ public class AccountResourceIT {
 
     userRepository.saveAndFlush(user);
 
-    restAccountMockMvc.perform(get("/api/activate?key={activationKey}", activationKey)).andExpect(status().isOk());
+    mockMvc.perform(get("/api/activate?key={activationKey}", activationKey)).andExpect(status().isOk());
 
     user = userRepository.findOneByLogin(user.getLogin()).orElse(null);
     assertThat(user.getActivated()).isTrue();
@@ -392,7 +402,7 @@ public class AccountResourceIT {
   @Test
   @Transactional
   public void testActivateAccountWithWrongKey() throws Exception {
-    restAccountMockMvc.perform(get("/api/activate?key=wrongActivationKey")).andExpect(status().isInternalServerError());
+    mockMvc.perform(get("/api/activate?key=wrongActivationKey")).andExpect(status().isInternalServerError());
   }
 
   @Test
@@ -416,7 +426,7 @@ public class AccountResourceIT {
     userDTO.setLangKey(Constants.DEFAULT_LANGUAGE);
     userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN));
 
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/account").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(userDTO)))
       .andExpect(status().isOk());
 
@@ -453,7 +463,7 @@ public class AccountResourceIT {
     userDTO.setLangKey(Constants.DEFAULT_LANGUAGE);
     userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN));
 
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/account").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(userDTO)))
       .andExpect(status().isBadRequest());
 
@@ -489,7 +499,7 @@ public class AccountResourceIT {
     userDTO.setLangKey(Constants.DEFAULT_LANGUAGE);
     userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN));
 
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/account").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(userDTO)))
       .andExpect(status().isBadRequest());
 
@@ -518,7 +528,7 @@ public class AccountResourceIT {
     userDTO.setLangKey(Constants.DEFAULT_LANGUAGE);
     userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN));
 
-    restAccountMockMvc
+    mockMvc
       .perform(post("/api/account").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(userDTO)))
       .andExpect(status().isOk());
 
@@ -537,7 +547,7 @@ public class AccountResourceIT {
     user.setEmail("change-password-wrong-existing-password@example.com");
     userRepository.saveAndFlush(user);
 
-    restAccountMockMvc
+    mockMvc
       .perform(
         post("/api/account/change-password")
           .contentType(MediaType.APPLICATION_JSON)
@@ -561,7 +571,7 @@ public class AccountResourceIT {
     user.setEmail("change-password@example.com");
     userRepository.saveAndFlush(user);
 
-    restAccountMockMvc
+    mockMvc
       .perform(
         post("/api/account/change-password")
           .contentType(MediaType.APPLICATION_JSON)
@@ -586,7 +596,7 @@ public class AccountResourceIT {
 
     String newPassword = RandomStringUtils.random(ManagedUserVM.PASSWORD_MIN_LENGTH - 1);
 
-    restAccountMockMvc
+    mockMvc
       .perform(
         post("/api/account/change-password")
           .contentType(MediaType.APPLICATION_JSON)
@@ -611,7 +621,7 @@ public class AccountResourceIT {
 
     String newPassword = RandomStringUtils.random(ManagedUserVM.PASSWORD_MAX_LENGTH + 1);
 
-    restAccountMockMvc
+    mockMvc
       .perform(
         post("/api/account/change-password")
           .contentType(MediaType.APPLICATION_JSON)
@@ -634,7 +644,7 @@ public class AccountResourceIT {
     user.setEmail("change-password-empty@example.com");
     userRepository.saveAndFlush(user);
 
-    restAccountMockMvc
+    mockMvc
       .perform(
         post("/api/account/change-password")
           .contentType(MediaType.APPLICATION_JSON)
@@ -656,7 +666,7 @@ public class AccountResourceIT {
     user.setEmail("password-reset@example.com");
     userRepository.saveAndFlush(user);
 
-    restAccountMockMvc.perform(post("/api/account/reset-password/init").content("password-reset@example.com")).andExpect(status().isOk());
+    mockMvc.perform(post("/api/account/reset-password/init").content("password-reset@example.com")).andExpect(status().isOk());
   }
 
   @Test
@@ -669,16 +679,12 @@ public class AccountResourceIT {
     user.setEmail("password-reset-upper-case@example.com");
     userRepository.saveAndFlush(user);
 
-    restAccountMockMvc
-      .perform(post("/api/account/reset-password/init").content("password-reset-upper-case@EXAMPLE.COM"))
-      .andExpect(status().isOk());
+    mockMvc.perform(post("/api/account/reset-password/init").content("password-reset-upper-case@EXAMPLE.COM")).andExpect(status().isOk());
   }
 
   @Test
   public void testRequestPasswordResetWrongEmail() throws Exception {
-    restAccountMockMvc
-      .perform(post("/api/account/reset-password/init").content("password-reset-wrong-email@example.com"))
-      .andExpect(status().isOk());
+    mockMvc.perform(post("/api/account/reset-password/init").content("password-reset-wrong-email@example.com")).andExpect(status().isOk());
   }
 
   @Test
@@ -696,7 +702,7 @@ public class AccountResourceIT {
     keyAndPassword.setKey(user.getResetKey());
     keyAndPassword.setNewPassword("new password");
 
-    restAccountMockMvc
+    mockMvc
       .perform(
         post("/api/account/reset-password/finish")
           .contentType(MediaType.APPLICATION_JSON)
@@ -723,7 +729,7 @@ public class AccountResourceIT {
     keyAndPassword.setKey(user.getResetKey());
     keyAndPassword.setNewPassword("foo");
 
-    restAccountMockMvc
+    mockMvc
       .perform(
         post("/api/account/reset-password/finish")
           .contentType(MediaType.APPLICATION_JSON)
@@ -742,7 +748,7 @@ public class AccountResourceIT {
     keyAndPassword.setKey("wrong reset key");
     keyAndPassword.setNewPassword("new password");
 
-    restAccountMockMvc
+    mockMvc
       .perform(
         post("/api/account/reset-password/finish")
           .contentType(MediaType.APPLICATION_JSON)
diff --git a/src/test/java/com/ippon/pouet/web/rest/errors/ExceptionTranslatorIT.java b/src/test/java/com/ippon/pouet/web/rest/errors/ExceptionTranslatorIT.java
deleted file mode 100644
index 5ddd9f26857bb842c80789fe25b621687598b618..0000000000000000000000000000000000000000
--- a/src/test/java/com/ippon/pouet/web/rest/errors/ExceptionTranslatorIT.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package com.ippon.pouet.web.rest.errors;
-
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-
-import com.ippon.pouet.PouetApp;
-import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.http.MediaType;
-import org.springframework.security.test.context.support.WithMockUser;
-import org.springframework.test.web.servlet.MockMvc;
-
-/**
- * Integration tests {@link ExceptionTranslator} controller advice.
- */
-@WithMockUser
-@AutoConfigureMockMvc
-@SpringBootTest(classes = PouetApp.class)
-public class ExceptionTranslatorIT {
-  @Autowired
-  private MockMvc mockMvc;
-
-  @Test
-  public void testConcurrencyFailure() throws Exception {
-    mockMvc
-      .perform(get("/api/exception-translator-test/concurrency-failure"))
-      .andExpect(status().isConflict())
-      .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
-      .andExpect(jsonPath("$.message").value(ErrorConstants.ERR_CONCURRENCY_FAILURE));
-  }
-
-  @Test
-  public void testMethodArgumentNotValid() throws Exception {
-    mockMvc
-      .perform(post("/api/exception-translator-test/method-argument").content("{}").contentType(MediaType.APPLICATION_JSON))
-      .andExpect(status().isBadRequest())
-      .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
-      .andExpect(jsonPath("$.message").value(ErrorConstants.ERR_VALIDATION))
-      .andExpect(jsonPath("$.fieldErrors.[0].objectName").value("test"))
-      .andExpect(jsonPath("$.fieldErrors.[0].field").value("test"))
-      .andExpect(jsonPath("$.fieldErrors.[0].message").value("NotNull"));
-  }
-
-  @Test
-  public void testMissingServletRequestPartException() throws Exception {
-    mockMvc
-      .perform(get("/api/exception-translator-test/missing-servlet-request-part"))
-      .andExpect(status().isBadRequest())
-      .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
-      .andExpect(jsonPath("$.message").value("error.http.400"));
-  }
-
-  @Test
-  public void testMissingServletRequestParameterException() throws Exception {
-    mockMvc
-      .perform(get("/api/exception-translator-test/missing-servlet-request-parameter"))
-      .andExpect(status().isBadRequest())
-      .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
-      .andExpect(jsonPath("$.message").value("error.http.400"));
-  }
-
-  @Test
-  public void testAccessDenied() throws Exception {
-    mockMvc
-      .perform(get("/api/exception-translator-test/access-denied"))
-      .andExpect(status().isForbidden())
-      .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
-      .andExpect(jsonPath("$.message").value("error.http.403"))
-      .andExpect(jsonPath("$.detail").value("test access denied!"));
-  }
-
-  @Test
-  public void testUnauthorized() throws Exception {
-    mockMvc
-      .perform(get("/api/exception-translator-test/unauthorized"))
-      .andExpect(status().isUnauthorized())
-      .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
-      .andExpect(jsonPath("$.message").value("error.http.401"))
-      .andExpect(jsonPath("$.path").value("/api/exception-translator-test/unauthorized"))
-      .andExpect(jsonPath("$.detail").value("test authentication failed!"));
-  }
-
-  @Test
-  public void testMethodNotSupported() throws Exception {
-    mockMvc
-      .perform(post("/api/exception-translator-test/access-denied"))
-      .andExpect(status().isMethodNotAllowed())
-      .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
-      .andExpect(jsonPath("$.message").value("error.http.405"))
-      .andExpect(jsonPath("$.detail").value("Request method 'POST' not supported"));
-  }
-
-  @Test
-  public void testExceptionWithResponseStatus() throws Exception {
-    mockMvc
-      .perform(get("/api/exception-translator-test/response-status"))
-      .andExpect(status().isBadRequest())
-      .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
-      .andExpect(jsonPath("$.message").value("error.http.400"))
-      .andExpect(jsonPath("$.title").value("test response status"));
-  }
-
-  @Test
-  public void testInternalServerError() throws Exception {
-    mockMvc
-      .perform(get("/api/exception-translator-test/internal-server-error"))
-      .andExpect(status().isInternalServerError())
-      .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON))
-      .andExpect(jsonPath("$.message").value("error.http.500"))
-      .andExpect(jsonPath("$.title").value("Internal Server Error"));
-  }
-}
diff --git a/src/test/java/com/ippon/pouet/web/rest/errors/ExceptionTranslatorTestController.java b/src/test/java/com/ippon/pouet/web/rest/errors/ExceptionTranslatorTestController.java
deleted file mode 100644
index ef025802bebaf5f777026d5c4163fa7bbbdee88d..0000000000000000000000000000000000000000
--- a/src/test/java/com/ippon/pouet/web/rest/errors/ExceptionTranslatorTestController.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.ippon.pouet.web.rest.errors;
-
-import javax.validation.Valid;
-import javax.validation.constraints.NotNull;
-import org.springframework.dao.ConcurrencyFailureException;
-import org.springframework.http.HttpStatus;
-import org.springframework.security.access.AccessDeniedException;
-import org.springframework.security.authentication.BadCredentialsException;
-import org.springframework.web.bind.annotation.*;
-
-@RestController
-@RequestMapping("/api/exception-translator-test")
-public class ExceptionTranslatorTestController {
-
-  @GetMapping("/concurrency-failure")
-  public void concurrencyFailure() {
-    throw new ConcurrencyFailureException("test concurrency failure");
-  }
-
-  @PostMapping("/method-argument")
-  public void methodArgument(@Valid @RequestBody TestDTO testDTO) {}
-
-  @GetMapping("/missing-servlet-request-part")
-  public void missingServletRequestPartException(@RequestPart String part) {}
-
-  @GetMapping("/missing-servlet-request-parameter")
-  public void missingServletRequestParameterException(@RequestParam String param) {}
-
-  @GetMapping("/access-denied")
-  public void accessdenied() {
-    throw new AccessDeniedException("test access denied!");
-  }
-
-  @GetMapping("/unauthorized")
-  public void unauthorized() {
-    throw new BadCredentialsException("test authentication failed!");
-  }
-
-  @GetMapping("/response-status")
-  public void exceptionWithResponseStatus() {
-    throw new TestResponseStatusException();
-  }
-
-  @GetMapping("/internal-server-error")
-  public void internalServerError() {
-    throw new RuntimeException();
-  }
-
-  public static class TestDTO {
-    @NotNull
-    private String test;
-
-    public String getTest() {
-      return test;
-    }
-
-    public void setTest(String test) {
-      this.test = test;
-    }
-  }
-
-  @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "test response status")
-  @SuppressWarnings("serial")
-  public static class TestResponseStatusException extends RuntimeException {}
-}
diff --git a/src/test/resources/i18n/messages_en.properties b/src/test/resources/i18n/messages_en.properties
deleted file mode 100644
index b283f637dc0758b2b1250de19c007aaed20bbab3..0000000000000000000000000000000000000000
--- a/src/test/resources/i18n/messages_en.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-email.test.title=test title
-# Value used for English locale unit test in MailServiceIT
-# as this file is loaded instead of real file
-email.activation.title=pouet account activation
diff --git a/src/test/resources/i18n/messages_fr.properties b/src/test/resources/i18n/messages_fr.properties
deleted file mode 100644
index 9653e56f6990478e7f4cdf12a991c48bbf2c2117..0000000000000000000000000000000000000000
--- a/src/test/resources/i18n/messages_fr.properties
+++ /dev/null
@@ -1 +0,0 @@
-email.test.title=Activation de votre compte pouet