Skip to content
This repository was archived by the owner on Dec 19, 2023. It is now read-only.

Commit 751cfe7

Browse files
MikeSafonovjmisur
authored andcommitted
make i18n configurable (#329)
* make i18n configurable * make translation configurable via alwaysDo(...) mechanism * ResourceBundleSnippetTranslationResolver load resources only if used * custom snippet translation test * constraint translation
1 parent 9ef5fab commit 751cfe7

25 files changed

+454
-204
lines changed

spring-auto-restdocs-core/src/main/java/capital/scalable/restdocs/OperationAttributeHelper.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
99
* You may obtain a copy of the License at
10-
*
10+
*
1111
* http://www.apache.org/licenses/LICENSE-2.0
12-
*
12+
*
1313
* Unless required by applicable law or agreed to in writing, software
1414
* distributed under the License is distributed on an "AS IS" BASIS,
1515
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -30,6 +30,8 @@
3030
import java.util.Map;
3131

3232
import capital.scalable.restdocs.constraints.ConstraintReader;
33+
import capital.scalable.restdocs.i18n.SnippetTranslationManager;
34+
import capital.scalable.restdocs.i18n.SnippetTranslationResolver;
3335
import capital.scalable.restdocs.jackson.TypeMapping;
3436
import capital.scalable.restdocs.javadoc.JavadocReader;
3537
import capital.scalable.restdocs.misc.AuthorizationSnippet;
@@ -95,6 +97,19 @@ public static void setJavadocReader(MockHttpServletRequest request,
9597
.put(JavadocReader.class.getName(), javadocReader);
9698
}
9799

100+
public static void setTranslationResolver(MockHttpServletRequest request, SnippetTranslationResolver translationResolver){
101+
((Map) request.getAttribute(ATTRIBUTE_NAME_CONFIGURATION))
102+
.put(SnippetTranslationResolver.class.getName(), translationResolver);
103+
}
104+
105+
public static SnippetTranslationResolver getTranslationResolver(Operation operation){
106+
Object resolver = operation.getAttributes().get(SnippetTranslationResolver.class.getName());
107+
if(resolver == null) {
108+
return SnippetTranslationManager.getDefaultResolver();
109+
}
110+
return (SnippetTranslationResolver) resolver;
111+
}
112+
98113
public static RestDocumentationContext getDocumentationContext(Operation operation) {
99114
return (RestDocumentationContext) operation
100115
.getAttributes().get(RestDocumentationContext.class.getName());

spring-auto-restdocs-core/src/main/java/capital/scalable/restdocs/constraints/ConstraintAndGroupDescriptionResolver.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
*/
2020
package capital.scalable.restdocs.constraints;
2121

22-
import static capital.scalable.restdocs.i18n.SnippetTranslationResolver.translate;
2322
import static java.util.Collections.emptyList;
2423
import static java.util.Collections.singletonMap;
2524
import static org.apache.commons.lang3.StringUtils.isBlank;
@@ -31,6 +30,7 @@
3130
import java.util.List;
3231
import java.util.MissingResourceException;
3332

33+
import capital.scalable.restdocs.i18n.SnippetTranslationResolver;
3434
import org.slf4j.Logger;
3535
import org.springframework.restdocs.constraints.Constraint;
3636
import org.springframework.restdocs.constraints.ConstraintDescriptionResolver;
@@ -42,9 +42,11 @@ public class ConstraintAndGroupDescriptionResolver implements
4242
static final String VALUE = "value";
4343

4444
private final ConstraintDescriptionResolver delegate;
45+
private final SnippetTranslationResolver translationResolver;
4546

46-
public ConstraintAndGroupDescriptionResolver(ConstraintDescriptionResolver delegate) {
47+
public ConstraintAndGroupDescriptionResolver(ConstraintDescriptionResolver delegate, SnippetTranslationResolver translationResolver) {
4748
this.delegate = delegate;
49+
this.translationResolver = translationResolver;
4850
}
4951

5052
@Override
@@ -92,7 +94,7 @@ public String resolveGroupDescription(Class group, String constraintDescription)
9294
}
9395

9496
private String fallbackGroupDescription(Class group, String constraintDescription) {
95-
return translate("constraints-groups", constraintDescription, group.getSimpleName());
97+
return translationResolver.translate("constraints-groups", constraintDescription, group.getSimpleName());
9698
}
9799

98100
private String resolvePlainDescription(Constraint constraint) {

spring-auto-restdocs-core/src/main/java/capital/scalable/restdocs/constraints/ConstraintReaderImpl.java

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
import static capital.scalable.restdocs.constraints.ConstraintAndGroupDescriptionResolver.VALUE;
2323
import static capital.scalable.restdocs.constraints.MethodParameterValidatorConstraintResolver.CONSTRAINT_CLASS;
24-
import static capital.scalable.restdocs.i18n.SnippetTranslationResolver.translate;
2524
import static capital.scalable.restdocs.util.FormatUtil.collectionToString;
2625
import static java.util.Collections.emptyList;
2726
import static java.util.Collections.emptyMap;
@@ -36,46 +35,51 @@
3635
import java.util.Collections;
3736
import java.util.List;
3837

38+
import capital.scalable.restdocs.i18n.SnippetTranslationResolver;
3939
import com.fasterxml.jackson.core.JsonProcessingException;
4040
import com.fasterxml.jackson.databind.ObjectMapper;
4141
import org.apache.commons.lang3.StringUtils;
4242
import org.slf4j.Logger;
4343
import org.springframework.core.MethodParameter;
4444
import org.springframework.restdocs.constraints.Constraint;
45+
import org.springframework.restdocs.constraints.ConstraintDescriptionResolver;
4546
import org.springframework.restdocs.constraints.ConstraintDescriptions;
46-
import org.springframework.restdocs.constraints.ResourceBundleConstraintDescriptionResolver;
4747

4848
public class ConstraintReaderImpl implements ConstraintReader {
4949

5050
private static final Logger log = getLogger(ConstraintReaderImpl.class);
5151

52-
private final ConstraintAndGroupDescriptionResolver constraintDescriptionResolver;
52+
private final ConstraintAndGroupDescriptionResolver constraintAndGroupDescriptionResolver;
5353

5454
private final SkippableConstraintResolver skippableConstraintResolver;
5555

5656
private final MethodParameterConstraintResolver constraintResolver;
5757

5858
private final ObjectMapper objectMapper;
5959

60-
private ConstraintReaderImpl(MethodParameterConstraintResolver actualResolver, ObjectMapper objectMapper) {
61-
constraintDescriptionResolver = new ConstraintAndGroupDescriptionResolver(
62-
new ResourceBundleConstraintDescriptionResolver());
60+
private final SnippetTranslationResolver translationResolver;
61+
62+
private ConstraintReaderImpl(MethodParameterConstraintResolver actualResolver, ObjectMapper objectMapper, SnippetTranslationResolver translationResolver,
63+
ConstraintDescriptionResolver constraintDescriptionResolver) {
64+
this.translationResolver = translationResolver;
65+
constraintAndGroupDescriptionResolver = new ConstraintAndGroupDescriptionResolver(
66+
constraintDescriptionResolver, translationResolver);
6367
skippableConstraintResolver = new SkippableConstraintResolver(
64-
actualResolver, constraintDescriptionResolver);
68+
actualResolver, constraintAndGroupDescriptionResolver);
6569
constraintResolver = new HumanReadableConstraintResolver(skippableConstraintResolver);
6670
this.objectMapper = objectMapper;
6771
}
6872

69-
public static ConstraintReaderImpl create(ObjectMapper objectMapper) {
70-
return CONSTRAINT_CLASS != null ? createWithValidation(objectMapper) : createWithoutValidation(objectMapper);
73+
public static ConstraintReaderImpl create(ObjectMapper objectMapper, SnippetTranslationResolver translationResolver, ConstraintDescriptionResolver constraintDescriptionResolver) {
74+
return CONSTRAINT_CLASS != null ? createWithValidation(objectMapper, translationResolver, constraintDescriptionResolver) : createWithoutValidation(objectMapper, translationResolver, constraintDescriptionResolver);
7175
}
7276

73-
static ConstraintReaderImpl createWithoutValidation(ObjectMapper objectMapper) {
74-
return new ConstraintReaderImpl(new NoOpMethodParameterConstraintResolver(), objectMapper);
77+
static ConstraintReaderImpl createWithoutValidation(ObjectMapper objectMapper, SnippetTranslationResolver translationResolver, ConstraintDescriptionResolver constraintDescriptionResolver) {
78+
return new ConstraintReaderImpl(new NoOpMethodParameterConstraintResolver(), objectMapper, translationResolver, constraintDescriptionResolver);
7579
}
7680

77-
static ConstraintReaderImpl createWithValidation(ObjectMapper objectMapper) {
78-
return new ConstraintReaderImpl(new MethodParameterValidatorConstraintResolver(), objectMapper);
81+
static ConstraintReaderImpl createWithValidation(ObjectMapper objectMapper, SnippetTranslationResolver translationResolver, ConstraintDescriptionResolver constraintDescriptionResolver) {
82+
return new ConstraintReaderImpl(new MethodParameterValidatorConstraintResolver(), objectMapper, translationResolver, constraintDescriptionResolver);
7983
}
8084

8185
@Override
@@ -85,7 +89,7 @@ public List<String> getOptionalMessages(Class<?> javaBaseClass, String javaField
8589

8690
@Override
8791
public String getTypeSpecifier(Class<?> javaBaseClass) {
88-
String message = constraintDescriptionResolver.resolveDescription(
92+
String message = constraintAndGroupDescriptionResolver.resolveDescription(
8993
new Constraint(javaBaseClass.getCanonicalName(), emptyMap()));
9094

9195
// fallback
@@ -99,7 +103,7 @@ public String getTypeSpecifier(Class<?> javaBaseClass) {
99103
@Override
100104
public List<String> getConstraintMessages(Class<?> javaBaseClass, String javaFieldName) {
101105
ConstraintDescriptions constraints = new ConstraintDescriptions(javaBaseClass,
102-
constraintResolver, constraintDescriptionResolver);
106+
constraintResolver, constraintAndGroupDescriptionResolver);
103107
List<String> constraintMessages = new ArrayList<>();
104108
constraintMessages.addAll(constraints.descriptionsForProperty(javaFieldName));
105109
constraintMessages.addAll(getEnumConstraintMessage(javaBaseClass, javaFieldName));
@@ -112,7 +116,7 @@ public List<String> getConstraintMessages(MethodParameter param) {
112116
List<String> constraintMessages = new ArrayList<>();
113117
for (Constraint constraint : constraints) {
114118
constraintMessages.add(
115-
constraintDescriptionResolver.resolveDescription(constraint));
119+
constraintAndGroupDescriptionResolver.resolveDescription(constraint));
116120
}
117121
constraintMessages.addAll(getEnumConstraintMessage(param));
118122
Collections.sort(constraintMessages);
@@ -154,12 +158,12 @@ private List<String> getEnumConstraintMessage(Class<?> rawClass) {
154158

155159
String value = collectionToString(serializedEnumValues);
156160
String enumName = enumClass.getCanonicalName();
157-
String message = constraintDescriptionResolver.resolveDescription(
161+
String message = constraintAndGroupDescriptionResolver.resolveDescription(
158162
new Constraint(enumName, singletonMap(VALUE, (Object) value)));
159163

160164
// fallback
161165
if (isBlank(message) || message.equals(enumName)) {
162-
message = translate("constraints-enum", value);
166+
message = translationResolver.translate("constraints-enum", value);
163167
}
164168
return singletonList(message);
165169
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*-
2+
* #%L
3+
* Spring Auto REST Docs Core
4+
* %%
5+
* Copyright (C) 2015 - 2019 Scalable Capital GmbH
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package capital.scalable.restdocs.i18n;
21+
22+
import java.text.MessageFormat;
23+
import java.util.Locale;
24+
import java.util.MissingResourceException;
25+
import java.util.ResourceBundle;
26+
27+
/**
28+
* Inspired by
29+
* {@link org.springframework.restdocs.constraints.ResourceBundleConstraintDescriptionResolver}
30+
*/
31+
public class ResourceBundleSnippetTranslationResolver implements SnippetTranslationResolver {
32+
33+
private ResourceBundle defaultMessages;
34+
35+
private ResourceBundle userMessages;
36+
37+
public ResourceBundleSnippetTranslationResolver() {
38+
defaultMessages = getBundle("DefaultSnippetMessages");
39+
userMessages = getBundle("SnippetMessages");
40+
}
41+
42+
private static ResourceBundle getBundle(String name) {
43+
try {
44+
return ResourceBundle.getBundle(
45+
ResourceBundleSnippetTranslationResolver.class.getPackage()
46+
.getName() + "." + name,
47+
Locale.getDefault(), Thread.currentThread().getContextClassLoader());
48+
} catch (MissingResourceException ex) {
49+
return null;
50+
}
51+
}
52+
53+
public String translate(String key, Object... args) {
54+
try {
55+
if (userMessages != null) {
56+
return format(userMessages.getString(key), args);
57+
}
58+
} catch (MissingResourceException ex) {
59+
// Continue and return default description, if available
60+
}
61+
return format(defaultMessages.getString(key), args);
62+
}
63+
64+
// visible for testing
65+
void setUserMessages(String name) {
66+
userMessages = getBundle(name);
67+
}
68+
69+
private static String format(String message, Object[] args) {
70+
return new MessageFormat(message).format(args);
71+
}
72+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package capital.scalable.restdocs.i18n;
2+
3+
4+
public class SnippetTranslationManager {
5+
6+
private static SnippetTranslationResolver snippetTranslationResolver;
7+
8+
public static synchronized SnippetTranslationResolver getDefaultResolver() {
9+
if (snippetTranslationResolver == null) {
10+
snippetTranslationResolver = new ResourceBundleSnippetTranslationResolver();
11+
}
12+
return snippetTranslationResolver;
13+
}
14+
}
Lines changed: 2 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,6 @@
1-
/*-
2-
* #%L
3-
* Spring Auto REST Docs Core
4-
* %%
5-
* Copyright (C) 2015 - 2019 Scalable Capital GmbH
6-
* %%
7-
* Licensed under the Apache License, Version 2.0 (the "License");
8-
* you may not use this file except in compliance with the License.
9-
* You may obtain a copy of the License at
10-
*
11-
* http://www.apache.org/licenses/LICENSE-2.0
12-
*
13-
* Unless required by applicable law or agreed to in writing, software
14-
* distributed under the License is distributed on an "AS IS" BASIS,
15-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16-
* See the License for the specific language governing permissions and
17-
* limitations under the License.
18-
* #L%
19-
*/
201
package capital.scalable.restdocs.i18n;
212

22-
import java.text.MessageFormat;
23-
import java.util.Locale;
24-
import java.util.MissingResourceException;
25-
import java.util.ResourceBundle;
3+
public interface SnippetTranslationResolver {
264

27-
/**
28-
* Inspired by
29-
* {@link org.springframework.restdocs.constraints.ResourceBundleConstraintDescriptionResolver}
30-
*/
31-
public class SnippetTranslationResolver {
32-
33-
private static ResourceBundle defaultMessages = getBundle("DefaultSnippetMessages");
34-
35-
private static ResourceBundle userMessages = getBundle("SnippetMessages");
36-
37-
private static ResourceBundle getBundle(String name) {
38-
try {
39-
return ResourceBundle.getBundle(
40-
SnippetTranslationResolver.class.getPackage()
41-
.getName() + "." + name,
42-
Locale.getDefault(), Thread.currentThread().getContextClassLoader());
43-
} catch (MissingResourceException ex) {
44-
return null;
45-
}
46-
}
47-
48-
public static String translate(String key, Object... args) {
49-
try {
50-
if (userMessages != null) {
51-
return format(userMessages.getString(key), args);
52-
}
53-
} catch (MissingResourceException ex) {
54-
// Continue and return default description, if available
55-
}
56-
return format(defaultMessages.getString(key), args);
57-
}
58-
59-
// visible for testing
60-
static void setUserMessages(String name) {
61-
userMessages = getBundle(name);
62-
}
63-
64-
private static String format(String message, Object[] args) {
65-
return new MessageFormat(message).format(args);
66-
}
5+
String translate(String key, Object... args);
676
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package capital.scalable.restdocs.i18n;
2+
3+
import org.springframework.test.web.servlet.MvcResult;
4+
import org.springframework.test.web.servlet.ResultHandler;
5+
6+
import static capital.scalable.restdocs.OperationAttributeHelper.setTranslationResolver;
7+
8+
public abstract class TranslationHandlers {
9+
10+
public static ResultHandler defaultTranslation() {
11+
return new TranslationPreparingResultHandler(SnippetTranslationManager.getDefaultResolver());
12+
}
13+
14+
public static ResultHandler translation(SnippetTranslationResolver translationResolver) {
15+
return new TranslationPreparingResultHandler(translationResolver);
16+
}
17+
18+
private static class TranslationPreparingResultHandler implements ResultHandler {
19+
20+
private final SnippetTranslationResolver translationResolver;
21+
22+
public TranslationPreparingResultHandler(SnippetTranslationResolver translationResolver) {
23+
this.translationResolver = translationResolver;
24+
}
25+
26+
@Override
27+
public void handle(MvcResult mvcResult) throws Exception {
28+
setTranslationResolver(mvcResult.getRequest(), translationResolver);
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)