GWT I18N on the server side - java

What is the best way to implement GWT Server Side Internationalization?
Use native Java properties files (not sure how to read and how to locate the right language file) (unicode string need to be ASCII encoded)
Use GWTI18N.java - GWT module which gives you seamless use of GWT I18N on both the client and the server and uses "java.lang.reflect.Proxy method"
Use Kotori I18N - ...
Other ideas?
How can I find and pass localization from client to sever?
On the server side I have an Servlet which still doesn't use any GWT dependant source, is it better not to do so?

I found this solution and it looks very good
gwt-i18n-server - Provides a simple support of gwt i18n feature on the server side
The aim is to permit to the GWT developer to use their Constants and Messages interfaces on the server side (See internationzation). The implementation is based on java reflect api. It loads the properties files from the classpath (same folder than the interface). It supports Constants, ConstantsWithLookup, Messages (plural too). The licence is LGPL.
Client current locale can be found this way:
LocaleInfo.getCurrentLocale().getLocaleName()

Following other threads here in SO, I came up with this solution that also considers the encoding used for the properties files (which can be troublesome as ResourceBundle uses by default "ISO-8859-1"):
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");

Related

Validating BCP-47 language tag using Java APIs

As far as I know, the only way to validate whether a given BCP-47 language tag is valid is to use the following idiom:
private static boolean isValid(String tag) {
try {
new Locale.Builder().setLanguageTag(tag).build();
return true;
} catch (IllformedLocaleException e) {
return false;
}
}
However, the downside of this approach is that setLanguageTag throws an exception which has noticeable (in a profile) performance overhead in workloads where locales are checked often.
The setLanguageTag function is implemented using sun.util.locale APIs, and as far as I can tell it's the only place where sun.util.locale.ParseStatus is checked.
What I would like to be able to do is to use a method which has the following semantics:
import sun.util.locale.LanguageTag;
import sun.util.locale.ParseStatus;
private static boolean isValid(String tag) {
ParseStatus sts = new ParseStatus();
LanguageTag.parse(tag, sts);
return !sts.isError();
}
However, it's not possible to check the locale in the above way since it's using sun.* classes directly since it requires additional JDK options to export sun.util.locale from the java.base module.
Is there a way to validate a language tag without using private sun.* APIs while being consistent with the implementation of sun.util.locale.LanguageTag#parse?
The simplest solution should be:
boolean isValidBCP47 = "und".equals(Locale.forLanguageTag(tag))
The best solution is to make use of java.util.Locale's filtering to handle this for you.
Often we need to fallback to locales. For example; en-JP is what you want for English Speakers visiting a theme park in Japan. However; when en-JP is not present you likely want to fall back to just en. In addition, your platform likely doesn't support every locale and would require a check against a list of supported locales.
Using com.glide.Locale you can do the following:
ArrayList<Locale.LanguageRange> priorityList = new ArrayList<>();
priorityList.add(new Locale.LanguageRange("en-JP"));
priorityList.add(new Locale.LanguageRange("joking")); // invalid tag
priorityList.add(new Locale.LanguageRange("fr")); // unsupported tag
priorityList.add(new Locale.LanguageRange("en"));
ArrayList<String> supportedTags = new ArrayList<>();
supportedTags.add("ja");
supportedTags.add("en-JP");
supportedTags.add("en");
Locale.filterTags(priorityList, supportedTags, Locale.FilteringMode.AUTOSELECT_FILTERING);
// returns ArrayList ["en-JP", "en"]

Using Eclipse externalized Strings - how to get a String in language XYZ?

In our Eclipse RCP application, all Strings are externalized and present in 2 languages, which works just fine.
E.g. I got a Messages.java like this:
public class Messages extends NLS
{
private static final String BUNDLE_NAME =
"abc.fixedcolumns.messages"; //$NON-NLS-1$
public static String additionalReq;
static
{
// initialize resource bundle
NLS.initializeMessages( BUNDLE_NAME, Messages.class );
}
private Messages()
{
super();
}
With the matching messages_de.properties and messages_en.properties.
It works fine, starting the application in english shows the english strings and in german shows the german ones.
Now I need to get some English strings in the german version.
How can this be accomplished?
(One attempt was to change the Locale of the JVM, get the string and change it back, but this would be a very bad solution)
You don't have to put all your messages in the language specific properties file. NLS will also look in a messages.properties file. You could put the English only messages there and not put anything in the language specific properties.
Other than that NLS does not support doing a lookup for anything other than the current locale. You could make your own version of the class to do what you want, NLS is not very big.
Eclipse e4 RCPs do support more flexible translation systems.

GWT - impossible to find working dir with Eclipse

I need to show on my panel the working dir.
I use String value = System.getProperty("user.dir"). Afterwards i put this string on label but I receive this message on console:
The method getProperty(String, String) in the type System is not applicable for the arguments (String).
I use eclipse.
Issue
I am guessing you have not gone through GWT 101 - You cannot blindly use JAVA CODE on client side.
Explanation
You can find the list of classes and methods supported for GWT from JAVA.
https://developers.google.com/web-toolkit/doc/latest/RefJreEmulation
For System only the following are supported.
err, out,
System(),
arraycopy(Object, int, Object, int, int),
currentTimeMillis(),
gc(),
identityHashCode(Object),
setErr(PrintStream),
setOut(PrintStream)
Solution
In your case Execute System.getProperty("user.dir") in your server side code and access it using RPC or any other server side gwt communication technique.
System.getProperty("key") is not supported,
but System.getProperty("key", "default") IS supported, though it will only return the default value as there is not system properties per se.
If you need the working directory during gwt compile, you need to use a custom linker or generator, grab the system property at build time, and emit it as a public resource file.
For linkers, you have to export an external file that gwt can download and get the compile-time data you want. For generators, you just inject the string you want into compiled source.
Here's a slideshow on linkers that is actually very interesting.
http://dl.google.com/googleio/2010/gwt-gwt-linkers.pdf
If you don't want to use a linker and an extra http request, you can use a generator as well, which is likely much easier (and faster):
interface BuildData {
String workingDirectory();
}
BuildData data = GWT.create(BuildData.class);
data.workingDirectory();
Then, you need to make a generator:
public class BuildDataGenerator extends IncrementalGenerator {
#Override
public RebindResult generateIncrementally(TreeLogger logger,
GeneratorContext context, String typeName){
//generator boilerplate
PrintWriter printWriter = context.tryCreate(logger, "com.foo", "BuildDataImpl");
if (printWriter == null){
logger.log(Type.TRACE, "Already generated");
return new RebindResult(RebindMode.USE_PARTIAL_CACHED,"com.foo.BuildDataImpl");
}
SourceFileComposerFactory composer =
new SourceFileComposerFactory("com.foo", "BuildDataImpl");
//must implement interface we are generating to avoid class cast exception
composer.addImplementedInterface("com.foo.BuildData");
SourceWriter sw = composer.createSourceWriter(printWriter);
//write the generated class; the class definition is done for you
sw.println("public String workingDirectory(){");
sw.println("return \""+System.getProperty("user.dir")+"\";");
sw.println("}");
return new RebindResult(RebindMode.USE_ALL_NEW_WITH_NO_CACHING
,"com.foo.BuildDataImpl");
}
}
Finally, you need to tell gwt to use your generator on your interface:
<generate-with class="dev.com.foo.BuildDataGenerator">
<when-type-assignable class="com.foo.BuildData" />
</generate-with>

How do I mimic HybridUrlCodingStrategy in Wicket 1.5?

We have an existing Java Wicket 1.4 application which uses the HybridUrlCodingStrategy extensively:
mount(new HybridUrlCodingStrategy("/myurl", MyPage.class));
This results in our URL's looking like:
http://host/myurl/paramName1/paramValue1/paramName2/paramValue2
I would like to maintain this URL format in Wicket 1.5, however the HybridUrlCodingStrategy has been removed. In wicket 1.5, pages are mounted as:
mountPage("/myurl", MyPage.class);
Which results in traditional URLs like:
http://host/myurl?paramName1=paramValue2&paramName2=paramValue2
I have read that we should be using the MountedMapper class, but looking at the Wicket 1.5 examples, API docs, and source code, it is still not clear to me how to get the same behavior with MountedMapper as we are getting with the HybridUrlCodingStrategy.
Does anyone know how to do this?
Maybe something like this:
mountPage("/myurl/paramName1/${paramValue1}/paramName2/${paramValue2}", MyPage.class)
would work? Granted, you'd have to manually specify your parameters, which could be a lot more work.
The MountedMapper class javadoc explains how to use parameters.
The other option I can think of would be (Note: this is untested):
class MyPageParametersEncoder implements IPageParametersEncoder() {
public PageParameters decodePageParameters(Request request)
{
PageParameters parameters = new PageParameters();
int i = 0;
for (Iterator<String> segment = request.getUrl().getSegements().iterator(); segment.hasNext()) {
String key = segment.next();
String value = segment.next();
parameters.add(key, value);
}
return parameters.isEmpty() ? null : parameters;
}
public Url encodePageParameters(PageParameters pageParameters)
{
Url url = new Url();
for (PageParemeters.NamedPair pair : pageParameters.getAllNamed() {
url.getSegments().add(pair.getKey());
url.getSegments().add(pair.getValue());
}
return url;
}
}
mount(new MountedMapper("/myurl/", MyPage.class, new MyPageParametersEncoder());
No need of custom IPageParametersEncoder.
With mountPage("/myurl/paramName1/${paramValue1}/paramName2/${paramValue2}", MyPage.class) the URL will look like in 1.4 but the values will be reachable as StringValue value1 = parameters.get("paramValue1"). Similar for value2.
With mountPage("/myurl/${paramValue1}/${paramValue2}", MyPage.class) is the same according to extracting the values, just shorter URL will be used.
It also supports optional paramaters - #{optionalValue3}.
NOTE: A new class has been added to Wicket 1.5.2 for backwards compatibility with 1.4 style URL encoding. It's called UrlPathPageParametersEncoder - use that if you're migrating a wicket 1.4 app to 1.5 and you have bookmarkable page links of the style:
www.mysite.com/name1/value1/name2/value2
We had the exact same issue when migrating from 1.4 to 1.5. Any 1.4 app that has been live for a while would likely have a collection of links pointing to it from external sites on the web. You really want the Wicket 1.5 version of your app to be able to handle these existing hybrid links without generating an error.
When migrating to 1.5, without a 1.4 compatible IPageParametersEncoder implementation, you need to include the full parameter specification in every mount if you want to avoid making changes to each individual Page class that reads parameters. The implementation below means that is no longer necessary. Just mount the page as livid suggests above.
I'm submitting this .java file as a patch to the Wicket devs - they may include it in Wicket in the future to make it easy to implement backwards compatible URL parameter encoding for other 1.4 migrators.
I took luniv's sample code above and made a few small changes to get it compiling/working. The following should work as a parameter encoder to provide 1.4.x style parameter encoding in 1.5.
package org.apache.wicket.request.mapper.parameter;
import java.lang.*;
import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder;
import java.util.Iterator;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.mapper.parameter.PageParameters;
public
class HybridPageParametersEncoder implements IPageParametersEncoder
{
/**
* Encodes a URL in the form:
*
* /mountpoint/paramName1/paramValue1/paramName2/paramValue2
*
* (i.e. a URL using the pre wicket 1.5 Hybrid URL strategy)
*/
public Url encodePageParameters(PageParameters pageParameters)
{
Url url = new Url();
for (PageParameters.NamedPair pair : pageParameters.getAllNamed())
{
url.getSegments().add(pair.getKey());
url.getSegments().add(pair.getValue());
}
return url;
}
/**
* Decodes a URL in the form:
*
* /mountpoint/paramName1/paramValue1/paramName2/paramValue2
*
* (i.e. a URL using the pre wicket 1.5 Hybrid URL strategy)
*/
public PageParameters decodePageParameters(Request request)
{
PageParameters parameters = new PageParameters();
int i = 0;
for (Iterator<String> segment = request.getUrl().getSegments().iterator(); segment.hasNext(); )
{
String key = segment.next();
String value = segment.next();
parameters.add(key, value);
}
return parameters.isEmpty() ? null : parameters;
}
}

What happened to URIUtil.encodePath from commons-httpclient-3.1?

I want to do what's described in question 724043, namely encode the path components of a URI. The class recommended to do that is URIUtil from Commons HttpClient 3.1. Unfortunately, that class seems to have disappeared from the most recent version of HttpClient. A similarly named class from HttpClient 4.1, URIUtils, doesn't provide the same functionality. Has this class/method been moved to some other library that I'm not aware of or is it just gone? Am I best off just copying the class from the 3.1 release into my code or is there a simpler way?
The maintainers of the module have decreed that you should use the standard JDK URI class instead:
The reason URI and URIUtils got replaced with the standard Java URI was
very simple: there was no one willing to maintain those classes.
There is a number of utility methods that help work around various
issues with the java.net.URI implementation but otherwise the standard
JRE classes should be sufficient, should not they?
So, the easiest is to look at the source of encodePath from the 3.1 release and duplicate what it does in your own code (or just copy the method/class into your codebase).
Or you could go with the accepted answer on the question you referred to (but it seems you have to break the URL into parts first):
new URI(
"http",
"search.barnesandnoble.com",
"/booksearch/first book.pdf",
null).toString();
This can be achieved using org.apache.http.client.utils.URIBuilder utility in httpclient-4.X () as follows.
public static String encodePath(final String path) {
if(path.length() == 0)
return "";
else
return new URIBuilder().setPath(path).toString();
}
You can use Standard JDK functions, e.g.
public static String encodeURLPathComponent(String path) {
try {
return new URI(null, null, path, null).toASCIIString();
} catch (URISyntaxException e) {
// do some error handling
}
return "";
}

Categories