error-handling-spring-boot-starter icon indicating copy to clipboard operation
error-handling-spring-boot-starter copied to clipboard

Internationalization of error codes/messages

Open vishalrs opened this issue 1 year ago • 3 comments

Hi,

I am currently evaluating this framework and I am kind of wondering if there is a possibility to achieve internalization of codes/messages. The framework I think expects the custom codes and messages to be part of application.properties. How message bundles can be utilised to achieve internalization in this scenario. If you can post some examples that would be helpful.

Best Regards

vishalrs avatar Nov 29 '23 07:11 vishalrs

There is indeed no support for i18n in this library. This is kind of intentional. The goal is that the client would use the code value to display an appropriate message to the user and the i18n would be done on the client side. The message is intentionally in English so any developer that receives this, or sees it in a log file can understand it, no matter what language the actual user of the system is using.

wimdeblauwe avatar Nov 29 '23 19:11 wimdeblauwe

As a workaround, you could define a bean in your application of type ErrorMessageMapper:

@Bean
public ErrorMessageMapper errorMessageMapper(ErrorHandlingProperties properties, MessageSource messageSource) {
  return new MyErrorMessageMapper(properties,messageSource);
}

With MyErrorMessageMapper defined as:

public class MyErrorMessageMapper extends ErrorMessageMapper {
  private final MessageSource messageSource;

  public MyErrorMessageMapper(ErrorHandlingProperties properties, MessageSource messageSource) {
    super(properties);
    this.messageSource = messageSource;
  }

  // override all public methods here and use `messageSource` to get the translations from `messages.properties` and the other `messages_xx.properties` files that have the translations.

}

I did not test it, but normally that should work.

wimdeblauwe avatar Nov 29 '23 19:11 wimdeblauwe

Another alternative is to use a custom superclass for all your exceptions.

For example:

public abstract class TranslatableRuntimeException extends RuntimeException {

  @ResponseErrorProperty
  private TranslatedItem translatedItem;

  public TranslatableRuntimeException(String message, TranslatedItem translatedItem) {
  }

   public record TranslatedItem(
      String key,
      @Nullable String... parameters
  ) {

  }
}

This would add translatedItem into the JSON response. We can now remove that translatedItem from the JSON and replace it with translatedMessage that has the actual translated text based on the translatedItem by writing a custom ApiErrorResponseCustomizer:

@Configuration
public class ErrorHandlingConfiguration {
  @Bean
  public ApiErrorResponseCustomizer translatableRuntimeExceptionCustomizer(MessageSource messageSource) {
    return new ApiErrorResponseCustomizer() {
      @Override
      public void customize(ApiErrorResponse response) {
        TranslatedItem item = (TranslatedItem)response.getProperties().get("translatedItem");
        if(item != null) {
          response.getProperties().remove("translatedItem");
          resonse.addErrorProperty("translatedMessage", messageSource.getMessage(item.key(), item.parameters(), LocaleContextHolder.getLocale());
        }
      }
    }
  }
}

wimdeblauwe avatar Apr 24 '24 14:04 wimdeblauwe