Thymeleaf - Populate ang get language property values [duplicate] - spring-boot

This question already has an answer here:
I18n in Spring boot + Thymeleaf
(1 answer)
Closed 9 months ago.
Circumstances
I'm using Spring Boot with Thymeleaf for populating my HTML template files and get back the result as a String. For that I used SpringTemplateEngine.
The code looks like this
Context context = new Context();
context.setVariables(myProperties);
return templateEngine.process(htmlTemplateName, context);
The problem
I want to achieve something similar, but with language.property files
I have two language property files: language.properties and language_en.properties which looks like this
my.value = This a string containing a dummy name = {name}
What I want to achieve?
I want to use thymeleaf to reach the correct language property file
Populate the {name} variable with a defined variable. The templating should be based on variable name like in HTMLs eg: hashmap: <"name", "FooName">
Get back the populated text as String. I don't have HTML file, I just want to use the templating mechanism of the thymeleaf.
Question
Is it possible and how can I do that?
What is the right formatting in language.properties if it's possible?

Yes, you can!
You need to configure a MessageSource bean with the correct basename to your message files in your resource directory, like:
spring.messages.basename=path/to/language, assuming your properties files are located at path/to/language(_en).properties
Given this bean, wherever you need a translated string, you inject an instance of MessageSource and use that to get your translated string for a given message key:
public class I18NHelper {
private final MessageSource messageSource;
public I18NHelper(final MessageSource messageSource) {
this.messageSource = messageSource;
}
public String translate(String key, String name) {
return messageSource.getMessage(key, new Object[] {name}, Locale.ENGLISH);
}
}
Edit: Fixed the class to inject and the call to getMessage.
Also, there multiple ways to get the Locale of the current Session or the System. I used the English locale as an example. Adjust to your needs.

Related

Spring Custom Configuration not populating properties with underscore

I am trying to populate Custom class with properties. following is my Custom Properties class:
#Configuration
#ConfigurationProperties(prefix = "foo.bar")
public class CustomProperties{
private String PROPERTY_ONE;
private String propertyTwo;
//setters
//getters
}
and my properties in application.properties are:
foo.bar.PROPERTY_ONE=some text
foo.bar.PROPERTY_TWO=some other text
When I am trying to use value from CustomProperties this is what I gets:
customProperties.getPROPERTY_ONE() = null
customProperties.getPopertyTwo() = some other text
So I observed that if I have variable name with underscore(_) in it not populating the property value.
is there any way to get the value with variable having underscore?
Yes, it is 100% possible to get your configuration values.
It's all about the casing! Inside of CustomProperties simply name your first property propertyOne ... and refactor your getters/setters appropriately ... and you'll be good to go!
Spring's got the camel casing going on when translating the configuration fields to your Configuration classes/properties. So instead of matching the casing of your properties, follow the camel casing equivalent of the property name found in your configuration file.
Example: PROPERTY_ONE translates to propertyOne

When do I use org.springframework.format.Printer interface?

When I use spring-mvc, I always use spring formatter to format a string to Java Bean. So, I implement org.springframework.format.Formatter interface.
Despite I implement two methods(print(),parse()), the print method has never been used. Because I just use parse()method format a String to Java Bean and never format Java Bean to String.
So, my question is, what situation does the print() will be called? Or when I need format a Java Bean to String.
Formatters can be used for parsing and printing Dates, Timestamps, and general numeric data.
This means, as you already pointed out, that we can customize the way parsing is handled for a specific type by overriding the parse() method.
It also means that we can provide custom print behaviour by overriding the print() method.
So, what's print's use case?
Let's put an example.
Suppose you have a serial number type composed of 4 segments —e.g., 1111-2222-3333-4444.
public class SerialNumber {
private int segment1;
private int segment2;
private int segment3;
private int segment4;
//Some getters and setters;
}
Now, we'll implement a Formatter class that can parse an input like the one shown above. For brevity's sake I'll avoid try-catch blocks and validation logic:
public class SerialNumberFormatter implements Formatter {
public SerialNumber parse(String input, Locale locale) {
//Some code here to validate input
//Split the input into segments
String[] result = speech.split("-");
return new SerialNumber(Integer.parseInt(result[0],
Integer.parseInt(result[1],
Integer.parseInt(result[2],
Integer.parseInt(result[3])
}
}
So, this way we managed to parse the Serial Number. But we already knew this. Now, let's suppose we'd like to show a SerialNumber stored in our DB to the end-user in the following format "SN: 1111-2222-3333-4444". We need to print the object somehow. That logic can be implemented inside the print() method.
Completing our Formatter class:
public class SerialNumberFormatter implements Formatter {
public SerialNumber parse(String input, Locale locale) {
//Some code here to validate input
//Split the input into segments
String[] result = speech.split("-");
return new SerialNumber(Integer.parseInt(result[0],
Integer.parseInt(result[1],
Integer.parseInt(result[2],
Integer.parseInt(result[3])
}
public String print(SerialNumber sn, Locale locale) {
//Some code here to validate the sn
return String.format("SN: %d-%d-&d-%d", sn.getSegment1,
sn.getSegment2,
sn.getSegment3,
sn.getSegment4)
}
}
To sum up, Formatter allows us to encapsulate logic related to parsing and printing, that otherwise would need to be coded into a business or service layer.
Remember that a Formatter can also be composed of Parser and Printer classes. So, a print() method may contain printing logic much more complicated than just building a String. It could encapsulate logic to format a POJO and write it to a file. It could also return a JSON or an XML as output.
Take it mainly as an abstraction that lets us decouple formatting logic from the rest of our code.
Formatter interface is also a core piece of the Spring Framework. You can implement custom Formatters and then register them into the framework for different tasks. You can take a look at this Baeldung's article: https://www.baeldung.com/thymeleaf-in-spring-mvc
In Section 8, you'll appreciatte how a Formatter is implement and then registered to Spring MVC to convert and show data in the front-end.

Using #Value annotation with Spring and SPeL

I am trying to find a way to do the following in my spring boot 1.5 application.
I have a variable who's value is dynamic meaning it comes in from an external system.
String name = "abc"; //gets set externally
I want to try and use the name's value to lookup my property file and see if there is a matching property defined. something like..
#Value("#{myClassName.name.concat('something')}")
String propertyValue;
Now my application.property file has the following property set
assume name has the value "abc"
property file contents:
abc.something:abcValue
Now, when i try to access the value of the variable propertyValue it gets set to the value abc.something and not abcValue.
I probably think I cannot use #Value with #{} to get to that, I was wondering if there was a way to to use #{} inside ${} so that I goes and fetches the property value after calculating the name of the property using #{}.
Let me know if you need more details please.
A bean life-cycle requires properties to be resolved at compile time. So, #Value requires constant parameter.
You can use Environment bean to access your properties programmatically.
import org.springframework.core.env.Environment;
#Service
public class Serivce {
#Autowired
private Environment environment;
public String getProperty(final String keyPart) {
String key = "build.your." + keyPart;
return environment.getProperty(key)
}
}
By the way you can use #('${spring.some.property}') in SpEL to access placeholder.
// This is valid access to property
#Value("#('${spring.some.property}')")
private String property;

Spring-web tries to find resource named with informed path variable

Using spring-web, I am mapping a method to receive a request containing dots "." on the path:
#RequestMapping(value = "download/{id:.+}", method = RequestMethod.GET, produces = "application/xls")
public String download(#PathVariable(value = "id") String id) { ... }
For example, /download/file.xls should be a valid address. But when I try to access that address, Spring returns Could not find acceptable representation as if it was trying to find a resource named file.xls.
Spring shouldn't execute download method rather than try to find a resource named as the path variable?
Obs.: my application is a spring-boot application.
Your #RequestMapping says it produces "application/xls", but your return type is a String and you haven't annotated the return type with #ResponseBody.
If you want to return an Excel spreadsheet, you need to produce that spreadsheet on the server and return it as a byte[] from your request mapping. I'm not sure how or why you'd return a String, unless you're controller is a simple #Controller and you're returning the view name.
Have you tried configuring your RequestMappingHandlerMapping
handler.setUseSuffixPatternMatch( false )
(I was configuring my RequestMappingHandlerMapping anyway, so for me I just needed to add that line - chances are you may be letting Spring Boot autoconfig that class).
See https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.html#setUseRegisteredSuffixPatternMatch-boolean-
Possibly you may need to turn off content negotiation as well - I can't remember exactly what Spring Boot default content negotiation is, but it might be affecting your case.
#Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false)
}
Worth noting that if you are working on a wider/existing application then both these configurations have possible implications more widely, so if that is the case then tread carefully!

i18n translation in JSP custom tag

Is it possible to write a custom JSP tag to take an i18n message key and output the translation phrase for the given request?
Normally in JSP/JSTL, I do:
<fmt:message key="${messageKey}"><fmt:param>arg1</fmt:param></fmt:message>
And I get the translation phrase. Now I need to do the following (there's a good reason for this):
<custom:translate key="${messageKey}" arg="arg1"/>
But I don't know how to look up the translation in the custom tag code. The TagSupport base class provides a pageContext from which I can get a ServletRequest which has the Locale... but how do I then look up the translation for a key?
I use Spring 3.0 and in my application-context.xml, I've defined a ReloadableBundleMessageSource so I can call:
messageSource.getMessage(
key, new Object[] {arg}, pageContext.getRequest().getLocale()
);
but I don't think I can inject messageSource into a custom tag, can I? Otherwise I can instantiate a new one, but would it load my tens of thousands of translations for every call? I don't want to resort to making messageSource a static member of a static class.
I don't do Spring, but in "plain" JSP you can just put the ResourceBundle instance in the session scope with help of a Filter or Servlet
ResourceBundle bundle = ResourceBundle.getBundle(basename, request.getLocale());
request.getSession().setAttribute("bundle", bundle);
And treat it in JSP like any other bean in EL.
${bundle[messageKey]}
It must be possible to have Spring to put that as a bean in the session scope.
There is a utility in spring to access the web application context. Then you can look up a bean by its name or type.
To get hold of your resource bundle, you could do something like:
WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(pageContext.getServletContext());
messageResource = springContext.getBean("messageResource");
This question is very old but I think it is worth to share another way to solve this.
To access the Spring message source in a custom tag you only need to extend the class org.springframework.web.servlet.tags.RequestContextAwareTag instead of TagSupport.
In this case you have to implement the method doStartTagInternal() instead of doStartTag(), but inside this method you will have access to the MessageSource through getRequestContext().getMessageSource() method.
Therefore, your class would look like:
public class CreateCustomFieldTag extends RequestContextAwareTag{
//variables (key, arg...), getters and setters
#Override
protected int doStartTagInternal() throws Exception {
getRequestContext().getMessageSource().getMessage(
key, new Object[] {arg}, getRequestContext().getLocale());
}
}

Resources