Reputation:
I have a Spring Boot (2.1.3.RELEASE
) application that uses Jersey to define the (RESTful) endpoints. I'm trying to read and propagate some messages based on the locale being sent by the user-agents.
I've configured these beans:
@Bean
public LocaleResolver localeResolver() {
final AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
resolver.setSupportedLocales(Arrays.asList(Locale.GERMANY, Locale.US));
resolver.setDefaultLocale(Locale.ENGLISH);
return resolver;
}
@Bean
public MessageSource messageSource() { // Not sure if this is needed
final ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name());
messageSource.setBasenames("classpath:/messages");
messageSource.setUseCodeAsDefaultMessage(true);
messageSource.setCacheSeconds(5);
return messageSource;
}
...and also the bundles (inside ../src/main/resources/
) like: messages.properties
(fallback), messages_en_US.properties
, messages_de_DE.properties
, etc.
Now, the challenge is that I'm not sure how to "read" the locale sent by the user-agents in order to read the messages from the bundles appropriately. I'm injecting a MessageSource ms
, and programmatically reading messages like:
final Locale locale = ???
ms.getMessage("message.duplicate-token", null, locale);
Any clues?
I've tried
LocaleContextHolder.getLocale()
but it's alwaysen_US
. If I hardcode the corresponding locale for thegetMessage
call, I'm able to retrieve the correct message(s). So I know the setup/configuration works for the most part.
Clients are sending the locale using the Accept-Language
header — and values like: de-DE
, en-US
, etc.
Upvotes: 11
Views: 40867
Reputation: 419
I also implemented same scenario and it's works for me. For this, need to override the resolveLocale
method in AcceptHeaderLocaleResolver
.
Create component LanguageResolver
for the custom implementation. Use Locale.forLanguageTag(language)
to create locale from accept-header
value. This will create a local with language and country code.
@Component
public class LanguageResolver extends AcceptHeaderLocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
String language = request.getHeader("Accept-Language");
List<Locale> supportedLocales = getSupportedLocales();
Locale defaultLocale = getDefaultLocale();
Locale requestLocale = Locale.forLanguageTag(language);
if (StringUtils.isEmpty(language)) {
return defaultLocale;
} else if (supportedLocales.contains(requestLocale)) {
return requestLocale;
} else {
return defaultLocale;
}
}
}
In the configuration class create bean using custom LanguageResolver
class.
@Configuration
public class Internationalization extends WebMvcConfigurerAdapter {
@Bean
public AcceptHeaderLocaleResolver localeResolver() {
final LanguageResolver resolver = new LanguageResolver();
resolver.setSupportedLocales(Arrays.asList(Locale.GERMANY, Locale.US,Locale.UK));
resolver.setDefaultLocale(Locale.US);
return resolver;
}
@Bean
public ResourceBundleMessageSource messageSource() {
final ResourceBundleMessageSource source = new ResourceBundleMessageSource();
source.setBasename("language/messages");
source.setDefaultEncoding("UTF-8");
return source;
}
}
Here LocaleContextHolder.getLocale()
will invoke the override method in LanguageResolver
class.
@Service
public class LocaleService {
@Autowired
ResourceBundleMessageSource messageSource;
public String getMessage(String code) {
return messageSource.getMessage(code, null, LocaleContextHolder.getLocale());
}
}
And property files are in path of resources -> language
messages_en_US.properties
messages_en_GB.properties
messages_de_DE.properties
Content of the file in the format of below
test.hello=Hello GERMANY
Tested method using below.
@RestController
public class TestController {
private LocaleService localeService;
@Autowired
public TestController(LocaleService localeService) {
this.localeService = localeService;
}
@GetMapping("/local")
public String getMessageForLocal() {
return localeService.getMessage("test.hello");
}
}
Upvotes: 1
Reputation: 406
You have to add Accept-Language
header in your api endpoint to get desired locale output. Then you have to add following configuration to parse and set the Accept-Language
header value from incoming request.
@Configuration
public class I18NConfiguration {
@Value("${i18n.locale.default:en-US}")
private String defaultLocale;
@Value("#{'${i18n.locale.supported: }'.split(',\\s*')}")
private List<String> supportedLocales;
@Bean
public LocaleResolver localeResolver() {
AcceptHeaderLocaleResolver acceptHeaderLocaleResolver = new AcceptHeaderLocaleResolver();
acceptHeaderLocaleResolver.setDefaultLocale(Locale.forLanguageTag(defaultLocale));
if (supportedLocales != null && !supportedLocales.isEmpty()) {
List<Locale> localeList = supportedLocales.stream().map(Locale::forLanguageTag).collect(Collectors.toUnmodifiableList());
acceptHeaderLocaleResolver.setSupportedLocales(localeList);
}
return acceptHeaderLocaleResolver;
}
}
Upvotes: 0
Reputation: 5297
If you want to get this on a REST controller level, you can directly get the locale instance in the REST methods. Spring does this magic
@GetMapping("/status")
public ResponseEntitiy<String> getStatus(final Locale locale) {
}
Upvotes: 4
Reputation: 83
There is no need to extend AcceptHeaderLocaleResolver
.
Create a bean definition such as:
@Bean
public LocalResolver localeResolver() {
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setSupportedLocales(Arrays.asList(new Locale("fa"), new Locale("en")));
localeResolver.setDefaultLocale(new Locale("fa"));
return localeResolver;
}
Upvotes: 1
Reputation: 99
The LocaleResolver bean that you create only gets used in Spring MVC and not in the Jersey container. It is the Spring's DispatcherServlet that uses the LocaleResolver.
So LocaleContextHolder.getLocale(); will return different local depending on if call in a Jersey controller or in a Spring MVC controller.
Upvotes: 0
Reputation: 71
I m using a bean
@Bean
public LocaleResolver localeResolver() {
AcceptHeaderLocaleResolver slr = new AcceptHeaderLocaleResolver();
slr.setDefaultLocale(Locale.UK);
return slr;
}
then another one
@Bean
public LanguageUtil languageUtil() {
return new LanguageUtil();
}
with
private Locale getLocale() {
return LocaleContextHolder.getLocale();
}
public String getLocalizedMessage(String messageKey) {
return messageSource.getMessage(messageKey, null, getLocale());
}
The header is saved into the LocaleContextHolder, and you can use it when you need it.
Upvotes: 6
Reputation: 5502
Create a custom AcceptHeaderLocaleResolver
public class AcceptHeaderResolver extends AcceptHeaderLocaleResolver {
List<Locale> LOCALES = Arrays.asList(new Locale("en"), new Locale("ar"));
@Override
public Locale resolveLocale(HttpServletRequest request) {
String headerLang = request.getHeader("Accept-Language");
return headerLang == null || headerLang.isEmpty()
? Locale.getDefault()
: Locale.lookup(Locale.LanguageRange.parse(headerLang), LOCALES);
}
}
And Don't forgot to use it in @Configuration
file
@Bean
public LocaleResolver sessionLocaleResolver() {
AcceptHeaderResolver localeResolver = new AcceptHeaderResolver();
return localeResolver;
}
Upvotes: 4
Reputation: 1193
You need add an LocaleChangeInterceptor and configure the bean as follow: Refer Spring Boot internationalization for more
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
return lci;
}
If you want to use "Accept-Language" header only, then you can extend AcceptHeaderLocaleResolver and can customize:
package com.deb.demo.config;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
public class CustomLocaleResolver extends AcceptHeaderLocaleResolver {
List<Locale> LOCALES = Arrays.asList(new Locale("en"),new Locale("es"),new Locale("fr"));
@Override
public Locale resolveLocale(HttpServletRequest request) {
if (StringUtils.isEmpty(request.getHeader("Accept-Language"))) {
return Locale.getDefault();
}
List<Locale.LanguageRange> list = Locale.LanguageRange.parse(request.getHeader("Accept-Language"));
Locale locale = Locale.lookup(list,LOCALES);
return locale;
}
}
Upvotes: 7