summaryrefslogtreecommitdiff
path: root/src/server/gwt
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/gwt')
-rw-r--r--src/server/gwt/Translate-compile13
-rw-r--r--src/server/gwt/Translate-shell-external13
-rw-r--r--src/server/gwt/src/se/chalmers/cs/gf/gwt/PGF.gwt.xml8
-rw-r--r--src/server/gwt/src/se/chalmers/cs/gf/gwt/TranslateApp.gwt.xml24
-rw-r--r--src/server/gwt/src/se/chalmers/cs/gf/gwt/client/CompletionOracle.java84
-rw-r--r--src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGF.java221
-rw-r--r--src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGFRequest.java19
-rw-r--r--src/server/gwt/src/se/chalmers/cs/gf/gwt/client/TranslateApp.java214
-rw-r--r--src/server/gwt/src/se/chalmers/cs/gf/gwt/public/Translate.css38
-rw-r--r--src/server/gwt/src/se/chalmers/cs/gf/gwt/public/Translate.html36
10 files changed, 670 insertions, 0 deletions
diff --git a/src/server/gwt/Translate-compile b/src/server/gwt/Translate-compile
new file mode 100644
index 000000000..608f17e99
--- /dev/null
+++ b/src/server/gwt/Translate-compile
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+APPDIR=`dirname $0`;
+
+if [ -z "$GWT_CLASSPATH" ]; then
+ echo 'ERROR: $GWT_CLASSPATH is not set'
+ echo 'Set $GWT_CLASSPATH to point to the GWT JAR files. For example:'
+ echo 'export GWT_DIR="/Users/bringert/src/gwt-mac-1.5.2"'
+ echo 'export GWT_CLASSPATH="$GWT_DIR/gwt-user.jar:$GWT_DIR/gwt-dev-mac.jar"'
+ exit 1
+fi
+
+java -XstartOnFirstThread -Xmx256M -cp "$APPDIR/src:$APPDIR/bin:$GWT_CLASSPATH" com.google.gwt.dev.GWTCompiler -out "$APPDIR/www" "$@" se.chalmers.cs.gf.gwt.TranslateApp;
diff --git a/src/server/gwt/Translate-shell-external b/src/server/gwt/Translate-shell-external
new file mode 100644
index 000000000..78a258ec5
--- /dev/null
+++ b/src/server/gwt/Translate-shell-external
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+APPDIR=`dirname $0`;
+
+if [ -z "$GWT_CLASSPATH" ]; then
+ echo 'ERROR: $GWT_CLASSPATH is not set'
+ echo 'Set $GWT_CLASSPATH to point to the GWT JAR files. For example:'
+ echo 'export GWT_DIR="/Users/bringert/src/gwt-mac-1.5.2"'
+ echo 'export GWT_CLASSPATH="$GWT_DIR/gwt-user.jar:$GWT_DIR/gwt-dev-mac.jar"'
+ exit 1
+fi
+
+java -XstartOnFirstThread -Xmx256M -cp "$APPDIR/src:$APPDIR/bin:$GWT_CLASSPATH" com.google.gwt.dev.GWTShell -out "$APPDIR/www" -noserver "$@" http://localhost:41296/gwt/www/se.chalmers.cs.gf.gwt.TranslateApp/Translate.html;
diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/PGF.gwt.xml b/src/server/gwt/src/se/chalmers/cs/gf/gwt/PGF.gwt.xml
new file mode 100644
index 000000000..959f16040
--- /dev/null
+++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/PGF.gwt.xml
@@ -0,0 +1,8 @@
+<module>
+
+ <!-- Inherit the core Web Toolkit stuff. -->
+ <inherits name="com.google.gwt.user.User" />
+ <inherits name="com.google.gwt.http.HTTP" />
+ <inherits name="com.google.gwt.json.JSON" />
+
+</module>
diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/TranslateApp.gwt.xml b/src/server/gwt/src/se/chalmers/cs/gf/gwt/TranslateApp.gwt.xml
new file mode 100644
index 000000000..3f35d1bef
--- /dev/null
+++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/TranslateApp.gwt.xml
@@ -0,0 +1,24 @@
+<module>
+
+ <!-- Inherit the core Web Toolkit stuff. -->
+ <inherits name="com.google.gwt.user.User" />
+
+ <inherits name="se.chalmers.cs.gf.gwt.PGF" />
+
+ <!-- Inherit the default GWT style sheet. You can change -->
+ <!-- the theme of your GWT application by uncommenting -->
+ <!-- any one of the following lines. -->
+ <inherits name="com.google.gwt.user.theme.standard.Standard"/>
+ <!-- <inherits name="com.google.gwt.user.theme.chrome.Chrome"/> -->
+ <!-- <inherits name="com.google.gwt.user.theme.dark.Dark"/> -->
+
+ <!-- Other module inherits -->
+
+
+ <!-- Specify the app entry point class. -->
+ <entry-point class="se.chalmers.cs.gf.gwt.client.TranslateApp" />
+
+ <!-- Specify the application specific style sheet. -->
+ <stylesheet src="Translate.css" />
+
+</module>
diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/CompletionOracle.java b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/CompletionOracle.java
new file mode 100644
index 000000000..1073eed67
--- /dev/null
+++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/CompletionOracle.java
@@ -0,0 +1,84 @@
+package se.chalmers.cs.gf.gwt.client;
+
+import com.google.gwt.user.client.ui.SuggestOracle;
+
+import com.google.gwt.core.client.GWT;
+
+import java.util.*;
+
+public class CompletionOracle extends SuggestOracle {
+
+ private PGF pgf;
+
+ private ErrorHandler errorHandler;
+
+ private List<String> inputLangs = null;
+
+ private PGFRequest pgfRequest = null;
+
+
+ public CompletionOracle (PGF pgf) {
+ this(pgf, null);
+ }
+
+ public CompletionOracle (PGF pgf, ErrorHandler errorHandler) {
+ this.pgf = pgf;
+ this.errorHandler = errorHandler;
+ }
+
+ public void setInputLangs(List<String> inputLangs) {
+ this.inputLangs = inputLangs;
+ }
+
+ public List<String> getInputLangs() {
+ return inputLangs;
+ }
+
+ public void setErrorHandler(ErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ }
+
+ public static interface ErrorHandler {
+ public void onError(Throwable e);
+ }
+
+ public static class CompletionSuggestion implements SuggestOracle.Suggestion {
+ private String string;
+ public CompletionSuggestion(String string) {
+ this.string = string;
+ }
+
+ public String getDisplayString() {
+ return string;
+ }
+
+ public String getReplacementString() {
+ return string;
+ }
+ }
+
+ public void requestSuggestions(final SuggestOracle.Request request, final SuggestOracle.Callback callback) {
+
+ // only allow a single completion request at a time
+ if (pgfRequest != null)
+ pgfRequest.cancel();
+
+ pgfRequest = pgf.complete(request.getQuery(), getInputLangs(), null, request.getLimit(),
+ new PGF.CompleteCallback() {
+ public void onResult(PGF.Completions completions) {
+ Collection<CompletionSuggestion> suggestions = new ArrayList<CompletionSuggestion>();
+ for (int i = 0; i < completions.length(); i++) {
+ String text = completions.get(i).getText();
+ suggestions.add(new CompletionSuggestion(text));
+ }
+ callback.onSuggestionsReady(request, new SuggestOracle.Response(suggestions));
+ }
+
+ public void onError(Throwable e) {
+ errorHandler.onError(e);
+ }
+
+ });
+ }
+
+}
diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGF.java b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGF.java
new file mode 100644
index 000000000..daf97c7ae
--- /dev/null
+++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGF.java
@@ -0,0 +1,221 @@
+package se.chalmers.cs.gf.gwt.client;
+
+import com.google.gwt.http.client.*;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.core.client.JsArray;
+import com.google.gwt.core.client.JavaScriptObject;
+
+import com.google.gwt.json.client.JSONValue;
+import com.google.gwt.json.client.JSONObject;
+import com.google.gwt.json.client.JSONParser;
+
+import java.util.Set;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.ArrayList;
+
+public class PGF {
+
+ private String baseURL;
+ private String pgfName;
+
+ public PGF (String baseURL, String pgfName) {
+ this.baseURL = baseURL;
+ this.pgfName = pgfName;
+ }
+
+ public static interface GFCallback<T extends JavaScriptObject> {
+ public void onResult (T result) ;
+ public void onError (Throwable e) ;
+ }
+
+ public static class IterableJsArray<T extends JavaScriptObject> extends JsArray<T> {
+ protected IterableJsArray() {}
+
+ public final Iterable<T> iterable() {
+ return new Iterable<T>() {
+ public Iterator<T> iterator() {
+ return new Iterator<T>() {
+ private int i = 0;
+ public boolean hasNext() {
+ return i < length();
+ }
+ public T next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return get(i++);
+ }
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ };
+ }
+ }
+
+ /* Grammar */
+
+ public PGFRequest grammar (final GrammarCallback callback) {
+ return sendRequest("grammar", null, callback);
+ }
+
+ public interface GrammarCallback extends GFCallback<Grammar> { }
+
+ public static class Grammar extends JavaScriptObject {
+ protected Grammar() { }
+
+ public final native String getName() /*-{ return this.name; }-*/;
+
+ public final native String getUserLanguage() /*-{ return this.userLanguage; }-*/;
+
+ public final native IterableJsArray<Language> getLanguages() /*-{ return this.languages; }-*/;
+
+ public final Language getLanguage(String name) {
+ int c = getLanguages().length();
+ for (int i = 0; i < c; i++) {
+ Language l = getLanguages().get(i);
+ if (l.getName().equals(name))
+ return l;
+ }
+ return null;
+ }
+ }
+
+ public static class Language extends JavaScriptObject {
+ protected Language() { }
+
+ public final native String getName() /*-{ return this.name; }-*/;
+ public final native String getLanguageCode() /*-{ return this.languageCode; }-*/;
+ public final native boolean canParse() /*-{ return this.canParse; }-*/;
+ }
+
+ /* Translation */
+
+ public PGFRequest translate (String input, List<String> fromLangs, String cat, List<String> toLangs,
+ final TranslateCallback callback) {
+ List<Arg> args = new ArrayList<Arg>();
+ args.add(new Arg("input", input));
+ if (fromLangs != null) {
+ for (String from : fromLangs) {
+ args.add(new Arg("from", from));
+ }
+ }
+ args.add(new Arg("cat", cat));
+ if (toLangs != null) {
+ for (String to : toLangs) {
+ args.add(new Arg("to", to));
+ }
+ }
+ return sendRequest("translate", args, callback);
+ }
+
+ public interface TranslateCallback extends GFCallback<Translations> { }
+
+ public static class Translations extends IterableJsArray<Translation> {
+ protected Translations() { }
+ }
+
+ public static class Translation extends JavaScriptObject {
+ protected Translation() { }
+
+ public final native String getFrom() /*-{ return this.from; }-*/;
+ public final native String getTo() /*-{ return this.to; }-*/;
+ public final native String getText() /*-{ return this.text; }-*/;
+ }
+
+ /* Completion */
+
+ public PGFRequest complete (String input, List<String> fromLangs, String cat, int limit, final CompleteCallback callback) {
+ List<Arg> args = new ArrayList<Arg>();
+ args.add(new Arg("input", input));
+ if (fromLangs != null) {
+ for (String from : fromLangs) {
+ args.add(new Arg("from", from));
+ }
+ }
+ args.add(new Arg("cat", cat));
+ args.add(new Arg("limit", limit));
+ return sendRequest("complete", args, callback);
+ }
+
+ public interface CompleteCallback extends GFCallback<Completions> { }
+
+ public static class Completions extends IterableJsArray<Translation> {
+ protected Completions() { }
+ }
+
+ public static class Completion extends JavaScriptObject {
+ protected Completion() { }
+
+ public final native String getFrom() /*-{ return this.from; }-*/;
+ public final native String getText() /*-{ return this.text; }-*/;
+ }
+
+ /* Utilities */
+
+ private <T extends JavaScriptObject> PGFRequest sendRequest (String resource, List<Arg> vars, final GFCallback<T> callback) {
+ String url = baseURL + "/" + pgfName + "/" + resource + "?" + buildQueryString(vars);
+ RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);
+ builder.setTimeoutMillis(30000);
+ builder.setHeader("Accept","text/plain, text/html;q=0.5, */*;q=0.1");
+ Request request = null;
+
+ try {
+ request = builder.sendRequest(null, new RequestCallback() {
+ public void onError(Request request, Throwable e) {
+ callback.onError(e);
+ }
+
+ public void onResponseReceived(Request request, Response response) {
+ if (200 == response.getStatusCode()) {
+ callback.onResult((T)eval(response.getText()).cast());
+ } else {
+ RequestException e = new RequestException("Response not OK: " + response.getStatusCode() + ". " + response.getText());
+ callback.onError(e);
+ }
+ }
+ });
+ } catch (RequestException e) {
+ callback.onError(e);
+ }
+
+ return new PGFRequest(request);
+ }
+
+ private static native JavaScriptObject eval(String json) /*-{
+ return eval('(' + json + ')');
+ }-*/;
+
+ private static class Arg {
+ public final String name;
+ public final String value;
+ public Arg (String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+ public Arg (String name, int value) {
+ this(name, Integer.toString(value));
+ }
+ }
+
+ private static String buildQueryString(List<Arg> args) {
+ StringBuffer sb = new StringBuffer();
+ if (args != null) {
+ for (Arg arg : args) {
+ if (arg.value != null) {
+ if (sb.length() > 0) {
+ sb.append("&");
+ }
+ sb.append(URL.encodeComponent(arg.name));
+ sb.append("=");
+ sb.append(URL.encodeComponent(arg.value));
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+} \ No newline at end of file
diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGFRequest.java b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGFRequest.java
new file mode 100644
index 000000000..b46ad9382
--- /dev/null
+++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGFRequest.java
@@ -0,0 +1,19 @@
+package se.chalmers.cs.gf.gwt.client;
+
+import com.google.gwt.http.client.*;
+
+public class PGFRequest {
+
+ private Request httpRequest;
+
+ PGFRequest (Request httpRequest) {
+ this.httpRequest = httpRequest;
+ }
+
+ public void cancel() {
+ if (httpRequest != null) {
+ httpRequest.cancel();
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/TranslateApp.java b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/TranslateApp.java
new file mode 100644
index 000000000..fd92713c3
--- /dev/null
+++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/TranslateApp.java
@@ -0,0 +1,214 @@
+package se.chalmers.cs.gf.gwt.client;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.ChangeListener;
+import com.google.gwt.user.client.ui.ClickListener;
+import com.google.gwt.user.client.ui.DockPanel;
+import com.google.gwt.user.client.ui.DialogBox;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwt.user.client.ui.PopupPanel;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.SuggestBox;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.user.client.ui.KeyboardListenerAdapter;
+
+import com.google.gwt.core.client.GWT;
+
+import com.google.gwt.user.client.Window;
+
+import com.google.gwt.i18n.client.LocaleInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class TranslateApp implements EntryPoint {
+
+ private static final String pgfBaseURL = "/pgf";
+ private static final String pgfName = "grammar.pgf";
+
+ private PGF pgf;
+
+ private CompletionOracle oracle;
+ private SuggestBox suggest;
+ private PGF.Grammar grammar;
+ private ListBox fromLangBox;
+ private ListBox toLangBox;
+ private Button translateButton;
+ private VerticalPanel outputPanel;
+ private PopupPanel statusPopup;
+ private Label statusLabel;
+
+ private void addTranslation(String text, String toLang) {
+ Label l = new Label(text);
+ l.addStyleName("my-translation");
+ PGF.Language lang = grammar.getLanguage(toLang);
+ if (lang != null) {
+ l.getElement().setLang(lang.getLanguageCode());
+ }
+ outputPanel.add(l);
+ }
+
+ private void translate() {
+ outputPanel.clear();
+ setStatus("Translating...");
+ pgf.translate(suggest.getText(), listBoxSelection(fromLangBox), null,
+ listBoxSelection(toLangBox), new PGF.TranslateCallback() {
+ public void onResult (PGF.Translations translations) {
+ for (PGF.Translation t : translations.iterable()) {
+ addTranslation(t.getText(), t.getTo());
+ }
+ clearStatus();
+ }
+ public void onError (Throwable e) {
+ showError("Translation failed", e);
+ }
+ });
+ }
+
+ private void updateLangs() {
+ oracle.setInputLangs(listBoxSelection(fromLangBox));
+ }
+
+ private List<String> listBoxSelection(ListBox box) {
+ int c = box.getItemCount();
+ List<String> l = new ArrayList<String>();
+ for (int i = 0; i < c; i++) {
+ if (box.isItemSelected(i)) {
+ l.add(box.getValue(i));
+ }
+ }
+ return l;
+ }
+
+ private void setStatus(String msg) {
+ statusLabel.setText(msg);
+ statusPopup.center();
+ }
+
+ private void showError(String msg, Throwable e) {
+ GWT.log(msg, e);
+ setStatus(msg);
+ }
+
+ private void clearStatus() {
+ statusPopup.hide();
+ }
+
+ private void setGrammar(PGF.Grammar grammar) {
+ this.grammar = grammar;
+
+ for (PGF.Language l : grammar.getLanguages().iterable()) {
+ String name = l.getName();
+ if (l.canParse()) {
+ fromLangBox.addItem(name);
+ if (name.equals(grammar.getUserLanguage())) {
+ fromLangBox.setSelectedIndex(fromLangBox.getItemCount()-1);
+ }
+ }
+ toLangBox.addItem(name);
+ }
+
+ updateLangs();
+ clearStatus();
+ fromLangBox.setEnabled(true);
+ toLangBox.setEnabled(true);
+ translateButton.setEnabled(true);
+ }
+
+ private void createTranslationUI() {
+
+ oracle = new CompletionOracle(pgf, new CompletionOracle.ErrorHandler() {
+ public void onError(Throwable e) {
+ showError("Completion failed", e);
+ }
+ });
+
+ suggest = new SuggestBox(oracle);
+ suggest.addKeyboardListener(new KeyboardListenerAdapter() {
+ public void onKeyUp (Widget sender, char keyCode, int modifiers) {
+ if (keyCode == KEY_ENTER) {
+ translate();
+ }
+ }
+ });
+
+ fromLangBox = new ListBox();
+ fromLangBox.setEnabled(false);
+ fromLangBox.addItem("Any language", "");
+ fromLangBox.addChangeListener(new ChangeListener() {
+ public void onChange(Widget sender) {
+ updateLangs();
+ translate();
+ }
+ });
+
+ toLangBox = new ListBox();
+ toLangBox.setEnabled(false);
+ toLangBox.addItem("All languages", "");
+ toLangBox.addChangeListener(new ChangeListener() {
+ public void onChange(Widget sender) {
+ updateLangs();
+ translate();
+ }
+ });
+
+ translateButton = new Button("Translate");
+ translateButton.setEnabled(false);
+ translateButton.addClickListener(new ClickListener() {
+ public void onClick(Widget sender) {
+ translate();
+ }
+ });
+
+ HorizontalPanel settingsPanel = new HorizontalPanel();
+ settingsPanel.addStyleName("my-settingsPanel");
+ settingsPanel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
+ settingsPanel.add(new Label("From:"));
+ settingsPanel.add(fromLangBox);
+ settingsPanel.add(new Label("To:"));
+ settingsPanel.add(toLangBox);
+ settingsPanel.add(translateButton);
+
+ outputPanel = new VerticalPanel();
+ outputPanel.addStyleName("my-translations");
+
+ VerticalPanel vPanel = new VerticalPanel();
+ vPanel.setWidth("100%");
+ vPanel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER);
+ vPanel.add(suggest);
+ vPanel.add(settingsPanel);
+ vPanel.add(outputPanel);
+
+ RootPanel.get().add(vPanel);
+
+ }
+
+ public void onModuleLoad() {
+ statusLabel = new Label("Loading...");
+ statusPopup = new PopupPanel(true, true);
+ statusPopup.add(statusLabel);
+ statusPopup.center();
+
+ pgf = new PGF(pgfBaseURL, pgfName);
+
+ createTranslationUI();
+
+ pgf.grammar(new PGF.GrammarCallback() {
+ public void onResult(PGF.Grammar grammar) {
+ setGrammar(grammar);
+ }
+
+ public void onError (Throwable e) {
+ showError("Error getting language information", e);
+ }
+ });
+ }
+
+}
diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/Translate.css b/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/Translate.css
new file mode 100644
index 000000000..5b5d22023
--- /dev/null
+++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/Translate.css
@@ -0,0 +1,38 @@
+/** Add css rules here for your application. */
+
+.gwt-SuggestBox {
+ width: 70%;
+ font-size: 150%;
+ margin: 1em 0 0.5em 0;
+}
+
+.my-settingsPanel * {
+ margin: 0 0.4em;
+}
+
+.my-translations {
+ margin-top: 1em;
+}
+
+.my-translation {
+ margin: 0.2em;
+ padding-left: 25px;
+ font-size: 150%;
+ background-repeat: no-repeat;
+ background-position: 0% 50%;
+}
+
+/*
+* [LANG=bg] { background-image: url("flags/bg.png"); }
+* [LANG=ca] { background-image: url("flags/catalonia.png"); }
+* [LANG=da] { background-image: url("flags/dk.png"); }
+* [LANG=de] { background-image: url("flags/de.png"); }
+* [LANG=en] { background-image: url("flags/gb.png"); }
+* [LANG=fi] { background-image: url("flags/fi.png"); }
+* [LANG=fr] { background-image: url("flags/fr.png"); }
+* [LANG=it] { background-image: url("flags/it.png"); }
+* [LANG=no] { background-image: url("flags/no.png"); }
+* [LANG=ru] { background-image: url("flags/ru.png"); }
+* [LANG=es] { background-image: url("flags/es.png"); }
+* [LANG=sv] { background-image: url("flags/se.png"); }
+*/ \ No newline at end of file
diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/Translate.html b/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/Translate.html
new file mode 100644
index 000000000..f3fc1da8c
--- /dev/null
+++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/Translate.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!-- The HTML 4.01 Transitional DOCTYPE declaration-->
+<!-- above set at the top of the file will set -->
+<!-- the browser's rendering engine into -->
+<!-- "Quirks Mode". Replacing this declaration -->
+<!-- with a "Standards Mode" doctype is supported, -->
+<!-- but may lead to some differences in layout. -->
+
+<html>
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <!-- -->
+ <!-- Any title is fine -->
+ <!-- -->
+ <title>Translate</title>
+
+ <!-- -->
+ <!-- This script loads your compiled module. -->
+ <!-- If you add any GWT meta tags, they must -->
+ <!-- be added before this line. -->
+ <!-- -->
+ <script type="text/javascript" language="javascript" src="se.chalmers.cs.gf.gwt.TranslateApp.nocache.js"></script>
+ </head>
+
+ <!-- -->
+ <!-- The body can have arbitrary html, or -->
+ <!-- you can leave the body empty if you want -->
+ <!-- to create a completely dynamic UI. -->
+ <!-- -->
+ <body>
+
+ <!-- OPTIONAL: include this if you want history support -->
+ <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
+
+ </body>
+</html>