-
Notifications
You must be signed in to change notification settings - Fork 0
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
Add internationalization support #133
base: main
Are you sure you want to change the base?
Conversation
@pvdbosch I have some questions related to this PR:
|
Hi @jpraet , I'd abstract away resolving the locale, including setting response Content-Language or Vary headers. Some framework support may be used:
If an API supports an explicit way of specifying the language (e.g.
All languages in the list should be considered (with I agree to continue with a default language when no match found:
|
For Spring Boot I use RequestContextUtils.getLocale() which should delegate to the configured LocaleResolver. Maybe the Belgif REST Guide should be adapted, it currently says "In case the server could not honor any of the requested languages, it SHOULD return a 406 Not Acceptable error." |
For Spring, could LocaleContextHolder be used instead? From RequestContextUtils javadoc:
It should also work outside a servlet environment, and wouldn't require registering a filter or syncing with a threadlocal variable. I believe resolving and setting the locale shouldn't be part of the rest-problem-java library so APIs could choose their own logic e.g. based on the authenticated user's language preference. Perhaps in a separate library if the framework doesn't provide an abstraction. |
Agreed, or recommend instead to continue with a supported language. Created belgif/rest-guide#205 |
I will look into it. Something like a pluggable LocaleResolver then, configurable via |
Yes, rest-problem-java should only depend on "consulting the current locale", but not on any logic that deduces it from request or user context. These aspects should probably be best in a separate I18N module for JEE; because it's not really logical to search for such functionality in a rest-problem module. Maybe we should consider having a belgif-rest-java-sdk as root project that bundles modules... |
Current implementation has pluggable LocaleResolver, which on Spring Boot delegates to LocaleContextHolder. belgif-rest-java-sdk would be in a separate repository? |
belgif-rest-problem/src/main/resources/io/github/belgif/rest/problem/Messages_fr.properties
Show resolved
Hide resolved
Ok, seems like hibernate wrote their own "LocaleResolver" SPI, which integrates with RestEasy.
Too bad that this isn't standardized in JEE, so we could reuse it. If the locale resolver is overridable by JEE applications, it's probably sufficient then not to warrant another module.
I was thinking about a repo to group all possible belgif rest java modules ( incl rest-problem-java, i18n, possible future validation lib, ...). But if internationalization can be done without an additional module, probably not needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't review the NL and DE translations :-)
Peter talks about a separate module of -problem , are you planning to more everything afterwards
belgif-rest-problem/src/main/resources/io/github/belgif/rest/problem/Messages_fr.properties
Outdated
Show resolved
Hide resolved
belgif-rest-problem/src/main/resources/io/github/belgif/rest/problem/Messages_fr.properties
Outdated
Show resolved
Hide resolved
belgif-rest-problem/src/main/resources/io/github/belgif/rest/problem/Messages_fr.properties
Outdated
Show resolved
Hide resolved
belgif-rest-problem/src/main/resources/io/github/belgif/rest/problem/Messages_fr.properties
Outdated
Show resolved
Hide resolved
belgif-rest-problem/src/main/resources/io/github/belgif/rest/problem/Messages_fr.properties
Outdated
Show resolved
Hide resolved
belgif-rest-problem/src/main/resources/io/github/belgif/rest/problem/Messages_fr.properties
Outdated
Show resolved
Hide resolved
belgif-rest-problem/src/main/resources/io/github/belgif/rest/problem/Messages_fr.properties
Outdated
Show resolved
Hide resolved
...java-ee/src/main/java/io/github/belgif/rest/problem/jaxrs/i18n/I18NAcceptLanguageFilter.java
Outdated
Show resolved
Hide resolved
...-ee/src/test/java/io/github/belgif/rest/problem/jaxrs/i18n/I18NAcceptLanguageFilterTest.java
Show resolved
Hide resolved
...lem-spring/src/test/java/io/github/belgif/rest/problem/spring/i18n/I18NConfiguratorTest.java
Show resolved
Hide resolved
Not planned at this point. |
belgif-rest-problem/src/main/resources/io/github/belgif/rest/problem/Messages_fr.properties
Outdated
Show resolved
Hide resolved
There's a failure on quarkus:
|
[[i18n]] | ||
== Internationalization (I18N) | ||
|
||
The detail messages of Belgif problem and issue types can be localized to the language requested in the https://www.belgif.be/specification/rest/api-guide/#i18n-negotiation[Accept-Language HTTP request header]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a LocaleResolver
interface that determines the language to use, clients could in theory provide a custom implementation that has nothing to do with the header (unlikely though). Shouldn't we describe or at least mention the locale resolver logic as we provide default implementations for Spring and Java EE?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some documentation on LocaleResolver.
belgif-rest-problem/src/main/java/io/github/belgif/rest/problem/i18n/I18N.java
Outdated
Show resolved
Hide resolved
...java-ee/src/main/java/io/github/belgif/rest/problem/jaxrs/i18n/I18NAcceptLanguageFilter.java
Outdated
Show resolved
Hide resolved
@Test | ||
void i18n() { | ||
getSpec().when() | ||
.header("Accept-Language", "nl-BE") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be interesting to add an additional test that verifies behavior with multiple values that use weighting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test added. Note that we just take the one with the highest weight though, and if that one is not supported, it will fallback to English. So with Accept-Language = "fr-BE;q=0.5, es;q=0.7" the resulting language will be English, not French.
@@ -338,4 +339,39 @@ void constraintViolationBodyInheritance() { | |||
.body("issues[1].detail", equalTo("must not be blank")); | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We never test localization can be disabled, would it be easy to add a test for that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we could expose an endpoint that calls I18N.setEnabled(false).
...-ee/src/test/java/io/github/belgif/rest/problem/jaxrs/i18n/I18NAcceptLanguageFilterTest.java
Show resolved
Hide resolved
* Checks whether I18N should be enabled, based on system property / environment variable. | ||
*/ | ||
static void init() { | ||
if (System.getProperty(I18N_FLAG) != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this code Java EE specific? For Spring we always let Spring look for the property in all its locations through @ConfigurationProperties
and set its state here with I18NConfigurator
.
The only scenario where this method is relevant is for a Java EE application where it wasn't provided as a servlet context parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to java ee I18NConfigurator.
@@ -67,7 +69,7 @@ public static InputValidationIssue schemaViolation(InEnum in, String name, Objec | |||
|
|||
public static InputValidationIssue unknownInput(InEnum in, String name, Object value) { | |||
return new InputValidationIssue(ISSUE_TYPE_UNKNOWN_INPUT, "Unknown input") | |||
.detail(String.format("Input %s is unknown", name)) | |||
.detail(I18N.getLocalizedString("unknownInput.detail", name)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be worth it to add a localizedDetail
method, similar to I18N.getLocalizedDetail(...)
? Would make the code less verbose + remove duplication of ".detail" part of the bundle key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a risk we ever forget to keep the files in sync? Shouldn't happen with a good review though, but was thinking about adding a test that verifies all messages are defined in all languages.
If we ever forget an entry it would result in a MissingResourceException
and cause the request to fail, but risk seems limited.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a check for missing keys.
(ServletContext injection for JAX-RS provider does not work on Quarkus)
…est-problem-java-ee
Quality Gate passedIssues Measures |
Fixes #124.