Sunday, February 28, 2010

Oracle ADF and Google Translate : Leveraging Google Translation Services in Oracle ADF Applications

An application that will be used in a multi-cultural and multi-language environment can leverage the free translation service of Google through Google Translate API.

This post recreated the Google Translate service using Oracle ADF Faces RC in JDeveloper 11g to demonstrate the simplicity and the possibilities of using the Google Translate API. Below are screen shots of our completed Google Translate API demo in ADF:
 
Just like the official Google Translate page, the screen shot above shows that we can choose the "from what" and "to what" language shall we translate. Invoking the "Translate" button will display the translated text without full submit of the page.
 
Invoking "Clear" button will reset the components.

To recreate this demo we need to do the following:
  1. Download the Java client API for using Google Translate.
  2. Create a new JDeveloper application with one view-controller project.
  3. Create the translate.jspx page
  4. Create the GoogleTranslateForm class (the backing bean of translate.jspx)
  5. Create and register a LanguageEnumConverter class

Download the Java client API for using Google Translate.

The unofficial Java client API is only about 44KB. We can download it from the following link: google-api-translate-java. To use it, we just simply include the google-api-translate-java.jar file into our application's classpath.

Create a new JDeveloper application with one view-controller project.

Create a new generic application with one view-controller project in which ADF Faces and ADF Page Flows project technologies are selected. The ADF Page Flows is optional, but we use it for consistency.
Below is a screen shot of my sample project structure:

Create the translate.jspx page

To create the translate.jspx page, we need to open adfc-config.xml, drop a view object, named it "translate", double click it to invoke the create page wizard. Basically our page will have the following components:
  • an inputText component to hold user input;
  • two selectOneChoice component to hold the Language From and Language To;
  • a button to to invoke the translation service; and
  • a button to reset the form
Below is my translate.jspx page:
<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <jsp:directive.page contentType="text/html;charset=UTF-8"/>
  <f:view>
    <af:document id="d1">
      <af:form id="f1">
        <af:panelGroupLayout layout="scroll"
                             xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
                             id="pgl2">
          <af:panelGroupLayout id="pgl7" layout="horizontal">
            <af:image source="/images/translate_logo.gif" id="i1"/>
            <af:activeOutputText value="API Demo in ADF" id="aot1"
                                 inlineStyle="font-size:medium;"/>
          </af:panelGroupLayout>
          <af:outputText value="Translate text, webpages and documents" id="ot2"
                         inlineStyle="font-size:medium; font-weight:bold;"/>
          <af:panelFormLayout id="pfl1" labelAlignment="top">
            <af:inputText id="it1" columns="100" rows="6"
                          label="Enter text or a webpage URL, or upload a document."
                          value="#{backingBeanScope.googleTranslate.input}"
                          autoSubmit="true" partialTriggers="cb2"/>
            <f:facet name="footer">
              <af:panelGroupLayout id="pgl3">
                <af:panelGroupLayout id="pgl5" layout="horizontal">
                  <af:panelGroupLayout id="pgl6">
                    <af:panelFormLayout id="pfl2" labelAlignment="start">
                      <af:selectOneChoice label="Translate From" id="soc1"
                                          value="#{backingBeanScope.googleTranslate.translateFrom}"
                                          autoSubmit="true"
                                          converter="LanguageEnumConverter">
                        <f:selectItems value="#{backingBeanScope.googleTranslate.languageItems}"
                                       id="si1"/>
                      </af:selectOneChoice>
                      <af:selectOneChoice label="Translate To" id="soc2"
                                          value="#{backingBeanScope.googleTranslate.translateTo}"
                                          autoSubmit="true"
                                          converter="LanguageEnumConverter">
                        <f:selectItems value="#{backingBeanScope.googleTranslate.languageItems}"
                                       id="si2"/>
                      </af:selectOneChoice>
                    </af:panelFormLayout>
                    <f:facet name="separator"/>
                  </af:panelGroupLayout>
                  <af:panelGroupLayout id="pgl1">
                    <af:commandButton text="Translate" id="cb1"
                                      actionListener="#{backingBeanScope.googleTranslate.translate}"
                                      partialSubmit="true"/>
                    <af:commandButton text="Clear" id="cb2"
                                      actionListener="#{backingBeanScope.googleTranslate.clear}"/>
                    <f:facet name="separator">
                      <af:spacer width="10" height="10" id="s3"/>
                    </f:facet>
                  </af:panelGroupLayout>
                  <f:facet name="separator">
                    <af:spacer width="20" height="10" id="s1"/>
                  </f:facet>
                </af:panelGroupLayout>
                <af:panelGroupLayout id="pgl4" inlineStyle="width:800.0px;">
                  <af:separator id="s2"/>
                  <af:outputText value="#{backingBeanScope.googleTranslate.translated}"
                                 id="ot1" partialTriggers="cb1 cb2"/>
                </af:panelGroupLayout>
              </af:panelGroupLayout>
            </f:facet>
          </af:panelFormLayout>
        </af:panelGroupLayout>
      </af:form>
    </af:document>
  </f:view>
</jsp:root>

Create the GoogleTranslateForm class (the backing bean of translate.jspx)

The GoogleTranslateForm class is a backing bean that will hold the server side values of our components. In addition, it also provide selectItems composed of Google Translate API Language Enum.

ADF Faces RC greatly simplifies our development because of the partial page rendering (PPR) and auto submit features. As you can see in my code below, to implement the functionality, I don't even have a single value changed listener.
package soadev.blogspot.googletranslateapi.backing;

import com.google.api.translate.Language;
import com.google.api.translate.Translate;
import java.util.ArrayList;
import java.util.List;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;

public class GoogleTranslateForm {
    private List <SelectItem> languageItems;
    private String input;
    private Language translateFrom = Language.ENGLISH;
    private Language translateTo = Language.DUTCH;
    private String translated;

    public void setLanguageItems(List<SelectItem> languages) {
        this.languageItems = languages;
    }

    public List<SelectItem> getLanguageItems() {
        if (languageItems == null){         
            languageItems = new ArrayList<SelectItem>();
            languageItems.add(new SelectItem(Language.ENGLISH, "ENGLISH"));
            languageItems.add(new SelectItem(Language.DANISH, "DANISH"));
            languageItems.add(new SelectItem(Language.DUTCH, "DUTCH"));
            languageItems.add(new SelectItem(Language.FILIPINO, "FILIPINO"));
            languageItems.add(new SelectItem(Language.NORWEGIAN, "NORWEGIAN"));
            languageItems.add(new SelectItem(Language.ARABIC, "ARABIC"));
            languageItems.add(new SelectItem(Language.CHINESE_SIMPLIFIED, "CHINESE_SIMPLIFIED"));
            languageItems.add(new SelectItem(Language.CHINESE_TRADITIONAL, "CHINESE_TRADITIONAL"));
            languageItems.add(new SelectItem(Language.FRENCH, "FRENCH"));
            languageItems.add(new SelectItem(Language.GERMAN, "GERMAN"));
            languageItems.add(new SelectItem(Language.GREEK, "GREEK"));
            languageItems.add(new SelectItem(Language.ITALIAN, "ITALIAN"));
            languageItems.add(new SelectItem(Language.JAPANESE, "JAPANESE"));
            languageItems.add(new SelectItem(Language.KOREAN, "KOREAN"));
            languageItems.add(new SelectItem(Language.RUSSIAN, "RUSSIAN"));
            languageItems.add(new SelectItem(Language.SPANISH, "SPANISH"));
        }
        return languageItems;
    }

    public void setInput(String input) {
        this.input = input;
    }

    public String getInput() {
        return input;
    }

    public void setTranslateFrom(Language translateFrom) {
        this.translateFrom = translateFrom;
    }

    public Language getTranslateFrom() {
        return translateFrom;
    }

    public void setTranslateTo(Language translateTo) {
        this.translateTo = translateTo;
    }

    public Language getTranslateTo() {
        return translateTo;
    }

    public void setTranslated(String translated) {
        this.translated = translated;
    }

    public String getTranslated() {
        return translated;
    }

    public void translate(ActionEvent actionEvent) {
        
       try{
            translate();
        }
        catch (Exception e) {
            // TODO: Add catch code
            e.printStackTrace();
        }
    }
    public void translate() throws Exception{           
            if(input != null){
                //below are proxy related configuration
//                System.setProperty("http.proxyHost", "yourHost");             
//                System.setProperty("http.proxyPort", "8080");
                Translate.setHttpReferrer("http://localhost");
                translated = Translate.execute(input, getTranslateFrom(), getTranslateTo());
                
            }
    }

    public void clear(ActionEvent actionEvent) {
        input = null;
        translated = null;
    }
}

Create and register a LanguageEnumConverter class

Below is my LanguageEnumConverter class:
package soadev.blogspot.googletranslateapi.convert;

import com.google.api.translate.Language;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;

public class LanguageEnumConverter implements Converter {
   
    @Override
    public Object getAsObject(FacesContext facesContext,
                              UIComponent uIComponent, String string) {
        return Language.fromString(string);
    }
    @Override
    public String getAsString(FacesContext facesContext,
                              UIComponent uiComponent, Object object) {
        return object.toString();
    }
}
We need to register this converter in faces-config.xml.
<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee">
  <application>
    <default-render-kit-id>oracle.adf.rich</default-render-kit-id>
  </application>
  <converter>
    <converter-id>LanguageEnumConverter</converter-id>
    <converter-class>soadev.blogspot.googletranslateapi.convert.LanguageEnumConverter</converter-class>
  </converter>
</faces-config>
This converter is used in the translate.jspx above.


Conclusion

With minimal effort, we can leverage the valuable service offered by Google Translate.
Cheers!

ps. If your testing behind a firewall that requires proxy authentication, read the following post.

Related Posts

1 comment:

  1. This backing bean code is showing error at

    Translate.setHttpReferrer("http://localhost");
    translated = Translate.execute(input, getTranslateFrom(), getTranslateTo());

    Error(91,26): cannot find method setHttpReferrer(java.lang.String)
    Error(92,39): non-static method execute(java.lang.String,com.google.api.translate.Language,com.google.api.translate.Language) cannot be referenced from a static context

    ReplyDelete