Recently I've started to make i18n for JSF 1.2 web-application.
Actually for this moment we need to have only two locales and instances of web-application will be deployed and configured on separate servers with same code-base.
Locale is specified at JVM level in tomcat (catalina.bat) using JAVA_OPTS, as:
-Duser.language=en -Duser.region=US
We can specify default and supported locales in faces-config.xml like this:
<locale-config>
<default-locale>en_US</default-locale>
<supported-locale>en_GB</supported-locale>
</locale-config>
But this configuration will use client browser specific locale instead of server default locale (what is we really need right now for our purposes).
So I was thinking about programmatically way to specify locale. First that came into my mind was:
FacesContext.getCurrentInstance().getViewRoot().setLocale(Locale.getDefault());
But if I understand correctly this way will set locale only for current user's request to webapp.
Right now I'm thinking about two ways:
Set locale using FacesContext after successull user login;
Specify locale configuration at the start of web-application (Not
sure how to do this in JSF).
Is there any better way to configure locale for FacesContext programmaticaly at application level?
Thanks,
Yuriy
To avoid pulling your hair, use this approach:
https://stackoverflow.com/a/12079507/1049870
I used to override PhaseListener implementing my own:
public class SessionPhaseListener implements PhaseListener {
private static final String key = "locale";
private static final String localeId = "es";
#Override
public void afterPhase(PhaseEvent event) {
java.util.Locale locale = new java.util.Locale(localeId);
PhaseId currentPhase = event.getPhaseId();
if (currentPhase == PhaseId.RESTORE_VIEW) {
viewRoot().setLocale(locale);
} else if (currentPhase == PhaseId.RENDER_RESPONSE) {
sessionMap().put(key, locale);
}
}
private Map<String, Object> sessionMap() {
return FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
}
private UIViewRoot viewRoot() {
return FacesContext.getCurrentInstance().getViewRoot();
}
}
You can create a more smart approach.
But keep in mind that, this code is called hardly.
Read this value from an Environment Variable is the best choice.
Related
In my xpage I need to set a user defined language before the page loads. the language to be set is stored in a document in the database
so I do this in beforePageLoad
var lang = getUserDoc().getItemValueString("Language")
facesContext.getViewRoot().setLocale(new java.util.Locale(lang));
context.reloadPage();
the problem is that if I do not do context.reloadPage the language is not set.
but context.reloadPage gives all kind of other problems when loading the page so
I need to find a better way.
is there anyway I can set the language of the page without reloading the page.
This doc suggests using context.setLocale/setLocaleString instead of viewRoot.setLocale. The advantage is that the context locale is used for the rest of the browser session. The context locale will be set as the viewRoot locale when any subsequent viewRoots are loaded, so you don't have to re-set the locale for every page. It does still require a context.reloadPage for the current viewRoot though, so its not exactly what you were asking for.
The doc is:
Locale use in XPages: Programmatically setting the locale
Hope I have got it correctly, just extending my answer to Per Henrik's solution here (based on the last comment), for setting the resource bundle correctly probably you can just compute it? Something like this?
<xp:this.resources>
<xp:bundle var="application">
<xp:this.src><![CDATA[#{javascript:if(context.getLocale()=="en_US")
return "/application.properties";
else
return "/application_de.properties";}]]></xp:this.src>
</xp:bundle>
</xp:this.resources>
I have just used the context variable here, but I am sure that the document variable is accessible too.
Hope this helps.
The problem is beforePageLoad runs too late - the components have already been loaded into the component tree with the relevant language.
It may work if you use a ViewHandler, as in Jesse Gallagher's Scaffolding framework on OpenNTF. You would definitely need to identify the language before the call to super.createView() though.
To use beforePageLoad, I think you would subsequently need to iterate through controls and amend the labels etc.
Setup a phase listener that sets the appropriate locale based on the user's configuration.
See this blog post by Sven Hasselbach for more details: http://hasselba.ch/blog/?p=649
I use this approach in several apps using the following phase listener based on the approach from Sven. The code reads the locale from a user bean:
public class LocalizationSetter implements PhaseListener {
private static final long serialVersionUID = -1L;
public void afterPhase(PhaseEvent event) {
}
public void beforePhase(PhaseEvent event) {
FacesContext facesContext = event.getFacesContext();
UIViewRoot view = facesContext.getViewRoot();
view.setLocale(User.get().getLocale());
}
public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}
}
I'm trying to make a program localized in Java.
package javaapplication8;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
public class LanguageController {
private final Map supportedLanguages;
private final ResourceBundle translation;
public LanguageController(String language){
supportedLanguages = new HashMap();
supportedLanguages.put("English",Locale.ENGLISH);
supportedLanguages.put("Italiano",Locale.ITALIAN);
//here I get error
translation = ResourceBundle.getBundle("language", supportedLanguages.get(language));
}
public String getWord(String keyword)
{
return translation.getString(keyword);
}
}
Than in a class I try to print a word in two different languages, italian and english. I have two proprieties file
Language.proprieties
Language_it.proprieties
In the class:
LanguageController langController_it = new LanguageController("Italiano");
System.out.println(langController_it.getWord("Option"));
LanguageController langController_en = new LanguageController("English");
System.out.println(langController_en.getWord("Option"));
EDIT: First problem solution java.util.MissingResourceException: Can't find bundle for base name 'property_file name', locale en_US
I still have error in that line supportedLanguages.get(language)
There are several problems with your application (and with your question for that matter).
First of all, you do not use parametrized collection:
private final Map supportedLanguages;
This map will always return Object, but the getBundle() method has different signature:
public static ResourceBundle getBundle(String baseName, Locale locale);
I am sure that's exactly what Netbeans is complaining about. The ugly way to fix this would be to cast the parameter to Locale:
translation = ResourceBundle.getBundle("language", (Locale) supportedLanguages.get(language));
The better way would be to use type parameters in Map declaration:
private final Map<String, Locale> supportedLanguages = new HashMap<>();
Another possible issue with your application is where you keep properties files with translations. Unfortunately, Java is extremely sensitive where it comes to file location and you have to provide the fully qualified path to a properties file. It changes a bit with Java 8 and ResourceBundle's SPI providers, but that's a different story.
Last, but not least, it seems that you are trying to implement the common anti-pattern, that is language switcher. If you are implementing desktop application, please don't do this mistake!
It is just enough to get user interface default locale:
Locale locale = Locale.getDefault(LocaleCategory.DISPLAY);
Believe it or not, but the ResourceBundle class will try to fall-back to the most appropriate language for the user. If I already have set the UI language in my Operating System preferences, why are you bothering to make a choice again?
Honestly, language switcher make sense for static web sites sometimes, but not for web applications, and definitely not for desktop applications.
I am trying to automate frontend tests with Selenium for a wicket based web application.
Therefore I have:
- Different languages
- language property files (submit.signup.form=Submit) and wicket messages () using them
- HTML pages which are generated by wicket (input type:button and value:Submit)
If I go ahead and automate a test case with that, it will work properly.
The problems start when somebody decides to change the property file to f.i. submit.signup.form=Send.
If that happens I will have to adjust all Selenium tests to check for the correct label again to make a test successful (this is not really applicalbe for that example but for error messages it will be a problem)
Now the question:
Is there a way to make wicket to put the property key onto/into the generated html files?
Desired benefit:
I can use Java and make Selenium take the property ke and check the property file for the text. That way a change of a label in the property file would not effect the Selenium tests at all and would make it much more easy to handle.
I am grateful for any answer. :)
Best regards
By default, Wicket starts in development mode. In development mode you should see the wicket tags, you should take a look in to IDebugSettings
, however you will not see the properties gathered from the java code, but you can add the key as attribute, for example
new Label(getString("propertieKey")).add(new AttributeAppender("key","propertieKey"))
It's quite easy to do actually.
Put in your application init method:
getResourceSettings().getStringResourceLoaders().add(0, new NoResourceLoader());
Implement NoResourceLoader:
public class NoResourceLoader implements IStringResourceLoader {
#Override
public String loadStringResource(Class<?> clazz, String key, Locale locale, String style, String variation) {
if ("noProperties".equals(style)) {
return key;
}
return null;
}
#Override
public String loadStringResource(Component component, String key, Locale locale, String style, String variation) {
if ("noProperties".equals(style)) {
return key;
}
return null;
}
}
This resource loader just returns the key if the style is set to noProperties. As it returns null, the localizer will try the next resourceloader for any other invocation.
In order to set style to "noProperties" I'd suggest adding a parameter check to your pages' constructor that would set the style on the session object when you call your application with the parameter.
public BasePage(PageParameters pp) {
String style = pp.get("st").toOptionalString();
if (style != null) {
getSession().setStyle("noProperties");
}
It would be enough to call your first url with this parameter set, then you should walk through the whole session with property keys instead of values in the html. I'd also disable this check when the app is running in production.
I have an interface that extends the com.google.gwt.i18n.client.Messages class, which I use for retrieving i18n messages in my GWT application. It looks like this:
public interface MyMessages extends com.google.gwt.i18n.client.Messages {
#DefaultMessage("Hello world")
#Key("message1")
String message1();
#DefaultMessage("Hello again")
#Key("message2")
String message2();
//...
}
Normally, I create an instance of it using GWT.create() like so:
private MyMessages messages = GWT.create(MyMessages.class);
However, this does not work with server-side code, only client-side code (it throws an error saying that GWT.create() is only usable in client-side code).
The answer to a similar question points to a separate library that you can download which will let you access the i18n messages on the server, but I don't want to download any extra libraries (this seems like a simple problem, there must be a simple solution).
In summary: How can I access my i18n messages in server-side code? Thanks.
On the server side you can use the standard Java localization tools like ResourceBundle.
Look here for a tutorial how to use it.
// Create a ResourceBundle out of your property files
ResourceBundle labels =
ResourceBundle.getBundle("LabelsBundle", currentLocale);
// Get localized value
String value = labels.getString(key);
The GWT specific way of creating an interface out of your property files and providing implementations via deferred binding can not be used on sever side Java.
If you are fearless and willing to spend the time, you can implement a code generation step to read your property files and generate implementation classes for your message interface. That's exactly what the Google GWT compiler does behind the scene.
I agree with Michael.. I was having this problem of trying to "localize" messages generated on the server.... but I decided to instead just throw an Exception on the server (because it is an error message which should only happen exceptionally) which contains the message code, which the client code can then look up and show the correct localized message to the user.
There's a great library for GWT internationalization gwt-dmesg. It allows you to 'share' .properties files between clent and server. However, project looks to be abandoned by author and you must recompile it manually for use with GWT versio >= 2.1.0.
GWT.create() can only be used in client-side code.
The good thing to do is that you provide your own I18NProvider class/interface, from which then you can extend to server side I18N factory and client side I18N factory read the same resource bundle.
After that you can simply use it all over your system, unify your code.
Hope that helps.
Following vanje's answer, and considering the encoding used for the properties files (which can be troublesome as ResourceBundle uses by default "ISO-8859-1", here is the solution I came up with:
import java.io.UnsupportedEncodingException;
import java.util.Locale;
import java.util.ResourceBundle;
public class MyResourceBundle {
// feature variables
private ResourceBundle bundle;
private String fileEncoding;
public MyResourceBundle(Locale locale, String fileEncoding){
this.bundle = ResourceBundle.getBundle("com.app.Bundle", locale);
this.fileEncoding = fileEncoding;
}
public MyResourceBundle(Locale locale){
this(locale, "UTF-8");
}
public String getString(String key){
String value = bundle.getString(key);
try {
return new String(value.getBytes("ISO-8859-1"), fileEncoding);
} catch (UnsupportedEncodingException e) {
return value;
}
}
}
The way to use this would be very similar than the regular ResourceBundle usage:
private MyResourceBundle labels = new MyResourceBundle("es", "UTF-8");
String label = labels.getString(key)
Or you can use the alternate constructor which uses UTF-8 by default:
private MyResourceBundle labels = new MyResourceBundle("es");
I am trying to implement internationalization in Tomcat. There are going to be different resource text files. My idea is to load all the resources in to the memory while tomcat loads.
Below is the sample code to load multiple resource in to the memory.
public class ResourceBundleLoader {
private static ResourceBundle enResourceBundle;
private static ResourceBundle frResourceBundle;
public static void loadBundle(){
Locale enLocale = new Locale("en", "US");
enResourceBundle = ResourceBundle.getBundle("MessagesBundle",enLocale);
enLocale = new Locale("fr", "FR");
frResourceBundle = ResourceBundle.getBundle("MessagesBundle",enLocale);
}
public static ResourceBundle getEnResourceBundle(){
return enResourceBundle;
}
public static ResourceBundle getFrResourceBundle(){
return frResourceBundle;
}
}
The method loadBundle is called once thru startup servlet. And getEnResourceBundle() and getFrResourceBundle() is called accordingly. Is this right way to implement/maintain internationalization in tomcat? or is there any better way?
Thanks in advance.
You dont need to make this helper class, as per the java documentation the bundles are already cached for you in memory. This will just make your code more complicated to maintain. ie You would have to alter your code every time you add a new "bundle".
Just add code like this to your servlets and/or JSP's:
//request.getLocale() returns the web browsers locale
bundle = ResourceBundle.getBundle("MessagesBundle",request.getLocale())
Just make sure you have a default message bundle file with all your text. Then you can just add extra locales at will as things get translated.
UTF-8 support
I also strongly suggest you create a servlet filter that applies to all requests that ensures that UTF-8 is turned on for both the html that is output, and the parsing of the form responses that are posted back to your application:
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
I wouldn't optimize until I knew the i18n was too slow.
But if I proceeded down your path, instead of using scalar ResourceBundles, I'd put the ResouceBundles into a Map. Now your code can use any bundle knowing the locale - which you have to select the appropriate ResourceBundle anyway.
Your code won't have any if locale is this, use English. Instead, it will be myResourceBundle = bundleMap.get(myLocale);