I have two files for the text I want to translate:
messages_es_ES.properties
messages_en_US.properties
I'm using a CookieLocaleResolver:
#Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setCookieName("language");
cookieLocaleResolver.setCookieMaxAge(3600);
cookieLocaleResolver.setDefaultLocale(new Locale("es_ES"));
return cookieLocaleResolver;
}
And this LocaleChangeInterceptor:
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
return localeChangeInterceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
In my jsp, I use this links:
EN
ES
Everything works, and the language is correctly changed when I click on one link, but everything is shown in English (in the system locale, to be more specific) when I start the application, even if I set "es_ES" as the default locale.
Solved, I changed the property setFallbackToSystemLocale from the ResourceBundleMessageSource to false, as mentioned by #noobandy here.
Now everything is working as expected.
You can set default locale as follow:
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource=new ResourceBundleMessageSource();
messageSource.setBasename("messages");
// SET DEFAULT LOCALE AS WELL
Locale.setDefault(Locale.US);
return messageSource;
}
When locale is null, AbstractMessageSource calls Locale.getDefault()
Related
I am sending emails using Thymeleaf. I've set up the templates that I need and it works perfectly. I'm now trying to translate this email depending on the receiver's language. I know the language I need to use through a previously recovered variable.
I'm using properties files to set up the variables that will be translated.
So far, I have three languages: French, English, and Chinese. So I have three properties files :
mail.messages_fr_FR
mail.messages_en_US
mail.messages_zh_CN
I have the following methods in my email configuration class :
#Bean
public ReloadableResourceBundleMessageSource emailMessageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("mailMessages");
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.US);
return slr;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
registry.addInterceptor(localeChangeInterceptor);
}
I have enabled the Mvc :
#SpringBootApplication
#EnableWebMvc
public class AccessRequestEventHookApplication extends SpringBootServletInitializer implements WebMvcConfigurer {
I have a method in my email configuration class which is the following :
public void sendMessageUsingThymeleafTemplate(String to, String subject, Map<String, Object> templateModel, String appName)
throws MessagingException {
Context thymeleafContext = new Context();
thymeleafContext.setVariables(templateModel);
String htmlBody;
if (appName == "Test1"){
htmlBody = thymeleafTemplateEngine.process("Test1-template", thymeleafContext);
} else {
htmlBody = thymeleafTemplateEngine.process("Test2-template", thymeleafContext);
}
sendHtmlMessage(to, subject, htmlBody);
}
This function is then called in my controller.
I also have my template resolver and engine as follow :
#Bean
#Primary
public ITemplateResolver thymeleafTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("mail-templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML");
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
#Bean
public SpringTemplateEngine thymeleafTemplateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
templateEngine.setTemplateEngineMessageSource(emailMessageSource());
return templateEngine;
}
I tried changing the locale resolver type with cookie, acceptLanguage, it still does not work.
If I change the language on my computer (from French to English for example), it works, the English translation is used. But I can't figure out how to change the locale variable directly in the code.
As it is sent to a user, I can not base myself on their locale.
If I was not clear, please let me know and I will provide any info needed.
Thanks !
You need to set the locale on the Context object you create for Thymeleaf:
Context thymeleafContext = new Context();
thymeleafContext.setLocale(locale);
If you are calling your sendMessageUsingThymeleafTemplate method from a web controller, you can do this as well:
#Controller
public class MyController {
private final ServletContext servletContext;
public MyController(ServletContext servletContext) {
this.servletContext = servletContext;
}
#GetMapping
public void myControllerMethod(HttpServletRequest request,
HttpServletResponse response,
Locale locale) {
WebContext context = new WebContext(request, response, servletContext);
context.setLocale(locale);
service.sendMessageUSingThymeleafTemplate(context, ...);
}
}
I use the next code
#Value("${app.user.root}")
private String userRoot;
to get constant value from my application.properties file.
In my GetMapping method I need to redirect to the error page and to pass a String as parameter.
#GetMapping("/user/activate")
public String activate(String activation) {
Users u = usersService.activate(activation);
if (u != null) {
usersService.autoLogin(u);
return "redirect:/";
}
return "redirect:/error?message=Could not activate with this activation code, please contact support";
But I need to have different String values with different languages. So, I am using Spring i18n, but how can I get the value I need at runtime? I need something like this:
return "redirect:/error?message=${errorMessage}";
Thank you, hope you will help me.
First you have to create multiple properties file for multiple languages
messages_en.properties
messages_fr.properties
The configuration of i18n should be following
#Configuration
public class LanguageConfig extends WebMvcConfigurerAdapter {
#Bean
public ReloadableResourceBundleMessageSource messageSource(){
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
SmartLocaleResolver slr = new SmartLocaleResolver();
Locale locale = new Locale("en", "us");
slr.setDefaultLocale(locale); // Set default Locale as en_cos
return slr;
}
#Bean
public LocaleChangeInterceptor localeInterceptor() {
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
interceptor.setParamName("lang");
return interceptor;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeInterceptor());
}
class SmartLocaleResolver extends CookieLocaleResolver {
#Override
public Locale resolveLocale(HttpServletRequest request) {
String acceptLanguage = request.getHeader("Accept-Language");
if (acceptLanguage == null || acceptLanguage.trim().isEmpty()) {
return super.determineDefaultLocale(request);
}
return request.getLocale();
}
}
}
Now update your controller code and autowire org.springframework.context.MessageSource and then use it to get localized message.
#Autowired
private MessageSource messageSource;
Then you can get the localized message using following code.
String errorMessage = messageSource.getMessage("project.errorMessage", new Object[]{"John Doe"}, LocaleContextHolder.getLocale());
You can also use Locale object from controller method parameter instead of LocaleContextHolder.getLocale(), but it works just fine.
I have a method that let you comment about customer and after adding a comment redirect you again to site with comments.
#RequestMapping(value="customers/details/{id}", method = RequestMethod.GET)
public String showCustomerComments(#ModelAttribute("commentContent") String commentContent, #PathVariable int id, Model model){
model.addAttribute("comment",commentRepository.getAllComments(id));
return "details";
}
#RequestMapping(value ="customers/details/{id}", method = RequestMethod.POST)
public String processAddCustomerComment(#ModelAttribute("commentContent") String commentContent, #PathVariable int id){
commentRepository.
addComment(commentContent, localDate.now().toString(), id);
return "redirect:/customers/details/{id}";
}
}
everything works fine but in the url appears model veriable:
http://localhost:8080/customers/details/62?commentContent=some_text
I already know the solution but I don't know how to implement it. The solution is to set ignoreDefaultModelOnRedirect true on ignoreDefaultModelOnRedirect. In this topic enter link description here they say to just put sth like <mvc:annotation-driven ignoreDefaultModelOnRedirect="true" /> to our xml file. But How to do this in Java Based configuration?
I have a such class:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.packt.webstore")
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean
public CommonsMultipartResolver multipartResolver(){
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(10240000);
return multipartResolver;
}
#Bean
public LocaleResolver localeResolver(){
SessionLocaleResolver sessionLocaleResolver = new SessionLocaleResolver();
sessionLocaleResolver.setDefaultLocale(new Locale("en"));
return sessionLocaleResolver;
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor(){
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
return localeChangeInterceptor;
}
#Bean
public MessageSource messageSource(){
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("resources/");
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}
and I thought to add to it:
#Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(){
RequestMappingHandlerAdapter requestMappingHandlerAdapter = new RequestMappingHandlerAdapter();
requestMappingHandlerAdapter.ignoreDefaultModelOnRedirect(true);
return requestMappingHandlerAdapter;
}
but it dosen't work.
I could add to processAddCustomerComment mehtod:
model.asMap().clear();
return "redirect:" + news.getUrl();
but I am not happy with this solution. Because suppose that we have 20 methods like mine and I don't want to put those two lines of code to every of those 20 methods.
How do I solve the problem?
It is possible that your implementation is not working because a RequestMappingHandlerAdapter is already on the context. Adding another one doesn't change the one used de facto. This article suggests that you autowire the existing RequestMappingHandlerAdapter and set the property instead.
#EnableWebMvc
#Configuration
public class MyWebConfig {
#Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
#PostConstruct
public void init() {
requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect(true);
}
......
}
All credits go to http://www.logicbig.com/ for the quoted code.
That being said, unless you have a specific reason for using #ModelAttribute, you should perhaps switch to #RequestParam, which is simpler, less strings attached. Here's this topic discussed at length.
I'm trying to implement internationalisation on my project, however I cannot resolve my BaseName, so far this is my AppConfiguration class methods:
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();`
messageSource.setBasename("messages/");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver resolver = new CookieLocaleResolver();
resolver.setDefaultLocale(new Locale("en_US"));
resolver.setCookieName("myLocaleCookie");
resolver.setCookieMaxAge(4800);
return resolver;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
interceptor.setParamName("mylocale");
registry.addInterceptor(interceptor);
}
And this is my project structure:
Project Structure
Any ideas what I'm doing wrong? I also trying to change my baseName to classpath:messages but nothing. Thank you.
try this:
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:/messages/messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
and rename your en_us.properties to messages_en_us.properties
just add this line to your application.properties
spring.messages.basename=validation
I am looking at using Spring JavaConfig with some property files but properties in bean is not getting set?in bean is not getting set?
Here is my WebConfig:
#Configuration
#EnableWebMvc
#PropertySource(value = "classpath:application.properties")
#Import(DatabaseConfig.class)
#ImportResource("/WEB-INF/applicationContext.xml")
public class WebMVCConfig extends WebMvcConfigurerAdapter {
private static final String MESSAGE_SOURCE = "/WEB-INF/classes/messages";
private static final Logger logger = LoggerFactory.getLogger(WebMVCConfig.class);
#Value("${rt.setPassword}")
private String RTPassword;
#Value("${rt.setUrl}")
private String RTURL;
#Value("${rt.setUser}")
private String RTUser;
#Bean
public ViewResolver resolver() {
UrlBasedViewResolver url = new UrlBasedViewResolver();
url.setPrefix("/WEB-INF/view/");
url.setViewClass(JstlView.class);
url.setSuffix(".jsp");
return url;
}
#Bean(name = "messageSource")
public MessageSource configureMessageSource() {
logger.debug("setting up message source");
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename(MESSAGE_SOURCE);
messageSource.setCacheSeconds(5);
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver lr = new SessionLocaleResolver();
lr.setDefaultLocale(Locale.ENGLISH);
return lr;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
logger.debug("setting up resource handlers");
registry.addResourceHandler("/resources/").addResourceLocations("/resources/**");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
logger.debug("configureDefaultServletHandling");
configurer.enable();
}
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
}
#Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver b = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.put("org.springframework.web.servlet.PageNotFound", "p404");
mappings.put("org.springframework.dao.DataAccessException", "dataAccessFailure");
mappings.put("org.springframework.transaction.TransactionException", "dataAccessFailure");
b.setExceptionMappings(mappings);
return b;
}
#Bean
public RequestTrackerConfig requestTrackerConfig()
{
RequestTrackerConfig tr = new RequestTrackerConfig();
tr.setPassword(RTPassword);
tr.setUrl(RTURL);
tr.setUser(RTUser);
return tr;
}
}
The value in tr.url is "rt.setUrl" not the value in application.properties?
I'm not 100%, but I think your #PropertySource isn't quite right. Instead of
#PropertySource(value = "classpath:application.properties")
It should just be:
#PropertySource("classpath:application.properties")
based on this:
Spring PropertySource Documentation
Also, based on the link above and since you have mentioned you were converting to a java config approach instead of xml, I think the below might be the solution to your issue:
Resolving ${...} placeholders in and #Value annotations In
order to resolve ${...} placeholders in definitions or #Value
annotations using properties from a PropertySource, one must register
a PropertySourcesPlaceholderConfigurer. This happens automatically
when using in XML, but must be
explicitly registered using a static #Bean method when using
#Configuration classes. See the "Working with externalized values"
section of #Configuration Javadoc and "a note on
BeanFactoryPostProcessor-returning #Bean methods" of #Bean Javadoc for
details and examples.
The example from the link above is how I normally do it:
#Configuration
#PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
#Autowired
Environment env;
#Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
So add at the top:
#Autowired
Environment env;
Then in your method use:
tr.setPassword(env.getProperty("rt.setPassword"));
and so on for the remaining property values. I am just not as familiar with the way you are doing it. I know the above approach will work though.
Aside from #ssn771 answer that involves injecting the Environment and retrieving the properties through it which is indeed the suggested way of doing it, this is what I've done as a workaround without having to change the way #Value is being used in the #Configuration POJO.
Out of the many suggested things the most important one is that you need to configure PropertySourcesPlaceholderConfigurer in Spring 3.1+ (or PropertyPlaceholderConfigurer in Spring 3.0). It must be static if you want it to be applied to the configuration class (to use #Value annotations).
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
From javadoc for PropertySourcesPlaceholderConfigurer :
This class is designed as a general replacement for PropertyPlaceholderConfigurer in Spring 3.1 applications. It is used by default to support the property-placeholder element in working against the spring-context-3.1 XSD, whereas spring-context versions <= 3.0 default to PropertyPlaceholderConfigurer to ensure backward compatibility. See the spring-context XSD documentation for complete details.