Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.

Commit df68570

Browse files
committed
Add default/request locale
1 parent dc068d0 commit df68570

7 files changed

Lines changed: 209 additions & 6 deletions

File tree

jooby-core/src/main/java/jooby/Jooby.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.nio.charset.Charset;
88
import java.util.LinkedHashSet;
99
import java.util.List;
10+
import java.util.Locale;
1011
import java.util.Map.Entry;
1112
import java.util.Optional;
1213
import java.util.Set;
@@ -700,6 +701,9 @@ public void start() throws Exception {
700701

701702
final Charset charset = Charset.forName(config.getString("application.charset"));
702703

704+
String[] lang = config.getString("application.lang").split("_");
705+
final Locale locale = lang.length ==1 ? new Locale(lang[0]) : new Locale(lang[0], lang[1]);
706+
703707
// dependency injection
704708
injector = Guice.createInjector(new com.google.inject.Module() {
705709
@Override
@@ -717,6 +721,9 @@ public void configure(final Binder binder) {
717721
// bind charset
718722
binder.bind(Charset.class).toInstance(charset);
719723

724+
// bind locale
725+
binder.bind(Locale.class).toInstance(locale);
726+
720727
// bind readers & writers
721728
Multibinder<BodyConverter> converters = Multibinder
722729
.newSetBinder(binder, BodyConverter.class);
@@ -823,6 +830,12 @@ private Config buildConfig(final Optional<Config> appConfig) {
823830
config = config.withValue("application.charset",
824831
ConfigValueFactory.fromAnyRef(Charset.defaultCharset().name()));
825832
}
833+
// locale
834+
if (!config.hasPath("application.lang")) {
835+
Locale locale = Locale.getDefault();
836+
config = config.withValue("application.lang",
837+
ConfigValueFactory.fromAnyRef(locale.getLanguage() + "_" + locale.getCountry()));
838+
}
826839

827840
// set module config
828841
for (Jooby.Module module : ImmutableList.copyOf(modules).reverse()) {

jooby-core/src/main/java/jooby/Request.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import java.nio.charset.Charset;
66
import java.util.List;
7+
import java.util.Locale;
78
import java.util.Map;
89
import java.util.Optional;
910

@@ -135,6 +136,11 @@ public Charset charset() {
135136
return request.charset();
136137
}
137138

139+
@Override
140+
public Locale locale() {
141+
return request.locale();
142+
}
143+
138144
@Override
139145
public String ip() {
140146
return request.ip();
@@ -427,6 +433,9 @@ default <T> T getInstance(@Nonnull final TypeLiteral<T> type) {
427433
@Nonnull
428434
Charset charset();
429435

436+
@Nonnull
437+
Locale locale();
438+
430439
String ip();
431440

432441
Route route();

jooby-core/src/main/java/jooby/internal/RequestImpl.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.LinkedHashSet;
1212
import java.util.LinkedList;
1313
import java.util.List;
14+
import java.util.Locale;
1415
import java.util.Map;
1516
import java.util.Optional;
1617
import java.util.Set;
@@ -21,10 +22,10 @@
2122

2223
import jooby.BodyConverter;
2324
import jooby.Cookie;
24-
import jooby.MediaTypeProvider;
2525
import jooby.HttpException;
2626
import jooby.HttpStatus;
2727
import jooby.MediaType;
28+
import jooby.MediaTypeProvider;
2829
import jooby.Request;
2930
import jooby.Route;
3031
import jooby.SetCookie;
@@ -47,6 +48,8 @@ public class RequestImpl implements Request {
4748

4849
private Charset charset;
4950

51+
private Locale locale;
52+
5053
private List<MediaType> accept;
5154

5255
private MediaType type;
@@ -61,16 +64,18 @@ public RequestImpl(
6164
final Injector injector,
6265
final Route route,
6366
final BodyConverterSelector selector,
64-
final Charset charset,
6567
final MediaType contentType,
66-
final List<MediaType> accept) {
68+
final List<MediaType> accept,
69+
final Charset charset,
70+
final Locale locale) {
6771
this.injector = requireNonNull(injector, "An injector is required.");
6872
this.request = requireNonNull(request, "The request is required.");
6973
this.route = requireNonNull(route, "A route is required.");
7074
this.selector = requireNonNull(selector, "A message converter selector is required.");
71-
this.charset = requireNonNull(charset, "A charset is required.");
7275
this.type = requireNonNull(contentType, "A contentType is required.");
7376
this.accept = requireNonNull(accept, "An accept is required.");
77+
this.charset = requireNonNull(charset, "A charset is required.");
78+
this.locale = requireNonNull(locale, "A locale is required.");
7479
}
7580

7681
@Override
@@ -227,6 +232,11 @@ public Charset charset() {
227232
return charset;
228233
}
229234

235+
@Override
236+
public Locale locale() {
237+
return locale;
238+
}
239+
230240
@Override
231241
public Route route() {
232242
return route;

jooby-core/src/main/java/jooby/internal/RouteHandler.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.LinkedHashMap;
1313
import java.util.LinkedList;
1414
import java.util.List;
15+
import java.util.Locale;
1516
import java.util.Map;
1617
import java.util.Map.Entry;
1718
import java.util.NoSuchElementException;
@@ -56,6 +57,8 @@ public class RouteHandler {
5657

5758
private Charset charset;
5859

60+
private Locale locale;
61+
5962
private Injector rootInjector;
6063

6164
private Set<Request.Module> modules;
@@ -67,12 +70,14 @@ public RouteHandler(final Injector injector,
6770
final BodyConverterSelector selector,
6871
final Set<Request.Module> modules,
6972
final Set<Route.Definition> routes,
70-
final Charset defaultCharset) {
73+
final Charset defaultCharset,
74+
final Locale defaultLocale) {
7175
this.rootInjector = requireNonNull(injector, "An injector is required.");
7276
this.selector = requireNonNull(selector, "A message converter selector is required.");
7377
this.modules = requireNonNull(modules, "Request modules are required.");
7478
this.routeDefs = requireNonNull(routes, "The routes are required.");
7579
this.charset = requireNonNull(defaultCharset, "A defaultCharset is required.");
80+
this.locale = requireNonNull(defaultLocale, "A defaultLocale is required.");
7681
this.typeProvider = injector.getInstance(MediaTypeProvider.class);
7782
}
7883

@@ -109,8 +114,11 @@ public void handle(final HttpServletRequest request, final HttpServletResponse r
109114
.map(Charset::forName)
110115
.orElse(this.charset);
111116

117+
Locale locale = Optional.ofNullable(request.getHeader("Accept-Language"))
118+
.map(l -> request.getLocale()).orElse(this.locale);
119+
112120
BiFunction<Injector, Route, Request> reqFactory = (injector, route) ->
113-
new RequestImpl(request, injector, route, selector, charset, type, accept);
121+
new RequestImpl(request, injector, route, selector, type, accept, charset, locale);
114122

115123
BiFunction<Injector, Route, Response> resFactory = (injector, route) ->
116124
new ResponseImpl(response, injector, route, locals, selector, typeProvider, charset);

jooby-core/src/main/java/jooby/internal/guice/TypeConverters.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.lang.reflect.InvocationTargetException;
44
import java.lang.reflect.Method;
55
import java.lang.reflect.Modifier;
6+
import java.util.Locale;
67

78
import com.google.inject.Binder;
89
import com.google.inject.TypeLiteral;
@@ -21,6 +22,37 @@ public static void configure(final Binder binder) {
2122

2223
binder.convertToTypes(stringConstructorMatcher(), stringConstructorTypeConverter());
2324

25+
binder.convertToTypes(localeMatcher(), localeTypeConverter());
26+
27+
binder.convertToTypes(staticMethodMatcher("forName"),
28+
staticMethodTypeConverter("forName"));
29+
30+
}
31+
32+
private static TypeConverter localeTypeConverter() {
33+
return new TypeConverter() {
34+
35+
@Override
36+
public Object convert(final String value, final TypeLiteral<?> type) {
37+
String[] locale = value.split("_");
38+
return locale.length == 1 ? new Locale(locale[0]) : new Locale(locale[0], locale[1]);
39+
}
40+
};
41+
}
42+
43+
private static Matcher<TypeLiteral<?>> localeMatcher() {
44+
return new AbstractMatcher<TypeLiteral<?>>() {
45+
@Override
46+
public boolean matches(final TypeLiteral<?> type) {
47+
Class<?> rawType = type.getRawType();
48+
return rawType == Locale.class;
49+
}
50+
51+
@Override
52+
public String toString() {
53+
return "Locale(String, String)";
54+
}
55+
};
2456
}
2557

2658
private static TypeConverter stringConstructorTypeConverter() {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package jooby;
2+
3+
import static java.util.Objects.requireNonNull;
4+
import static org.junit.Assert.assertEquals;
5+
6+
import java.nio.charset.Charset;
7+
8+
import javax.inject.Inject;
9+
import javax.inject.Named;
10+
11+
import jooby.FilterFeature.HttpResponseValidator;
12+
import jooby.mvc.GET;
13+
import jooby.mvc.Path;
14+
15+
import org.apache.http.HttpResponse;
16+
import org.apache.http.client.fluent.Request;
17+
import org.apache.http.client.utils.URIBuilder;
18+
import org.apache.http.util.EntityUtils;
19+
import org.junit.Test;
20+
21+
import com.google.common.base.Charsets;
22+
import com.typesafe.config.ConfigFactory;
23+
import com.typesafe.config.ConfigValueFactory;
24+
25+
public class CharsetFeature extends ServerFeature {
26+
27+
public static class Resource {
28+
29+
private Charset charset;
30+
31+
@Inject
32+
public Resource(@Named("application.charset") final Charset namedCharset, final Charset charset) {
33+
this.charset = requireNonNull(charset, "def charset is required.");
34+
assertEquals(charset, namedCharset);
35+
}
36+
37+
@GET
38+
@Path("/")
39+
public String locale(final jooby.Request req) {
40+
return charset.toString();
41+
}
42+
}
43+
44+
{
45+
use(ConfigFactory.empty().withValue("application.charset", ConfigValueFactory.fromAnyRef(Charsets.ISO_8859_1.name())));
46+
route(Resource.class);
47+
}
48+
49+
@Test
50+
public void charset() throws Exception {
51+
assertEquals("ISO-8859-1", execute(GET(uri("/")), (response) -> {
52+
assertEquals(200, response.getStatusLine().getStatusCode());
53+
}));
54+
}
55+
56+
private static Request GET(final URIBuilder uri) throws Exception {
57+
return Request.Get(uri.build());
58+
}
59+
60+
private static Object execute(final Request request, final HttpResponseValidator validator)
61+
throws Exception {
62+
HttpResponse resp = request.execute().returnResponse();
63+
validator.validate(resp);
64+
return EntityUtils.toString(resp.getEntity());
65+
}
66+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package jooby;
2+
3+
import static org.junit.Assert.assertEquals;
4+
5+
import java.util.Locale;
6+
7+
import javax.inject.Inject;
8+
import javax.inject.Named;
9+
10+
import jooby.FilterFeature.HttpResponseValidator;
11+
import jooby.mvc.GET;
12+
import jooby.mvc.Path;
13+
14+
import org.apache.http.HttpResponse;
15+
import org.apache.http.client.fluent.Request;
16+
import org.apache.http.client.utils.URIBuilder;
17+
import org.apache.http.util.EntityUtils;
18+
import org.junit.Test;
19+
20+
import com.typesafe.config.ConfigFactory;
21+
import com.typesafe.config.ConfigValueFactory;
22+
23+
public class LocaleFeature extends ServerFeature {
24+
25+
public static class Resource {
26+
27+
@Inject
28+
public Resource(@Named("application.lang") final Locale namedLocale, final Locale locale) {
29+
assertEquals(locale, namedLocale);
30+
}
31+
32+
@GET
33+
@Path("/")
34+
public String locale(final jooby.Request req) {
35+
return req.locale().toString();
36+
}
37+
}
38+
39+
{
40+
use(ConfigFactory.empty().withValue("application.lang", ConfigValueFactory.fromAnyRef("es_ar")));
41+
route(Resource.class);
42+
}
43+
44+
@Test
45+
public void locale() throws Exception {
46+
assertEquals("es_AR", execute(GET(uri("/")), (response) -> {
47+
assertEquals(200, response.getStatusLine().getStatusCode());
48+
}));
49+
50+
assertEquals("en_us", execute(GET(uri("/")).addHeader("Accept-Language", "en_us"), (response) -> {
51+
assertEquals(200, response.getStatusLine().getStatusCode());
52+
}));
53+
}
54+
55+
private static Request GET(final URIBuilder uri) throws Exception {
56+
return Request.Get(uri.build());
57+
}
58+
59+
private static Object execute(final Request request, final HttpResponseValidator validator)
60+
throws Exception {
61+
HttpResponse resp = request.execute().returnResponse();
62+
validator.validate(resp);
63+
return EntityUtils.toString(resp.getEntity());
64+
}
65+
}

0 commit comments

Comments
 (0)