-
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?
Changes from all commits
b606b1a
4b3a1c1
d4d0770
f585c5c
73997eb
a683b8d
075d8f5
ec89168
4508118
07b96a1
84fcb31
1a091fa
170d04a
66412bd
a8103be
d0d46b4
28ab016
6028ec9
58c298f
0bee5cc
238ceb7
2a483e1
f2a0a56
7d53e5c
5cd75d6
3cd8f4c
c77f154
7587a79
cf9795a
8367304
a7e75e4
16543b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -141,6 +141,7 @@ void constraintViolationMissingRequiredQueryParameter() { | |
.get("/beanValidation/queryParameter").then().assertThat() | ||
.statusCode(400) | ||
.body("type", equalTo("urn:problem-type:belgif:badRequest")) | ||
.body("detail", equalTo("The input message is incorrect")) | ||
.body("issues[0].type", equalTo("urn:problem-type:belgif:input-validation:schemaViolation")) | ||
.body("issues[0].title", equalTo("Input value is invalid with respect to the schema")) | ||
.body("issues[0].in", equalTo("query")) | ||
|
@@ -338,4 +339,90 @@ void constraintViolationBodyInheritance() { | |
.body("issues[1].detail", equalTo("must not be blank")); | ||
} | ||
|
||
@Test | ||
void i18n() { | ||
getSpec().when() | ||
.header("Accept-Language", "nl-BE") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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. |
||
.queryParam("param", -1) | ||
.queryParam("other", "TOO_LONG") | ||
.get("/beanValidation/queryParameter").then().assertThat() | ||
.statusCode(400) | ||
.body("type", equalTo("urn:problem-type:belgif:badRequest")) | ||
.body("detail", equalTo("Het input bericht is ongeldig")) | ||
.body("issues[0].type", equalTo("urn:problem-type:belgif:input-validation:schemaViolation")) | ||
.body("issues[0].title", equalTo("Input value is invalid with respect to the schema")) | ||
.body("issues[0].detail", equalTo("grootte moet tussen 0 en 5 liggen")) | ||
.body("issues[0].in", equalTo("query")) | ||
.body("issues[0].name", equalTo("other")) | ||
.body("issues[0].value", equalTo("TOO_LONG")) | ||
.body("issues[1].type", equalTo("urn:problem-type:belgif:input-validation:schemaViolation")) | ||
.body("issues[1].title", equalTo("Input value is invalid with respect to the schema")) | ||
.body("issues[1].detail", equalTo("moet groter dan 0 zijn")) | ||
.body("issues[1].in", equalTo("query")) | ||
.body("issues[1].name", equalTo("param")) | ||
.body("issues[1].value", equalTo(-1)); | ||
} | ||
|
||
@Test | ||
void i18nUnsupportedLanguage() { | ||
getSpec().when() | ||
.header("Accept-Language", "es") | ||
.queryParam("param", -1) | ||
.queryParam("other", "TOO_LONG") | ||
.get("/beanValidation/queryParameter").then().assertThat() | ||
.statusCode(400) | ||
.body("detail", equalTo("The input message is incorrect")); | ||
} | ||
|
||
@Test | ||
void i18nWeighted() { | ||
getSpec().when() | ||
.header("Accept-Language", "fr-BE;q=0.5, nl-BE;q=0.7") | ||
.queryParam("param", -1) | ||
.queryParam("other", "TOO_LONG") | ||
.get("/beanValidation/queryParameter").then().assertThat() | ||
.statusCode(400) | ||
.body("type", equalTo("urn:problem-type:belgif:badRequest")) | ||
.body("detail", equalTo("Het input bericht is ongeldig")) | ||
.body("issues[0].detail", equalTo("grootte moet tussen 0 en 5 liggen")); | ||
} | ||
|
||
@Test | ||
void i18nUnsupportedLanguageWeighted() { | ||
getSpec().when() | ||
.header("Accept-Language", "fr-BE;q=0.5, es;q=0.7") | ||
.queryParam("param", -1) | ||
.queryParam("other", "TOO_LONG") | ||
.get("/beanValidation/queryParameter").then().assertThat() | ||
.statusCode(400) | ||
.body("detail", equalTo("The input message is incorrect")); | ||
} | ||
|
||
@Test | ||
void i18nCustom() { | ||
getSpec().when() | ||
.header("Accept-Language", "nl-BE") | ||
.get("/custom").then().assertThat() | ||
.statusCode(409) | ||
.body("type", equalTo("urn:problem-type:acme:custom")) | ||
.body("customField", equalTo("value from frontend")) | ||
.body("detail", equalTo("NL detail")); | ||
} | ||
|
||
@Test | ||
void i18nDisabled() { | ||
try { | ||
getSpec().queryParam("enabled", "false").post("/i18n"); | ||
getSpec().when() | ||
.header("Accept-Language", "nl-BE") | ||
.queryParam("param", -1) | ||
.queryParam("other", "TOO_LONG") | ||
.get("/beanValidation/queryParameter").then().assertThat() | ||
.statusCode(400) | ||
.body("detail", equalTo("The input message is incorrect")); | ||
} finally { | ||
getSpec().queryParam("enabled", "true").post("/i18n"); | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
CustomProblem.detail=Custom detail |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
CustomProblem.detail=DE detail |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
CustomProblem.detail=FR detail |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
CustomProblem.detail=NL detail |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
quarkus.http.root-path=/quarkus | ||
quarkus.locales=en,fr-BE,nl-BE,de-BE | ||
quarkus.rest-client.backend.uri=http://localhost:${quarkus.http.port}/quarkus | ||
%test.quarkus.rest-client.backend.uri=http://localhost:${quarkus.http.test-port}/quarkus |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package io.github.belgif.rest.problem.jaxrs.i18n; | ||
|
||
import javax.ws.rs.container.ContainerRequestContext; | ||
import javax.ws.rs.container.ContainerRequestFilter; | ||
import javax.ws.rs.container.ContainerResponseContext; | ||
import javax.ws.rs.container.ContainerResponseFilter; | ||
import javax.ws.rs.container.PreMatching; | ||
import javax.ws.rs.ext.Provider; | ||
|
||
import io.github.belgif.rest.problem.i18n.I18N; | ||
|
||
/** | ||
* Filter that registers the requested locale, as specified in Accept-Language HTTP header, | ||
* with the {@link ThreadLocalLocaleResolver} (and clears it afterward). | ||
*/ | ||
@PreMatching | ||
@Provider | ||
public class I18NAcceptLanguageFilter implements ContainerRequestFilter, ContainerResponseFilter { | ||
|
||
@Override | ||
public void filter(ContainerRequestContext requestContext) { | ||
if (I18N.isEnabled() && !requestContext.getAcceptableLanguages().isEmpty()) { | ||
ThreadLocalLocaleResolver.setLocale(requestContext.getAcceptableLanguages().get(0)); | ||
} | ||
} | ||
|
||
@Override | ||
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) { | ||
if (I18N.isEnabled()) { | ||
ThreadLocalLocaleResolver.clear(); | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package io.github.belgif.rest.problem.jaxrs.i18n; | ||
|
||
import static io.github.belgif.rest.problem.i18n.I18N.*; | ||
|
||
import javax.servlet.ServletContextEvent; | ||
import javax.servlet.ServletContextListener; | ||
import javax.servlet.annotation.WebListener; | ||
|
||
import io.github.belgif.rest.problem.i18n.I18N; | ||
|
||
/** | ||
* ServletContextListener that enables/disables I18N based on the "io.github.belgif.rest.problem.i18n" | ||
* parameter, which is resolved from following configuration locations in order: | ||
* <ol> | ||
* <li>System property</li> | ||
* <li>Environment variable</li> | ||
* <li>web.xml context param</li> | ||
* </ol> | ||
*/ | ||
@WebListener | ||
public class I18NConfigurator implements ServletContextListener { | ||
|
||
@Override | ||
public void contextInitialized(ServletContextEvent sce) { | ||
if (System.getProperty(I18N_FLAG) != null) { | ||
I18N.setEnabled(Boolean.parseBoolean(System.getProperty(I18N_FLAG))); | ||
} else if (System.getenv(I18N_FLAG) != null) { | ||
I18N.setEnabled(Boolean.parseBoolean(System.getenv(I18N_FLAG))); | ||
} else if (sce.getServletContext().getInitParameter(I18N_FLAG) != null) { | ||
I18N.setEnabled(Boolean.parseBoolean(sce.getServletContext().getInitParameter(I18N_FLAG))); | ||
} | ||
} | ||
|
||
} |
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).