How to implement i18n in GWT application? - gwt

I have a problem with internationalization. I'm trying to implement support two languages ​​in my GWT application. Unfortunately I never found a complete example how to do it with the help of UiBinder. That is what I did:
My module I18nexample.gwt.xml:
<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='i18nexample'>
<inherits name="com.google.gwt.user.User" />
<inherits name='com.google.gwt.user.theme.clean.Clean' />
<inherits name="com.google.gwt.i18n.I18N" />
<inherits name="com.google.gwt.i18n.CldrLocales" />
<entry-point class='com.myexample.i18nexample.client.ExampleI18N' />
<servlet path="/start" class="com.myexample.i18nexample.server.StartServiceImpl" />
<extend-property name="locale" values="en, fr" />
<set-property-fallback name="locale" value="en" />
</module>
My interface Message.java:
package com.myexample.i18nexample.client;
import com.google.gwt.i18n.client.Constants;
public interface Message extends Constants {
String greeting();
}
The same package com.myexample.i18nexample.client has three properties file:
Message.properties:
greeting = hello
Message_en.properties:
greeting = hello
Message_fr.properties:
greeting = bonjour
My UiBinder file Greeting.ui.xml:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder
xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
ui:generateFormat="com.google.gwt.i18n.rebind.format.PropertiesFormat"
ui:generateKeys="com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator"
ui:generateLocales="default" >
<ui:with type="com.myexample.i18nexample.client.Message" field="string" />
<g:HTMLPanel>
<ui:msg key="greeting" description="greeting">Default greeting</ui:msg>
</g:HTMLPanel>
</ui:UiBinder>
When the application starts, I always get the output in the browser:
Default greeting
Why? What am I doing wrong?
I tried to run the application from different URL:
http://127.0.0.1:8888/i18nexample.html?gwt.codesvr=127.0.0.1:9997
http://127.0.0.1:8888/i18nexample.html?locale=en&gwt.codesvr=127.0.0.1:9997
http://127.0.0.1:8888/i18nexample.html?locale=fr&gwt.codesvr=127.0.0.1:9997
The result does not change. Although I expected in last case a message bonjour.
If for example I use a g:Buttton instead of the message ui:msg:
<g:HTMLPanel>
<g:Button text="{string.greeting}" />
</g:HTMLPanel>
Then I get as a result of the button with text "hello"
And if I enter the URL:
http://127.0.0.1:8888/i18nexample.html?locale=fr&gwt.codesvr=127.0.0.1:9997
The text on the button changes to "bonjour". Here everything works as expected. But why internationalization is not working in my first case?
And whether there is a difference between the following:
<ui:msg description="greeting">Default greeting</ui:msg>
<ui:msg description="greeting">hello</ui:msg>
<ui:msg description="greeting"></ui:msg>
Should there be different results in these cases? How to write properly?
Please explain to me the principles of internationalization in GWT and why my example does not work.
Any suggestions would be greatly appreciated.

First, the files should be named Message_fr.properties (resp. Message_en.properties), not Message.properties_fr (resp. Message.properties_en).
Then ui:msg et al. in UiBinder will generate an interface (extending com.google.gwt.i18n.client.Messages)), not use one that you defined. For that, you have to use {string.greeting} (where string is the ui:field you gave to your ui:with). The UiBinder generator will do a GWT.create() on the type class of your ui:with, which is what you'd have done in Java code:
Message string = GWT.create(Message.class);
String localizedGreeting = string.greeting();
In the implicit Messages interface (generated by UiBinder), the various ui:generateXxx attributes on the ui:UiBinder will be transformed into annotations on the interface (properties of the #Generate annotation, or the value of the #GenerateKeys annotation).
Then, one method will be generated for each ui:msg, where the attributes generate equivalent annotations (#Key, #Description) and the content of the ui:msg element is the value of the #DefaultMessage annotation. When you have or widgets inside the content, they'll be turned into arguments to the method and placeholders in the #DefaultMessage text (the values will be filled by UiBinder).
I'd suggest you make something working without UiBinder first, and understand how it works; then try the ui:msg in UiBinder, using -gen in DevMode or the compiler so you can see exactly what code does UiBinder generate (yes, it really only generates code that you could have written yourself by hand).
Also, you should add a <set-property name="locale" value="en, fr" /> or you'll still have the default locale around, despite the set-property-fallback (it'd just never be used)).

Related

Custom tag with variables

I am trying to use custom tag with variables.
for eg)
<c:forEach var="test" items="itemstest">
${test}
</c:forEach>
In the above code i am able to access the test value inside the <c:forEach> tag.
I need to create a custom tag with similar functionality.
I have got info from the oracle docs http://docs.oracle.com/javaee/5/tutorial/doc/bnamu.html under title Declaring Tag Variables for Tag Handlers.
Can anyone pls help me to implement the same with example.
Hi i have solved it in the following way
class: test.java
public void doTag() throws JspException, IOException {
getJspContext().getOut().flush();
//any method or operation
super.doTag();
getJspContext().setAttribute(variable, "Hello");
}
Create getter setter for variable
tld file:
<tag>
<name>test</name>
<tag-class>com.org.test</tag-class>
<body-content>empty</body-content>
<attribute>
<name>inputValue</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>variable</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
jsp file:
<%# taglib uri="/WEB-INF/tld/tldfilename.tld" prefix="tag" %>
<tag:test inputValue="hello" variable="testValue"/>
${testValue}
For something so simple, you may be better off using tag files, which makes it easy to create a tag library using some small additions to the normal jsp syntax (and is part of the standard)
http://docs.oracle.com/javaee/1.4/tutorial/doc/JSPTags5.html
A "tag with variables" is done using an attribute declaration, JSP code is quite simple:
<%#tag pageEncoding="utf-8" %>
<%-- dont forget to add declaration for JSTL here -->
<%#attribute name="test" type="java.lang.String" required="true" %>
<c:forEach var="test" items="itemstest">
${test}
</c:forEach>
See the linked documentation on whre to put and name the files to make them accessible within you own jsp files.

Changing entry point class based on form factor

Im looking to load a different user interface in my GWT application if the user is accessing from a mobile web browser or desktop web browser. I was wondering how I would edit my Application.gwt.xml file change which entry point class is loaded based on the the form factor. I thought it might be something along these lines but i'm kind of just hacking so I was wondering if anyone had any ideas?
<entry-point class="webapp.client.WebAppEntryPoint">
<when-property-is name="formfactor" value="desktop"/>
</entry-point>
<entry-point class="webapp.client.MobileAppEntryPoint">
<when-property-is name="formfactor" value="mobile"/>
</entry-point>
Cheers.
Its almost as easy as you describe it -- that is, once you have worked out the formfactor property and how to pick a value for it.
It turns out that when you create an entrypoint and declare it in your module, the compiler uses GWT.create to actually make an instance of it. This leaves it subject to the rebind rules declared in your module. So if both WebAppEntryPoint and MobileAppEntryPoint inherit from some common superclass, you can declare that entrypoint in the module, and a slight varient on the rules you made to trigger them to be selected:
<entry-point class="webapp.client.AbstractAppEntryPoint" />
<replace-with class="webapp.client.WebAppEntryPoint">
<when-type-is class="webapp.client.AbstractAppEntryPoint" />
<when-property-is name="formfactor" value="desktop"/>
</entry-point>
<replace-with class="webapp.client.MobileAppEntryPoint">
<when-type-is class="webapp.client.AbstractAppEntryPoint" />
<when-property-is name="formfactor" value="mobile"/>
</entry-point>
These rules state: "When GWT tries to start the app, use AbstractEntryPoint (which implements EntryPoint) to do so. When someone invokes GWT.create(AbstractEntryPoint) and formfactor is desktop, give them a WebAppEntryPoint instance. When someone invokes GWT.create(AbstractEntryPoint) and formfactor is mobil, give them a MobileAppEntryPoint instance.
This then leaves the hard part - how do you build the formfactor property, define the possible values, and let the app pick the right one on startup?
To help answer this, lets look at two standard properties that already existing in GWT - locale and user.agent. Useragent detection is managed in the com.google.gwt.useragent.UserAgent module - properties are defined, a way to select a property is listed, and some helpful 'make sure that this wiring worked' bits are added to the app. Possible locales are started in com.google.gwt.i18n.I18N, but are designed to be extended within your own app. There is lots of extra stuff in here as well, defining how to pick which locale should be activated. We'll want to steal the idea of pre-defining the possible formfactors from user.agent, and will want the idea of reading the right formfactor from the locale code.
First, define the property.
<define-property name="formfactor" values="desktop, mobile" />
In this example, we'll only allow these two possible values - in reality, you might want desktop (i.e. large and mouse/keyboard), tablet (large and touch), phone (small and touch), or some other variation on this.
Next, decide how to read the right property value. There are two basic ways to do this - via a simple snippet of javascript, written in your module file, and by writing a class that generates JavaScript, based on some configuration settings. I'm going to go with the simplest one first, and let you work out how to actually detect this detail in javascript (update the question or comment if you can clarify further what you have/need/expect):
<!-- borrowing/adapting from
http://code.google.com/p/google-web-toolkit/wiki/ConditionalProperties -->
<property-provider name="formfactor"><![CDATA[
{
var ua = window.navigator.userAgent.toLowerCase();
if (ua.indexOf('android') != -1) { return 'mobile'; }
if (ua.indexOf('iphone') != -1) { return 'mobile'; }
return 'desktop';
}
]]></property-provider>
Again, this goes in the module, and defines some simple JavaScript to pick the value for formfactor - if the useragent contains the string 'android' or 'iphone', activate the mobile value, otherwise activate desktop. This code will be placed in your .nocache.js file, and used to pick the right permutation (with the right entrypoint, as defined above).
Apart from Colin's detailed answer you might have a look at GWT's standard example for mobilewebapp - http://code.google.com/p/google-web-toolkit/source/browse/#svn%2Ftrunk%2Fsamples%2Fmobilewebapp
The example FormFactor.gwt.xml - http://code.google.com/p/google-web-toolkit/source/browse/trunk/samples/mobilewebapp/src/main/java/com/google/gwt/sample/mobilewebapp/FormFactor.gwt.xml
You can use replace with
<replace-with class="webapp.client.MobileAppEntryPoint">
<when-type-is class="webapp.client.WebAppEntryPoint" />
<any>
<when-property-is name="formfactor" value="mobile"/>
</any>
</replace-with>
NOTE :i am not tried with entry point class ..but working fine for some Widget classes
For details refer Gwt differed binding
SOLVED:
Okay, thanks to everyone who replied. I used a little bit of everyones answer to get to the solution! Firstly I followed SSRs and had a look at the example FormFactor.gwt.xml file. I copied this into my project and referred to it in my App.gwt.xml file. I then followed Colins and added the following code to my App.gwt.xml file in order to load a different EntryPoint based upon form factor:
<entry-point class="webapp.client.AbstractEntryPoint" />
<replace-with class="webapp.client.WebAppEntryPoint">
<when-type-is class="webapp.client.AbstractEntryPoint" />
<when-property-is name="formfactor" value="desktop"/>
</replace-with>
<replace-with class="webapp.client.MobileAppEntryPoint">
<when-type-is class="webapp.client.AbstractEntryPoint" />
<when-property-is name="formfactor" value="mobile"/>
</replace-with>

Struts 2 interceptor redirect JSP page

Using Struts 2, I have this interceptor:
public class AccessInterceptor extends AbstractInterceptor {
#Override
public String intercept(ActionInvocation ai) throws Exception {
ActionContext actionContext = ai.getInvocationContext();
Map<String, Object> session = actionContext.getSession();
Boolean logged = (Boolean) session.get("logged");
if(logged == null) {
session.put("logged", false);
logged = false;
}
if (!logged) {
// If exception is here, it IS thrown, so I am sure "login" is returned on /homepage
return "login";
}
return ai.invoke();
}
}
And this struts.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="loginInterceptor" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="access"
class="logininterceptor.interceptors.AccessInterceptor"/>
<interceptor-stack name="appDefault">
<interceptor-ref name="access" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="appDefault" />
<global-results>
<result name="login">/WEB-INF/content/login.jsp</result>
</global-results>
</package>
</struts>
I have also /WEB-INF/content/homepage.jsp JSP and /WEB-INF/content/login.jsp and mypackage.LoginAction.java class. There is not an acton class for homepage
The problem is, that when I go to the /homepage page, it is NOT redirected to login.jsp and I see the homepage.jsp content. What is wrong?
PS: I use struts2-convention-plugin
This is happening because the conventions plugin uses the "convention-default" package which does not know about your interceptor or interceptor stack.
If you could create a package which extends conventions-default doing just what your interceptor stack does (defines the interceptor, interceptor stack and defines the default result type) and then make the conventions plugin apply that stack as the default for all actions wouldn't that be great? You can.
After the <struts> tag define a constant like so:
<constant name="struts.convention.default.parent.package" value="loginInterceptor"/>
The other suggestion, was to use struts.xml to override the conventions, which is fine but a little verbose.
Another option is to use the parent package annotation, which is good when you need to override the default behavior but as you pointed out you want this to be the default behavior so you should reserve annotations for overriding this, not to implement it.
It may be worth browsing the following list of struts2 conventions plugin constants for the future: http://struts.apache.org/2.1.8/docs/convention-plugin.html#ConventionPlugin-Overwritingpluginclasses
PS: Just naming preference but I would call your package login-package rather than loginInterceptor... not a big deal.
If you want to force to execute the interceptor every time anyone is accessing to homepage.jsp, the correct way of doing this is to put an action that returns the jsp. For example:
<struts>
...
<package name="loginInterceptor" namespace="/" extends="struts-default">
<interceptors>
...
</interceptors>
<default-interceptor-ref name="appDefault" />
<global-results>
...
</global-results>
<action name="homepage">
<result>/WEB-INF/content/homepage.jsp</result>
</action>
</package>
</struts>
After that, when a user tries to access to http://.../homepage.action, the interceptor will be executed and will be redirected to the login page if the user is not logged in.

How to use UIBinder dynamacally?

I am using gwt 2.3.I am using UI binder in this. I want to create grid in this by using UIBinder. For this I have written this code.
<g:Grid ui:field='mygrid' addStyleNames='{style.panel}'
cellSpacing='10'>
<g:row>
<g:customCell>
<g:Label text="11" styleName="{style.label}" />
</g:customCell>
<g:customCell>
<g:Label text="22" styleName="{style.label}" />
</g:customCell>
</g:row>
<g:row>
<g:customCell>
<g:Label text="33" styleName="{style.label}" />
</g:customCell>
<g:customCell>
<g:Label text="44" styleName="{style.label}" />
</g:customCell>
</g:row>
</g:Grid>
Now here my data of grid is static.Now In case if my data comes from server side & depending on that data I want to create a Grid using UIbinder. How can this possible ??
One another case.There is form.In that from number of controls comes from data base.
So How can UIBider support to create this form.As number of control may vary each time
So I want to know is it possible to use UIBinder in case to create User Interface depending on run time data.
Any suggestions or example appreciated.
UiBinder is "cross compiled" to JavaScript and HTML code. Once that is done you cant modify it. There is now way to do something like send your XML syntax to client and then it creates a grid.
However it is possible that you add new Rows in your "code behind".
For your second question: I have no Idea what you mean :P
Yes, you can add data into UIBinder from Java. When you created UIBinder in Eclipse it also created .java file for this XML. Edit this corresponding java file and add something like this:
public class MyGridSample extends Composite {
// uiBinder interface declaration...
private static MyGridSampleUiBinderUiBinder uiBinder = GWT.create(MyGridSampleUiBinder.class);
interface MyGridSampleUiBinder extends UiBinder<Widget, MyGridSample > {
}
// annotation to get ui-field into code
#UiField
Grid mygrid;
public MyGridSample () {
setWidget(uiBinder.createAndBindUi(this));
// sets cell 0,0 with label
mygrid.setWidget(0, 0, new Label("cell 0"));
}
}
GWT is client side framework and UIBinder is precompiled as javascript files - they are served as static files. You should make a Webservice call to get data into this template.

GWT Internationalization throws an exception while rebinding

I'm trying to internationalize a test application with GWT following the instruction and I have:
com.example.client.MyConstants.java
com.example.client.MyConstants_en.properties
com.example.client.MyConstants_fr.properties
com.example.client.MyAppEntryPoint.java
In this code I have:
public interface MyConstants extends Constants
{
#DefaultStringValue("HelloWorld")
String hellowWorld();
}
And
public class MyAppEntryPoint implements EntryPoint
{
public void onModuleLoad()
{
MyConstants constants = GWT.create(MyConstants.class);
VerticalPanel mainPanel = new VerticalPanel();
mainPanel.add(new Label(constants.hellowWorld()));
RootPanel.get("myContainer").add(mainPanel);
}
}
For MyApp.gwt.xml I have:
<module rename-to="myModule">
<inherits name="com.google.gwt.xml.XML" />
<inherits name="com.google.gwt.i18n.I18N"/>
<inherits name='com.google.gwt.user.theme.standard.Standard'/>
<!-- Specify the app entry point class. -->
<entry-point class='com.example.client.MyAppEntryPoint'/>
<extend-property name="locale" values="en,fr"/>
</module>
In the html I have:
...
It all seems to work as long as I don't include in the xml file. As soon as I do, I get the following exception:
[ERROR] Generator 'com.google.gwt.i18n.rebind.LocalizableGenerator' threw threw an exception while rebinding 'com.example.client.myConstants'
java.lang.NullPointerException: null
...
Any help would be greatly appreciated on why it's throwing the exception.
-
The answer is that the module name has to be the same as the properties name. So if I use:
<module rename-to="MyApp">
Then the properties files need to be:
com.example.client.MyAppConstants.java
com.example.client.MyApp_en.properties
com.example.client.MyApp_fr.properties
In other words, the module name has to be the same as the properties files.
It could be the extend-property doesn't accept multiple values. I think you should write:
<extend-property name="locale" values="en"/>
<extend-property name="locale" values="fr"/>
After trying different options I figured, you don't need "en" option, I guess because that is the default hence you need:
com.example.client.MyAppConstants.java (Interface)
com.example.client.MyAppConstants.properties (Default)
com.example.client.MyAppConstants_fr.properties (Other language)
Hopefully this helps someone else's time.