Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
aalamu opened this issue Nov 26, 2024 · 1 comment
Closed
Labels
for: external-project For an external project and not something we can fix status: duplicate A duplicate of another issue

Comments

@aalamu
Copy link

aalamu commented Nov 26, 2024

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.

@bclozel
Copy link
Member

bclozel commented Nov 26, 2024

Duplicates spring-projects/spring-framework#33962

@bclozel bclozel closed this as not planned Won't fix, can't repro, duplicate, stale Nov 26, 2024
@bclozel bclozel added status: duplicate A duplicate of another issue for: external-project For an external project and not something we can fix labels Nov 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: external-project For an external project and not something we can fix status: duplicate A duplicate of another issue
Projects
None yet
Development

No branches or pull requests

2 participants