Skip to content

Feature Request: Support for Localized Message Handling in API Responses #33962

Closed as not planned
@aalamu

Description

@aalamu

Description:

I would like to propose a new feature to enhance Spring’s support for localized message handling in API responses. This feature could provide a utility or service that dynamically resolves localized messages based on message codes associated with API response objects. This functionality would streamline the process of internationalization for developers building REST APIs or web services with Spring Boot.

Proposed Solution:

The core idea of this feature is a utility class that resolves messages dynamically based on the current Locale, similar to the code below. The utility could work by:

  • Fetching message keys from a standard MessageSource.
  • Applying message parameters where needed.
  • Dynamically associating localized messages with API responses.
@Component
public class LocalizedResponse {

  private final MessageSource messageSource;

  public LocalizedResponse(final MessageSource messageSource) {
    this.messageSource = messageSource;
  }

  public String getMessage(final String key, final Locale locale, final Object... params) {
    return messageSource.getMessage(key, params, locale);
  }

  public String getMessage(final String key, final Object... params) {
    return getMessage(key, LocaleContextHolder.getLocale(), params);
  }

  public <T extends ApiResponse> T of(final T response) {
    if (response != null && response.getMessageCode() != null) {
      String message = getMessage(response.getMessageCode());
      response.setMessage(message);
      return response;
    }
    return null;
  }

  public <T extends ApiResponse> T of(final T response, final String messageCode) {
    if (response != null && messageCode != null) {
      String message = getMessage(messageCode, response.getParams());
      response.setMessage(message);
      return response;
    }
    return null;
  }

  public <T extends ApiResponse> Supplier<T> of(final Supplier<T> responseSupplier) {
    return () -> {
      T response = responseSupplier.get();
      if (response != null && response.getMessageCode() != null) {
        String message = getMessage(response.getMessageCode());
        response.setMessage(message);
      }
      return response;
    };
  }
}

Example Use Cases:

  • Sign-in and sign-up response messages can be localized based on user preferences.
  • Messages for country search results, both empty and non-empty, can be dynamically resolved.
return localizedResponse.of(ResendMfaVerificationCodeResponse.of());

return localizedResponse.of(signInResponse, signInResponse.getMfaMessageCode());

return localizedResponse.of(CountrySearchResult.of(toSearchResult(views, page)));

return localizedResponse.of(EmptyCountrySearchResult.of(toSearchResult(List.of(), page)));

Context & Usage

  public BlockUserStatusResponse blockOrUnblockUser() {
    // Returned the localized response of the blocked user
    return localizedResponse.of(BlockUserStatusResponse.of(BlockStatus.BLOCKED));
  }

ApiResponse Base Class:

To complement this utility, I propose a base ApiResponse class that API response objects can extend, ensuring each response object contains a message code for localization.

public abstract class ApiResponse {

  @JsonIgnore
  abstract public String getMessageCode();

  @JsonIgnore
  public Object[] getParams() {
    return new Object[] {};
  }

  @JsonProperty("message")
  protected String message;
}

Extending classes

public class ResendMfaVerificationCodeResponse extends ApiResponse {

  @Override
  public String getMessageCode() {
    return "resend.mfa.verification.code";
  }

  public static ResendMfaVerificationCodeResponse of() {
    return ResendMfaVerificationCodeResponse.builder()
        .build();
  }
}

public class BlockUserStatusResponse extends ApiResponse {

  private BlockStatus blockStatus;

  @Override
  public String getMessageCode() {
    return BlockStatus.isBlocked(blockStatus)
      ? "block.user.status.blocked"
      : "block.user.status.unblocked";
  }

  @JsonIgnore
  public Object[] getParams() {
    return new Object[] { blockStatus };
  }

  public static BlockUserStatusResponse of(final BlockStatus blockStatus) {
    return BlockUserStatusResponse.builder()
        .blockStatus(blockStatus)
        .build();
  }
}

Advantages:

  • Flexibility: Developers can easily define message codes for any API response and handle localization with minimal code changes.
  • Consistency: This approach standardizes how localized messages are handled across different parts of a web service.
  • Customizability: Developers can define their own ApiResponse types with custom message codes.

Conclusion:

This feature could greatly improve the developer experience by providing a robust and reusable solution for handling localized messages in API responses, reducing the overhead of managing message resolution in every response handler.

Related Issues:

Please let me know if any similar issues or requests have been made in the past. This feature could also tie into Spring’s existing support for internationalization and message resolution.

Metadata

Metadata

Assignees

No one assigned

    Labels

    in: webIssues in web modules (web, webmvc, webflux, websocket)status: declinedA suggestion or change that we don't feel we should currently apply

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions