Spring Boot: How to access locale in Services or Repositories - java

I followed some tutorials for internationalization in Spring Boot.
(https://www.youtube.com/watch?v=Y7pHhui0cD4 and https://www.baeldung.com/spring-boot-internationalization)
I came so far to configure it like this:
#Configuration
public class InternationalizationConfiguration {
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(Locale.GERMAN);
return sessionLocaleResolver;
}
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
}
and to use it in the RestConroller like this:
#RestController
public class ExperimentalControllerImpl implements ExperimentalController{
#Autowired
ResourceBundleMessageSource messageSource;
#Override
public String testI18n(#RequestHeader("Accept-Language") String locale) {
StringBuilder builder = new StringBuilder();
builder.append(messageSource.getMessage(Messages.HELLO, null, new Locale(locale)));
builder.append("\n");
builder.append(messageSource.getMessage(Messages.HOW_ARE_YOU, null, new Locale(locale)));
return builder.toString();
}
}
It works fine.....
But how do I get the locale information from the request header to my service layer or my repository layers?
Is there a best practise to store the information which locale is sent by the request, so that I can access it in my services and repositories? I think the value must be request/thread specific. Giving it as a method parameter down to the other layer seems pretty ugly.
Thank you
Regards form Germany

Related

Spring Boot: How to access set Locale in Service or Repository layer

I followed some tutorials for internationalization in Spring Boot.
(https://www.youtube.com/watch?v=Y7pHhui0cD4 and https://www.baeldung.com/spring-boot-internationalization)
I came so far to configure it like this:
#Configuration
public class InternationalizationConfiguration {
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(Locale.GERMAN);
return sessionLocaleResolver;
}
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
}
and to use it in the RestConroller like this:
#RestController
public class ExperimentalControllerImpl implements ExperimentalController{
#Autowired
ResourceBundleMessageSource messageSource;
#Override
public String testI18n(#RequestHeader("Accept-Language") String locale) {
StringBuilder builder = new StringBuilder();
builder.append(messageSource.getMessage(Messages.HELLO, null, new Locale(locale)));
builder.append("\n");
builder.append(messageSource.getMessage(Messages.HOW_ARE_YOU, null, new Locale(locale)));
return builder.toString();
}
}
It works fine.....
But how do I get the locale information from the request header to my service layer or my repository layers?
Is there a best practise to store the information which locale is sent by the request, so that I can access it in my services and repositories? I think the value must be request/thread specific. Giving it as a method parameter down to the other layer seems pretty ugly.
Thank you
Regards form Germany

How to i18n a Spring RestController on java.validation constraints?

How can I i18n a spring #RestController property on javax.validation constraints?
I thought I could just add /src/main/resources/messages.properties and messages_de.properties, and then spring would detect them and enable proper i18n? But that seems not to be the case:
#RestController
public void TestController {
#PostMapping
public void post(#Valid #RequestBody Person p) {
}
}
public class Person {
private String firstname;
#javax.validation.constraints.NotBlank(message = "{errors.person.lastname}")
private String lastname;
}
messages.properties:
errors.person.lastname=person should provide a lastname
messages_de.properties:
errors.person.lastname=Person ohne Nachnamen
Problem: if I now send a POST request:
{
"exception": "org.springframework.web.bind.MethodArgumentNotValidException",
"message": "'lastname': {errors.person.lastname}"
}
Question 1: do I really have to tell spring explicit to apply i18n as follows? Or can I somehow rely on auto-detect features?
Next step was adding the following configuration. Now the default message is resolved properly. But is it necessary to really add this whenever I want to have validation i18n?
#Configuration
public class MessageSourceConfig {
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource());
return bean;
}
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource bundle = new ResourceBundleMessageSource();
bundle.setBasenames("messages");
return bundle;
}
}
Question 2: I those beans are required: how can I now send a post request to switch the language?
I tried adding get-query parameters like ?local=de, locale=de, ?lang=de, language=de, but none of them worked...
With the help of #M. Deinum above, this is the missing peace:
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean(MessageSource messageSource) {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource);
return bean;
}
Then having to send a web request with http header Accept-Language=de.

How to set a standard i18n locale using spring-boot and thymeleaf?

I integrated i18n to my application using the following config:
#Configuration
public class LocaleConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
registry.addInterceptor(localeChangeInterceptor);
}
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/i18n/application");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
I set up properties for "en" and "de" and everything is working fine so far.
If I enter the page from spain for example I only see the placeholder from my html file. But instead of this I want to achieve that the standard language is english ("en") for languages/countries with no existing property config. So I tried this:
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(Locale.US);
return sessionLocaleResolver;
}
It sets the default to english as I want but now every page (also with existing property for the special language) is displayed in english.
Yo have to delete this
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(Locale.US);
return sessionLocaleResolver;
}
And add in src\main\resources\i18n the default application.properties in English
That should fix your problem

Spring i18n question marks instead of text

I hava i18n, but experience problems with russian letters. I have question marks ????? instead of text. Configuration:
#Bean
public LocaleResolver localeResolver(){
CookieLocaleResolver resolver = new CookieLocaleResolver();
resolver.setDefaultLocale(new Locale("ru"));
resolver.setCookieName("locale");
resolver.setCookieMaxAge(60 * 60 * 24 * 365 * 10);
return resolver;
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor changeInterceptor = new LocaleChangeInterceptor();
changeInterceptor.setParamName("lang");
return changeInterceptor;
}
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/i18n/messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
Also I am using Thymeleaf.
<h1 th:text="#{message}"></h1>
Just a guess: in "addInterceptor" you are constructing the LocaleChangeInterceptor manually yourself. If this class depends on other spring beans (like MessageSource) then these are not injected. Find a way to let spring instantiate the LocaleChangeInterceptor (e.g. by using one of the various getBean() of the application context).
Your files encoding is wrong. You should set it to UTF-8. If you use Intellij IDEA you can find it in the right bottom corner. If this option is disabled go to Settings -> Editor -> File Encodings and change the Default encoding for properties files parameter

Using I18n messages while validating forms with JSR-303

My app is fully-configured spring-boot app with thymeleaf templates engine. I18n configured as well so I can use it within my templates. Here is the config I use:
spring.messages.basename=i18n/messages
While manual validating fields I18n also work fine:
BindingResult result;
result.rejectValue("field", "some.i18n.code");
But once I want to implement some custom ConstraintValidator objects and use message field - no I18n involved, I receive plain codes as a response instead of a message. I.e.
{some.i18n.code}
I tried this solution - no result.
This on as well - same result.
What am I missing?
I guess I found the solution, maybe it will be helpful to others. All you have to do is to add the following definitions into your WebMvcConfigurerAdapter configuration implementation:
#Autowired
private MessageSource messageSource;
#Bean
public LocalValidatorFactoryBean validator() {
LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
validatorFactoryBean.setValidationMessageSource(messageSource);
return validatorFactoryBean;
}
#Override
public Validator getValidator() {
return validator();
}
An alternative solution is just declare this bean in any of your #Configuration classes:
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean(MessageSource messageSource) {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource);
return bean;
}
Due to declaring this, now my custom error codes from my custom validators are being searched for in my messages.properties (that I also have in a i18n subdirectory by declaring spring.messages.basename=i18n/messages).
Example validator code:
public class ContestValidator implements ConstraintValidator<ValidContest, CreateContestParameters> {
#Override
public void initialize(ValidContest constraintAnnotation) {
}
#Override
public boolean isValid(CreateContestParameters contestParameters, ConstraintValidatorContext context) {
boolean result = true;
if (!endDateIsEqualOrAfterStartDate(contestParameters)) {
context.buildConstraintViolationWithTemplate("{Contest.endDate.invalid}")
.addPropertyNode("endDate").addConstraintViolation();
result = false;
}
if (!registrationDeadlineIsBeforeStartDate(contestParameters)) {
context.buildConstraintViolationWithTemplate("{Contest.registrationDeadline.invalid}")
.addPropertyNode("registrationDeadline").addConstraintViolation();
}
return result;
}
}

Categories