Migration of thymeleaf version 2.14 to 3.0 not finding message properties - spring

Thymeleaf version 2.14 was picking up message.properties file correctly. After migration to 3.0,it is giving error ??hello.MESSAGE_en_US??.
But in javacode by autowiring messsagesource
messageSource.getMessage( "hello.MESSAGE",null, Locale.getDefault()) ->Hello App
The project structure
src/main/
-java
-com.cando
-controllers
-resources
-messages_en.properties
-spring.properties
-webapp
-WEB-INF
-templates
-index.html
index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Helllo</title>
</head>
<body>
<p th:text="#{hello.MESSAGE}">Hello App!</p>
</body>
</html>
messages_en.properties
hello.MESSAGE=Hello App
config
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
resourceBundleMessageSource.setBasename("messages");
resourceBundleMessageSource.setBasename("spring");
return resourceBundleMessageSource;
}

In your config file you setBasename() to messages and then override value to spring. I think ResourceBundleMessageSource just doesn't see your messages file, and you should add it like: setBasenames("messages","spring"). Give it a try and tell if it works :)

I think i found solution
While you configure SpringTemplateEngine it's possible to explicitly set messageSource and messageResolver.
For me, this works.
My code in kotlin:
#Configuration
#ComponentScan
open class ThymeleafConfig {
#Autowired
lateinit var applicationContext: ApplicationContext
#Autowired
lateinit var messageSource: MessageSource
#Autowired
lateinit var messageResolver: SpringMessageResolver
#Bean
open fun viewResolver(): ViewResolver = ThymeleafViewResolver().apply
{
templateEngine = templateEngine()
characterEncoding = "UTF-8"
}
fun templateEngine(): TemplateEngine = SpringTemplateEngine().apply {
enableSpringELCompiler = true
setTemplateResolver(templateResolver())
setMessageSource(messageSource)
addMessageResolver(messageResolver)
}
fun templateResolver(): ITemplateResolver = SpringResourceTemplateResolver().apply {
prefix = "file:./web/templates/"
suffix = ".html"
templateMode = TemplateMode.HTML
cacheTTLMs = 0
isCacheable = false
setApplicationContext(applicationContext)
}
}
#Component
class MessageResolverImpl: SpringMessageResolver() { }
and messageSource bean:
#Bean
open fun messageSource(): MessageSource {
val source = ReloadableResourceBundleMessageSource()
source.setBasename("classpath:messages")
source.setUseCodeAsDefaultMessage(true)
source.setDefaultEncoding("UTF-8")
return source
}
Hope this will help you

Related

Thymeleaf - return rendered template to String

I was using this solution as an example:
Can I render Thymeleaf templates manually from a String?
but after processing template I am getting not rendered template:
my template (plans/test.html):
<!doctype html>
<html lang="pl">
<div th:text="${loki}"></div>
</html>
Java code that should renderd the template:
#RestController
public class PlansPageRestController {
#Autowired
TemplateEngine myTemplateEngine;
#RequestMapping(value = {"/public/plans"}, method = RequestMethod.POST, produces = "application/json")
public Map<String,String> getPlans(#RequestParam Map<String, String> requestParams) {
Context ctx = new Context();
ctx.setVariable("loki", "Some test value");
String htmlTemplate = myTemplateEngine.process("plans/test.html", ctx);
Map<String,String> result = new HashMap<>();
result.put("html", htmlTemplate );
result.put("result", "success" );
return result;
}
}
but as a result I am getting content of plans/test.html so:
<!doctype html>
<html lang="pl">
<div th:text="${loki}"></div>
</html>
I am working with spring boot 3.0.0 and regarding to pom I am using thymeleaf:
<artifactId>thymeleaf-spring6</artifactId>
<version>3.1.0.RELEASE</version>
Can anyone help me in finding what I am doing wrong?
my thymeleaf configuration:
#Configuration
public class ThymeleafConfiguration implements WebMvcConfigurer, ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
#Bean
public TemplateEngine myTemplateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setEnableSpringELCompiler(true);
engine.setTemplateResolver(templateResolver());
engine.setDialect(new LayoutDialect());
return engine;
}
private ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("classpath:/templates/");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCacheable(false);
resolver.setCacheTTLMs(0L);
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
}
The reason your standard Thymeleaf expression is not being evaluated is because you have replaced the Thymeleaf "standard" dialect with the layout dialect:
engine.setDialect(new LayoutDialect());
(I should say "the Spring dialect", given you are using Spring-Thymeleaf.)
If you need to use the layout dialect, then you can add it to the engine - and still keep the standard dialect as well:
engine.addDialect(new LayoutDialect());
Or, you may not need the layout dialect at all (you do not use it in the sample HTML file in the question, but maybe you use it elsewhere). If that is the case, you can remove this line of code.
Just to add: The default Spring Boot settings should work without you needing to define any ThymeleafConfiguration class. But, again, there may be other places where you need to use a custom configuration (e.g. the UTF-8 encoding).

Display messages with thymeleaf in Spring Boot

I'm tryting to add externalized messages from a properties file in a thymeleaf html template. I have a file messages_es.properties located in i18n/messages with this inside:
estimado=Estimado:
In my html template i have the following
<div th:text="#{estimado}">Estimado placeholder</div>
but then, the html I get is the following:
<div>??estimado_es??</div>
Is anyone able to help me?
This is the configuration i have added in order to make this work:
#Bean
#Qualifier("custom")
public ITemplateResolver thymeleafTemplateResolver(){
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML");
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
#Bean
public SpringTemplateEngine thymeleafTemplateEngine(#Qualifier("custom") ITemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
templateEngine.setTemplateEngineMessageSource(messageSource());
return templateEngine;
}
#Bean
#Description("Spring Message Resolver")
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("i18n/messages");
return messageSource;
}
In my application.yml file:
spring.messages.basename: i18n/messages
first of all you should add classpath: as prefix to your base name.
secondly if you have baseName set toi18n/messages app will look for i18n/messages_es.properties
if i understand correctly your file path is i18n/messages/messages_es.properties
so you need to set baseName to: classpath:i18n/messages/messages

Internationalization in Spring

I have a springBoot 2.4.0 app, with this piece of code in the controller:
String defaultLocation =
messages.getMessage("home.default.location", null, LocaleContextHolder.getLocale());
In the application I see the messages from the properties loaded correctly with the messages, and no error, but in the log I see this error:
14:43:41.168 [http-nio-7080-exec-13] WARN o.s.c.s.ReloadableResourceBundleMessageSource.refreshProperties 445 - Could not parse properties file [messages_en.properties]
java.util.zip.ZipException: invalid code lengths set
at java.base/java.util.zip.InflaterInputStream.read(InflaterInputStream.java:165)
at org.springframework.boot.loader.jar.ZipInflaterInputStream.read(ZipInflaterInputStream.java:52)
at java.base/sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at java.base/sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at java.base/sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.base/java.io.InputStreamReader.read(InputStreamReader.java:185)
at java.base/java.io.Reader.read(Reader.java:229)
at java.base/java.util.Properties$LineReader.readLine(Properties.java:500)
at java.base/java.util.Properties.load0(Properties.java:415)
at java.base/java.util.Properties.load(Properties.java:378)
at org.springframework.util.DefaultPropertiesPersister.load(DefaultPropertiesPersister.java:64)
at org.springframework.context.support.ReloadableResourceBundleMessageSource.loadProperties(ReloadableResourceBundleMessageSource.java:495)
at org.springframework.context.support.ReloadableResourceBundleMessageSource.refreshProperties(ReloadableResourceBundleMessageSource.java:440)
at org.springframework.context.support.ReloadableResourceBundleMessageSource.getProperties(ReloadableResourceBundleMessageSource.java:395)
at org.springframework.context.support.ReloadableResourceBundleMessageSource.resolveCodeWithoutArguments(ReloadableResourceBundleMessageSource.java:186)
at org.springframework.context.support.AbstractMessageSource.getMessageInternal(AbstractMessageSource.java:212)
at org.springframework.context.support.AbstractMessageSource.getMessage(AbstractMessageSource.java:153)
at com.bonanza.controller.HomeController.home(HomeController.java:46)
this is my config class:
#Configuration
public class I18NConfig {
private final Environment env;
public I18NConfig(Environment env) {
this.env = env;
}
#Bean
#Qualifier("messageSource")
public ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource resourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
resourceBundleMessageSource.setBasename("classpath:i18n/messages");
resourceBundleMessageSource.setDefaultEncoding("UTF-8"); // Set the UTF-8 encoding
resourceBundleMessageSource.setCacheSeconds(1);
return resourceBundleMessageSource;
}
}
Change in your bean definition like this ...remove cache seconds and use setUSeCodeAsDefaultMessage() and give it a try.Also recheck you properties file definitions and naming references.
#Bean
#Qualifier("messageSource")
public ReloadableResourceBundleMessageSource messageSource() {
ReloadableResourceBundleMessageSource resourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
resourceBundleMessageSource.setBasename("classpath:i18n/messages");
resourceBundleMessageSource.setDefaultEncoding("UTF-8");
resourceBundleMessageSource.setUseCodeAsDefaultMessage(true);
return resourceBundleMessageSource;
}
use
org.springframework.context.support.ReloadableResourceBundleMessageSource
instead of
org.springframework.context.support.ResourceBundleMessageSource
configuration like this:
<bean name="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>classpath*:i18n/messages</value>
</property>
</bean>
or this:
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:i18n/messages");
messageSource.setCacheSeconds(10); //reload messages every 10 seconds
return messageSource;
}

spring boot thymeleaf does not work with LiteDeviceDelegatingViewResolver

I am working with spring boot 2.0.1.RELEASE, using spring-boot-starter-thymeleaf. And I have two version of pages for PC and Mobile phone.
Here is a simple version of my project structure
project structure
I would like to have the site automatically detect the PC browser and Mobile phone browser so that it can map the same requesting URL to different pages according to the type of the browsers
The html file is very simple as the following.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>This is from PC web browser</p>
</body>
</html>
Here is the code of my controller.
#Controller
public class MyController {
#RequestMapping("/")
public String mainDefault(){
return "home";
}
#RequestMapping("/home")
public String main(){
return "home";
}
}
To detect the device I wrote the following configuration class
#Configuration
public class Config implements WebMvcConfigurer {
#Autowired
private ApplicationContext applicationContext;
#Bean
public LiteDeviceDelegatingViewResolver liteDeviceDelegatingViewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setCharacterEncoding("UTF-8");
LiteDeviceDelegatingViewResolver resolver = new LiteDeviceDelegatingViewResolver(viewResolver);
resolver.setNormalPrefix("web/");
resolver.setMobilePrefix("mobile/");
resolver.setTabletPrefix("web/");
resolver.setOrder(1);
resolver.setEnableFallback(true);
return resolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addDialect(new LayoutDialect());
templateEngine.setTemplateResolver(templateResolver());
templateEngine.setEnableSpringELCompiler(true);
return templateEngine;
}
#Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(this.applicationContext);
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setPrefix("/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(".HTML5");
templateResolver.setCacheable(false);
return templateResolver;
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
DeviceResolverHandlerInterceptor deviceResolverHandlerInterceptor = new DeviceResolverHandlerInterceptor();
SitePreferenceHandlerInterceptor sitePreferenceHandlerInterceptor = new SitePreferenceHandlerInterceptor();
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
registry.addInterceptor(localeChangeInterceptor);
registry.addInterceptor(deviceResolverHandlerInterceptor);
registry.addInterceptor(sitePreferenceHandlerInterceptor);
WebMvcConfigurer.super.addInterceptors(registry);
}
}
Then when I tried to run the Application. I always get the following error. It seems it can map to the right resource which is [/templates/web/home.html]. But it always say Could not open ServletContext resource [/templates/web/home.html]
If I visit from a mobile browser it map to [/templates/mobile/home.html] which is also correct.
Can any one help me? Thank you in advance.
2018-04-19 17:18:57.040 ERROR 17732 --- [nio-9020-exec-9] org.thymeleaf.TemplateEngine : [THYMELEAF][http-nio-9020-exec-9] Exception processing template "web/home": An error happened during template parsing (template: "ServletContext resource [/templates/web/home.html]")
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "ServletContext resource [/templates/web/home.html]")
at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:235) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parseStandalone(AbstractMarkupTemplateParser.java:100) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:666) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
.........
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/templates/web/home.html]
at org.springframework.web.context.support.ServletContextResource.getInputStream(ServletContextResource.java:159) ~[spring-web-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.thymeleaf.spring5.templateresource.SpringResourceTemplateResource.reader(SpringResourceTemplateResource.java:103) ~[thymeleaf-spring5-3.0.9.RELEASE.jar:3.0.9.RELEASE]
at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:223) ~[thymeleaf-3.0.9.RELEASE.jar:3.0.9.RELEASE]
... 52 common frames omitted
2018-04-19 17:18:57.047 DEBUG 17732 --- [nio-9020-exec-9] o.s.web.servlet.DispatcherServlet : Error rendering view [org.thymeleaf.spring5.view.ThymeleafView#139a953a] in DispatcherServlet with name 'dispatcherServlet'
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "ServletContext resource [/templates/web/home.html]")
You are trying to configure something that is already being configured for you.
Make sure you have the spring-boot-starter-thymeleaf and that you have the spring-mobile-autoconfguration in your list of dependencies.
NOTE: I assume you are using the snapshot builds so that you are compatible with regards to Spring and Spring Boot version.
Remove your Config class
Add the following to your application.properties
spring.mobile.devicedelegatingviewresolver.normalPrefix=web/
spring.mobile.devicedelegatingviewresolver.tabletPrefix=web/
spring.thymeleaf
Restart your application

Confused about ThymeleafConfig in Spring Boot

I added a ThymeleafConfig to my Spring Boot application so I could configure the template mode to HTML5. Prior to adding it, the Spring Boot application could find the home.html template. After adding it now I get a:
org.thymeleaf.exceptions.TemplateInputException: Error resolving template "home", template might not exist or might not be accessible by any of the configured Template Resolvers
My directory structure is a standard resources/templates/home.html
Here is my ThmyeleafConfig:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
#Configuration
public class ThymeleafConfig {
#Bean
public ServletContextTemplateResolver defaultTemplateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode("LEGACYHTML5");
resolver.setCacheable(false);
return resolver;
}
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
I'm pretty sure I've followed what the examples are doing but there's obviously something I've missed. Any suggestions how I can fix this so it finds the templates properly?
There's no need to declare your own beans. You can configure the mode using application.properties:
spring.thymeleaf.mode=LEGACYHTML5

Resources