Something basic about Java pointers and changing locale in JSF application - java

I just tried to implement JSF Internationalization based on this article - "Internationalization in JSF with UTF-8 encoded properties files" and found something weird. Is it right way to change locale by using code in this bean?
#ManagedBean
#SessionScoped
public class LocaleBean {
private Locale locale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
public Locale getLocale() {
return locale;
}
public String getLanguage() {
return locale.getLanguage();
}
public void setLanguage(String language) {
this.locale = new Locale(language);
}
}
As I understand Java private Locale locale must be pointer to actual Locale object from viewRoot object but this method didn't work at me. Instead, when I changed setLanguage(String language) method to this
public void setLanguage(String language) {
FacesContext.getCurrentInstance().getViewRoot().setLocale(new Locale(language));
}
it began to work. Now I wonder where is mistake? What's wrong with #BulusC code? Maybe I did something wrong, maybe I forget something?
When I debugged I seen that private Locale locale and locale object from viewRoot are different objects.

Indeed, the code was missing the line you've posted. From this question: Localization in JSF, how to remember selected locale per session instead of per request/view, answered by BalusC, you can check the code for setLanguage (code taken from BalusC's answer, not mine):
public void setLanguage(String language) {
locale = new Locale(language);
//this is the line you added
FacesContext.getCurrentInstance().getViewRoot().setLocale(locale);
}

Related

Missing functionality between Accept-Language header and ResourceBundle

Google foo failed me. I want to find out if there is a standard "by the book" way of transforming the input locales from Accept-Language header to correct ResourceBundle.
ResourceBundle::getBundle() method(s) accepts a single locale but Accept-Language can have multiple locales weighted by index, eg: de;q=1.0, sl;q=0.9.
Current code:
#Context
private HttpServletRequest request;
public String getString(String key) {
ResourceBundle i18n = ResourceBundle.getBundle("locale/strings", this.request.getLocale());
return i18n.getString(key);
}
The problem is that getLocale() returns the preferred locale, in this case de. If available resource bundles are sl and en, this will try to find de and then fallback to en, but the actual expected result by the client is sl!
My question is basically, do I have to implement a custom fallback code that iterates over HttpServletRequest.getLocales() (I don't want to reinvent the wheel..) or is there a more standard and straightforward way of doing this? I'd also settle for some 3rd party lib that fills this gap.
Custom solution so far:
#RequestScoped
public class Localization {
#Context
private HttpServletRequest request;
private ResourceBundle i18n;
#PostConstruct
void postConstruct() {
//List of locales from Accept-Language header
List<Locale> locales = Collections.list(request.getLocales());
if (locales.isEmpty()) {
//Fall back to default locale
locales.add(request.getLocale());
}
for (Locale locale : locales) {
try {
i18n = ResourceBundle.getBundle("bundles/translations", locale);
if (!languageEquals(i18n.getLocale(), locale)) {
//Default fallback detected
//The resource bundle that was returned has different language than the one requested, continue
//Only language tag is checked, no support for detecting different regions in this sample
continue;
}
break;
}
catch (MissingResourceException ignore) {
}
}
}
private boolean languageEquals(Locale first, Locale second) {
return getISO2Language(first).equalsIgnoreCase(getISO2Language(second));
}
private String languageGetISO2(Locale locale) {
String[] localeStrings = (locale.getLanguage().split("[-_]+"));
return localeStrings[0];
}
public ResourceBundle i18n() {
return this.i18n;
}
}
I would write an Interceptor, there you can set the language you want and apply the logic you want into a ThreadLocal or pass it down.
i.e you check against the available languages and define an order or set a default.
If you use Spring, you could then set LocaleContextHolder manualy or use the LocaleContextResolver instead of writing a own interceptor.

How can I define an enumerator with string values in JDL?

It is possible to define an enumerator with values in String in JDL as follows and with result in Java and Angular
JDL may be
enum LocaleLanguage {
ES_ES("es_ES"), ES_CA("es_CA"), EN_GB("en_GB"), EN_US("en_US")
loacle String
}
Java result may be
public enum LocaleLanguage
{
ES_ES("es_ES"),
ES_CA("es_CA"),
EN_GB("en_GB"),
EN_US("en_US");
private String locale;
LanguageLocale(String locale) {
this.locale = locale;
}
public String getLocale() {
return locale;
}
}
Angular
export const enum LocaleLanguage {
ES_ES = 'es_ES',
ES_CA = 'es_CA',
EN_GB = 'en_GB',
EN_US = 'en_US'
}
At this time this is not possible. A way to circumvent this issue would be to use translations and in the front end change the value to the desired String.

How can I bring Wicket 7 to work with java.time from Java 8?

I have lots of beans and all use LocalDate and LocalDateTime. The DateTextField in Wicket and all other widgets (like the DatePicker) only work on java.util.Date. Is there any way to inject a converter into Wicket 7 so that it uses LocalDate or LocalDateTime?
The beans look like this:
public class SomeBean {
Long id = null;
LocalDate since = null;
// plus getters and setters
}
A Wicket form currently uses a CompoundPropertyModel
CompoundPropertyModel<SomeBean> model = new CompundPropertyModel<>( bean );
You can wrap your LocalDate and etc. models in a IModel<java.util.Date>, e.g.
public static class LocalDateModel implements IModel<java.util.Date> {
private IModel<LocalDate> localDateModel;
public LocalDateModel(IModel<LocalDate> localDateModel){
this.localDateModel = localDateModel;
}
#Override
public Date getObject() {
return convertLocalDateToUtilDateSomehow(localDateModel.getObject());
}
#Override
public void setObject(Date object) {
localDateModel.setObject(convertUtilDateToLocalDateSomehow(object));
}
#Override
public void detach() {
localDateModel.detach();
}
}
If you then feed models like this into the form components you want to use it should work just fine.
If you want your CompoundPropertyModel to automatically provide such wrapping models, you need to extend it and override it's CompoundPropertyModel#wrapOnInheritance(Component component) method to infer that a wrapping model is needed. Something like
#Override
public <C> IWrapModel<C> wrapOnInheritance(Component component)
{
IWrapModel<C> actualModel = super.wrapOnInheritance(component);
if (actualModel.getObject() instanceOf LocalDate) {
return new LocalDateModelButAlsoWrapping(actualModel);
} else {
return actualModel;
}
}
Where LocalDateModelButAlsoWrapping is unsurprisingly just an extension of LocalDateModel example above but which also implements IWrapModel<T>.
If you use this extension instead of your regular CompoundPropertyModel it would detect when fields are LocalDate and provide models to components (like your DateTextField) that are wrapped to look like java.util.Date models.
The code snippet I gave you is rather dirty though (you should probably not get the model object to infer its type) as I have only provided it to illustrate the general mechanism, so I suggest you devise your own way to infer the type of object expected (e.g. you can check if the Component argument is a DateTextField), but this is the general direction of the solution that I can imagine.
You can register your own converters:
https://ci.apache.org/projects/wicket/guide/7.x/guide/forms2.html#forms2_3
#Override
protected IConverterLocator newConverterLocator() {
ConverterLocator defaultLocator = new ConverterLocator();
defaultLocator.set(Pattern.class, new RegExpPatternConverter());
return defaultLocator;
}
Related: https://issues.apache.org/jira/browse/WICKET-6200
You can simply backport the converter classes from Wicket 8. You'll find these attached to this commit: https://issues.apache.org/jira/browse/WICKET-6200 (AbstractJavaTimeConverter and whatever subclasses you need for LocalDate, LocalDateTime, LocalTime, etc.)
Of course, that will not help with a DateTextField, because that has the Date type parameter hardcoded. For such, you can either create your own subclasses using the above converters, or use regular Label and TextField, with converters registered globally, as shown below:
#Override
protected IConverterLocator newConverterLocator() {
ConverterLocator converterLocator = new ConverterLocator();
converterLocator.set(LocalDateTime.class, new LocalDateTimeConverter());
converterLocator.set(LocalDate.class, new LocalDateConverter());
converterLocator.set(LocalTime.class, new LocalDateConverter());
return converterLocator;
}

Spring MVC: how to display formatted date values in JSP EL

Here's a simple value bean annotated with Spring's new (as of 3.0) convenience #DateTimeFormat annotation (which as I understand replaces the pre-3.0 need for custom PropertyEditors as per this SO question):
import java.time.LocalDate;
import org.springframework.format.annotation.DateTimeFormat;
public class Widget {
private String name;
#DateTimeFormat(pattern = "MM/dd/yyyy")
private LocalDate created;
// getters/setters excluded
}
When biding the values from a form submission to this widget, the date format works flawlessly. That is, only date strings in the MM/dd/yyyy format will convert successfully to actual LocalDate objects. Great, we're halfway there.
However, I would also like to be able to also display the created LocalDate property in a JSP view in the same MM/dd/yyyy format using JSP EL like so (assuming my spring controller added a widget attribute to the model):
${widget.created}
Unfortunately, this will only display the default toString format of LocalDate (in yyyy-MM-dd format). I understand that if I use spring's form tags the date displays as desired:
<form:form commandName="widget">
Widget created: <form:input path="created"/>
</form:form>
But I'd like to simply display the formatted date string without using the spring form tags. Or even JSTL's fmt:formatDate tag.
Coming from Struts2, the HttpServletRequest was wrapped in a StrutsRequestWrapper which enabled EL expressions like this to actually interrogate the OGNL value stack. So I'm wondering if spring provide something similar to this for allowing converters to execute?
EDIT
I also realize that when using spring's eval tag the date will display according the pattern defined in the #DateTimeFormat annotation:
<%# taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<spring:eval expression="widget.created"/>
Interestingly, when using a custom PropertyEditor to format the date, this tag does NOT invoke that PropertyEditor's getAsText method and therefore defaults to the DateFormat.SHORT as described in the docs. In any event, I'd still like to know if there is a way to achieve the date formatting without having to use a tag--only using standard JSP EL.
You may use the tag to provide you these kind of formattings, such as money, data, time, and many others.
You may add on you JSP the reference:
<%# taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
And use the formatting as:
<fmt:formatDate pattern="yyyy-MM-dd" value="${now}" />
Follows below a reference:
http://www.tutorialspoint.com/jsp/jstl_format_formatdate_tag.htm
To precise Eduardo answer:
<%# taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<fmt:formatDate pattern="MM/dd/yyyy" value="${widget.created}" />
I also prefer to not do any formatting via tags. I realise this may not be the solution you are looking for and are looking for a way to do this via spring annotations. Nevertheless, In the past I've used the following work around:
Create a new getter with the following signature:
public String getCreatedDateDisplay
(You can alter the name of the getter if you prefer.)
Within the getter, format the created date attribute as desired using a formatter such as SimpleDateFormat.
Then you can call the following from your JSP
${widget.createDateDisplay}
I was dispirited to learn that spring developers have decided not to integrate Unified EL (the expression language used in JSP 2.1+) with Spring EL stating:
neither JSP nor JSF have a strong position in terms of our development focus anymore.
But taking inspiration from the JIRA ticket cited, I created a custom ELResolver which, if the resolved value is a java.time.LocalDate or java.time.LocalDateTime, will attempt to pull the #DateTimeFormat pattern value in order to format the returned String value.
Here's the ELResolver (along with the ServletContextListener used to bootstrap it):
public class DateTimeFormatAwareElResolver extends ELResolver implements ServletContextListener {
private final ThreadLocal<Boolean> isGetValueInProgress = new ThreadLocal<>();
#Override
public void contextInitialized(ServletContextEvent event) {
JspFactory.getDefaultFactory().getJspApplicationContext(event.getServletContext()).addELResolver(this);
}
#Override
public void contextDestroyed(ServletContextEvent sce) {}
#Override
public Object getValue(ELContext context, Object base, Object property) {
try {
if (Boolean.TRUE.equals(isGetValueInProgress.get())) {
return null;
}
isGetValueInProgress.set(Boolean.TRUE);
Object value = context.getELResolver().getValue(context, base, property);
if (value != null && isFormattableDate(value)) {
String pattern = getDateTimeFormatPatternOrNull(base, property.toString());
if (pattern != null) {
return format(value, DateTimeFormatter.ofPattern(pattern));
}
}
return value;
}
finally {
isGetValueInProgress.remove();
}
}
private boolean isFormattableDate(Object value) {
return value instanceof LocalDate || value instanceof LocalDateTime;
}
private String format(Object localDateOrLocalDateTime, DateTimeFormatter formatter) {
if (localDateOrLocalDateTime instanceof LocalDate) {
return ((LocalDate)localDateOrLocalDateTime).format(formatter);
}
return ((LocalDateTime)localDateOrLocalDateTime).format(formatter);
}
private String getDateTimeFormatPatternOrNull(Object base, String property) {
DateTimeFormat dateTimeFormat = getDateTimeFormatAnnotation(base, property);
if (dateTimeFormat != null) {
return dateTimeFormat.pattern();
}
return null;
}
private DateTimeFormat getDateTimeFormatAnnotation(Object base, String property) {
DateTimeFormat dtf = getDateTimeFormatFieldAnnotation(base, property);
return dtf != null ? dtf : getDateTimeFormatMethodAnnotation(base, property);
}
private DateTimeFormat getDateTimeFormatFieldAnnotation(Object base, String property) {
try {
if (base != null && property != null) {
Field field = base.getClass().getDeclaredField(property);
return field.getAnnotation(DateTimeFormat.class);
}
}
catch (NoSuchFieldException | SecurityException ignore) {
}
return null;
}
private DateTimeFormat getDateTimeFormatMethodAnnotation(Object base, String property) {
try {
if (base != null && property != null) {
Method method = base.getClass().getMethod("get" + StringUtils.capitalize(property));
return method.getAnnotation(DateTimeFormat.class);
}
}
catch (NoSuchMethodException ignore) {
}
return null;
}
#Override
public Class<?> getType(ELContext context, Object base, Object property) {
return null;
}
#Override
public void setValue(ELContext context, Object base, Object property, Object value) {
}
#Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
return true;
}
#Override
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
return null;
}
#Override
public Class<?> getCommonPropertyType(ELContext context, Object base) {
return null;
}
}
Register the ELResolver in web.xml:
<listener>
<listener-class>com.company.el.DateTimeFormatAwareElResolver</listener-class>
</listener>
And now when I have ${widget.created} in my jsp, the value displayed will be formatted according to the #DateTimeFormat annotation!
Additionally, if the LocalDate or LocalDateTime object is needed by the jsp (and not just the formatted String representation), you can still access the object itself using direct method invocation like: ${widget.getCreated()}

messages.properties taken from db [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Design question regarding Java EE entity with multiple language support
I'm working on i18n of JSF application.
I need all standard jsf messages that are usually located in messages.properties to be taken from database. Is there some simple way to do it?
Thanks.
I think I found the answer:
public class DBMessagesBundle extends ResourceBundle {
#Override
protected String handleGetObject(String key){
...
}
#Override
public Enumeration<String> getKeys() {
...
}
}
and in FacesConfig.xml
<application>
...
<message-bundle>mypackage.DBMessagesBundle</message-bundle>
</application>
Thank You for help.
First, you will need your own MessageSource. Take a look at AbstractMessageSource and extend it:
public class CustomResourceBundleMessageSource extends AbstractMessageSource {
#Autowired
LocalizationStore localizationStore;
#Override
protected MessageFormat resolveCode(String code, Locale locale){
MessageFormat messageFormat = null;
ResourceBundle bundle = localizationStore.getBundle(locale);
try {
messageFormat = getMessageFormat(bundle, code, locale);
} catch (MissingResourceException | NullPointerException ex) {
//Return just the code in case this is used for logging, or throw, your choice
return createMessageFormat(code, locale);
}
return messageFormat;
}
protected MessageFormat getMessageFormat(ResourceBundle bundle, String code, Locale locale) throws MissingResourceException {
String msg = bundle.getString(code);
return createMessageFormat(msg, locale);
}
}
Your store must return a ResourceBundle:
This will largely be based off your db model. I would recommend using #Cachable on the getBundle() method, as your localizations are not likely to change often, and depending on your DB model, it may be expensive. The object returned needs only to implement the following methods for ResourceBundle:
public class DBResourceBundle extends ResourceBundle {
#Override
protected String handleGetObject(String key){
...
}
#Override
public Enumeration<String> getKeys() {
...
}
}
Finally, you need to register your MessageSource bean in your configuration:
<bean id="messageSource" class="com.example.CustomResourceBundleMessageSource"/>

Categories