Skip to content

Commit 8c901e3

Browse files
committed
Fix #35506: allow to override lang strings
1 parent 503fa36 commit 8c901e3

2 files changed

Lines changed: 72 additions & 26 deletions

File tree

src/main/java/eu/openanalytics/containerproxy/ContainerProxyApplication.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,6 @@ public static Properties getDefaultProperties() {
212212
properties.put("springdoc.api-docs.enabled", false);
213213
properties.put("springdoc.swagger-ui.enabled", false);
214214

215-
// Internationalization
216-
// ====================
217-
properties.put("spring.messages.basename", "messages,cp_messages");
218-
// use MessageFormat for all messages: single quotes must be escaped in every message
219-
properties.put("spring.messages.always-use-message-format", "true");
220-
221215
return properties;
222216
}
223217

src/main/java/eu/openanalytics/containerproxy/util/CustomMessageSource.java

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,42 +20,94 @@
2020
*/
2121
package eu.openanalytics.containerproxy.util;
2222

23-
import org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration;
24-
import org.springframework.boot.autoconfigure.context.MessageSourceProperties;
25-
import org.springframework.boot.context.properties.EnableConfigurationProperties;
26-
import org.springframework.context.MessageSource;
27-
import org.springframework.context.MessageSourceResolvable;
28-
import org.springframework.context.NoSuchMessageException;
23+
import eu.openanalytics.containerproxy.service.LanguageService;
24+
import org.apache.commons.lang3.StringUtils;
25+
import org.jspecify.annotations.Nullable;
26+
import org.springframework.boot.context.properties.bind.Bindable;
27+
import org.springframework.boot.context.properties.bind.Binder;
28+
import org.springframework.context.support.AbstractMessageSource;
29+
import org.springframework.context.support.ResourceBundleMessageSource;
30+
import org.springframework.core.env.Environment;
2931
import org.springframework.stereotype.Component;
30-
import org.thymeleaf.util.StringUtils;
3132

3233
import javax.annotation.Nonnull;
34+
import java.text.MessageFormat;
35+
import java.util.HashMap;
3336
import java.util.Locale;
37+
import java.util.Map;
38+
import java.util.ResourceBundle;
39+
import java.util.stream.Collectors;
3440

3541
@Component("messageSource")
36-
@EnableConfigurationProperties(MessageSourceProperties.class)
37-
public class CustomMessageSource implements MessageSource {
42+
public class CustomMessageSource extends AbstractMessageSource {
3843

39-
private MessageSource delegate;
44+
private static final String PROP_PROXY_TRANSLATION_OVERRIDES = "proxy.translation-overrides";
45+
private final Map<String, Map<String, String>> translationOverrides = new HashMap<>();
46+
private final Map<String, Map<String, MessageFormat>> translationOverridesMessageFormat = new HashMap<>();
4047

41-
public CustomMessageSource(MessageSourceProperties properties) {
42-
MessageSourceAutoConfiguration autoConfiguration = new MessageSourceAutoConfiguration();
43-
this.delegate = autoConfiguration.messageSource(properties);
48+
public CustomMessageSource(Environment environment, LanguageService languageService) {
49+
ResourceBundleMessageSource parentMessageSource = new CapitalizedMessageSource();
50+
parentMessageSource.setBasenames("messages", "cp_messages");
51+
parentMessageSource.setAlwaysUseMessageFormat(true);
52+
setParentMessageSource(parentMessageSource);
53+
54+
// read overridden translations from config file
55+
for (String language : languageService.getEnabledLanguages().keySet()) {
56+
Map<String, String> messages = getTranslationOverrides(environment, language);
57+
translationOverrides.put(language, messages);
58+
translationOverridesMessageFormat.put(language, convertToMessageFormat(messages, language));
59+
}
4460
}
4561

4662
@Override
47-
public String getMessage(@Nonnull String code, Object[] args, String defaultMessage, @Nonnull Locale locale) {
48-
return StringUtils.capitalize(delegate.getMessage(code, args, defaultMessage, locale));
63+
protected @Nullable String resolveCodeWithoutArguments(@Nonnull String code, @Nonnull Locale locale) {
64+
if (translationOverrides.containsKey(locale.getLanguage())) {
65+
return translationOverrides.get(locale.getLanguage()).get(code);
66+
}
67+
return null;
4968
}
5069

5170
@Override
52-
public String getMessage(@Nonnull String code, Object[] args, @Nonnull Locale locale) throws NoSuchMessageException {
53-
return StringUtils.capitalize(delegate.getMessage(code, args, locale));
71+
protected @Nullable MessageFormat resolveCode(@Nonnull String code, @Nonnull Locale locale) {
72+
if (translationOverrides.containsKey(locale.getLanguage())) {
73+
return translationOverridesMessageFormat.get(locale.getLanguage()).get(code);
74+
}
75+
return null;
5476
}
5577

56-
@Override
57-
public String getMessage(@Nonnull MessageSourceResolvable resolvable, @Nonnull Locale locale) throws NoSuchMessageException {
58-
return StringUtils.capitalize(delegate.getMessage(resolvable, locale));
78+
private Map<String, String> getTranslationOverrides(Environment environment, String language) {
79+
return Binder.get(environment)
80+
.bind(PROP_PROXY_TRANSLATION_OVERRIDES + "." + language, Bindable.mapOf(String.class, String.class))
81+
.orElse(new HashMap<>());
82+
}
83+
84+
private Map<String, MessageFormat> convertToMessageFormat(Map<String, String> messages, String language) {
85+
Locale locale = Locale.forLanguageTag(language);
86+
return messages.entrySet().stream().collect(Collectors.toMap(
87+
Map.Entry::getKey,
88+
e -> new MessageFormat(StringUtils.capitalize(e.getValue()), locale))
89+
);
90+
}
91+
92+
private static class CapitalizedMessageSource extends ResourceBundleMessageSource {
93+
94+
/**
95+
* Capitalizes the first letter of each string. Because this low-level method
96+
* is overridden, the strings are still properly cached.
97+
*
98+
* @param bundle the ResourceBundle to perform the lookup in
99+
* @param key the key to look up
100+
* @return the associated value, or {@code null} if none
101+
*/
102+
@Override
103+
protected @Nullable String getStringOrNull(@Nonnull ResourceBundle bundle, @Nonnull String key) {
104+
String result = super.getStringOrNull(bundle, key);
105+
if (result != null) {
106+
return StringUtils.capitalize(result);
107+
}
108+
return null;
109+
}
110+
59111
}
60112

61113
}

0 commit comments

Comments
 (0)