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);
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;
}
}
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.
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.
The gettext-tutorial states that
The preferable way to create an I18n object is through the I18nFactory. The factory caches the I18n object internally using the the package name of the provided class object and registers it with I18nManager. Thus all classes of the same package will use the same I18n instance.
But running inside an HttpServlet (actually an VelocityViewServlet in my particular case) it seems to me that if I change the locale on the cached object it could stay like that for subsequent requests and that is not desirable because each request must be served with particular locales.
So the question is, should I instead create ad-hoc i18n objects on a per-request basis, with the specific locale of that request? I think that would be a performance penalty, so should I instead create my own manager and select the appropriate instance based on the request's locale?
Any comments greatly appreciated. There doesn't seem to be much doc or examples besides the tutorial and the Javadoc.
Update: There is one thing I tried and seems to work. Instead of declaring a private static member like the tutorial says, I just call the factory method on each request, using this special version of the I18nFactory:
getI18n(java.lang.Class clazz, java.lang.String bundleName, java.util.Locale locale)
This should get a cached instance of that particular locale (or force the load on the first request for that locale), avoiding the performance penalties on subsequent requests for the same one. If anyone can confirm this make sense:
public class Foo extends VelocityViewServlet {
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) {
Locale reqLocale = request.getLocale();
I18n i18n = I18nFactory.getI18n(Foo.class, "app.i18n.Messages", reqLocale);
String localized = i18n.tr("Bar");
...
}
...
}
Again, I've tested this and it's working but I'd like to know if it's a correct implementation of gettext-commons for this type of application.
Thanks in advance,
--
Alejandro Imass
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");