Revision: 6127 Author: rj...@google.com Date: Fri Sep 11 16:18:26 2009 Log: Moved here from incubator http://code.google.com/p/google-web-toolkit/source/detail?r=6127
Added: /wiki/UiBinderI18n.wiki ======================================= --- /dev/null +++ /wiki/UiBinderI18n.wiki Fri Sep 11 16:18:26 2009 @@ -0,0 +1,366 @@ +#summary GWT UiBinder i18n use cases + += GWT UiBinder Internationalization Use Cases = + +Ray Ryan <br /> +freely plagiarizing from the brilliant minds behind [http://code.google.com/p/gxp/ gxp] + +This document explores internationalization use cases supported the GWT UiBinder. + += Background = + +The basic idea is that the localizable bits of your ui.xml template file are marked up as such. When the UiBinder code generator processes the template, it gathers these and creates an interface extends com.google.gwt.i18.client.Messages. You then provide localized versions of these messages as you would for any other portion of your app. (See the Binding to Properties Files section of the [http://google-web-toolkit.googlecode.com/svn/javadoc/1.6/com/google/gwt/i18n/client/Messages.html Messages javadoc].) + +Values for annotations that apply at the Messages interface level are set as attributes on the root element. Here is an example with all possible values set (normally you'd let some of these default). + +{{{ +<ui:UiBinder + xmlns:ui='urn:ui:com.google.gwt.uibinder' + + ui:defaultLocale="en_us" + ui:generateKeys="com.my.app.KeyGenerator" + ui:generateFormat="com.my.app.MessageCatalogFormat1, com.my.app.MessageCatalogFormat2" + ui:generateFilename="myapp_translate_source" + ui:generateLocales="default" +> +}}} + += Open Issues = + +Support for Constants and ConstantsWithLookup is not yet designed. + +PluralText is theoretically supported, but untested. + +So far, I have found no use for Gxp's <ui:eph/> element. Is it really not needed, or do I just lack imagination? + +Unannotated user visible text is allowed--a feature is needed to make this a compile time error. + += Complete sentence or headline with no internal tags. = + +*Original* +{{{ +<h1>User Account Management.</h1> +}}} + +*Tagged* +{{{ +<h1> + <ui:msg description="Title of UAM page">User Account Management.</ui:msg> +</h1> +}}} + + +*Generated* +{{{ +public class MyUiBinderImpl implements MyUiBinder { + static final MyUiMessages messages = GWT.create(MyUiMessages.class); + + void bindUi(MyUi myUi) { + HTML html = new HTML(); + html.setHtml("<h1>" + messages.message1() + "</h1>"); + myUi.setElement(html.getElement()); + } +} + +...@defaultlocale("en_US") +...@generatekeys +...@generatedfrom("com.foo.bar.MyUi.ul.xml") +public interface MyUiMessages extends com.google.gwt.i18.client.Messages { + @DefaultMessage("User Account Management.") + @Description("Title of UAM page") + String message1(); +} +}}} + +This is the hello world case‚ a simple, unparametrized string. + += Simple HTML tags inside message = + +*Original* +{{{ +We <b>strongly</b> urge you to reconsider. +}}} + +*Tagged* +{{{ +<ui:msg>We <b>strongly</b> urge you to reconsider.</ui:msg> +}}} + +*Generated* + +As above + +Simple formatting is reasonable to put in front of a translator, and +so UiBinder supports html in messages, not just text. + + += Messages with unclobberable portions = + +*Original* +{{{ +<!-- Uh oh, don't want translator to mess up brand CSS or the trademark span --> +<div><span class="brand">Colgate</span>, with MFP!<span class="tm">TM</span></div> +}}} + +*Tagged* +{{{ +<div> + <ui:msg description="blurb"><span class="brand" ui:ph="brandedSpan">Colgate</span>, + with MFP!<ui:ph name="trademark"><span class="tm">TM</span></ui:ph></ui:msg> +</div> +}}} + + +*Generated* +{{{ +public class MyUiBinderImpl implements MyUiBinder { + static final String MESSAGE1_BRANDED_SPAN_OPEN = "<span class=\"brand\">"; + static final String MESSAGE1_BRANDED_SPAN_CLOSE = "</span>"; + static final String MESSAGE1_TRADEMARK = "<span class='tm'>TM</span>"; + static final MyUiMessages messages = GWT.create(MyUiMessages.class); + + static final MyUiMessages messages = GWT.create(MyUiMessages.class); + + public void bind(MyUi myUi) { + HTML html = new HTML(); + html.setHtml("<div>" + + messages.message1(MESSAGE1_BRANDED_SPAN_OPEN, + MESSAGE1_BRANDED_SPAN_CLOSE, MESSAGE1_TRADEMARK) + + "</div>"); + } +} + +...@defaultlocale("en_US") +...@description("blurb") +...@generatekeys +...@generatedfrom("com.foo.bar.MyUi.ul.xml") +public interface MyUiMessages extends com.google.gwt.i18.client.Messages { + @DefaultMessage("{0}Colgate{1}, with MFP!{2}") + String message1( + @Example(MyUiBinderImpl.MESSAGE1_BRANDED_SPAN_OPEN) String brandedSpanOpen, + @Example(MyUiBinderImpl.MESSAGE1_BRANDED_SPAN_CLOSE) String brandedSpanClose, + @Example(MyUiBinderImpl.MESSAGE1_TRADEMARK) String trademark + ); +} +}}} + +There are two examples in here. First, you see a {{{ui:ph}}} attribute that +can be added to any child of a {{{ui:msg}}}, to indicate that placholders +should be created to protect it from translators. Two placeholders are +created, for the opening and closing tags of the element (in this +case, brandedSpanOpen and brandedSpanClose). + +Second, we see an element, also named {{{ui:ph}}}, that can surround an +arbitrary bit of markup to be protected in its entirety (in this case, +the trademark placeholder). + +So, you have both an element *<ui:ph>*to surround untranslatable +runs*</ui:ph>*, and the <span *ui:ph*>attribute{{{<}}}/span> to put in +arbitrary elements to hide their begin and end tags from translators, +but keep their content as part of the message. + + += Messages with runtime computed values = + +*Original* +{{{ +<!-- Java code will make calls like getClosingDate().setInnerText(closingDate()) --> + +(closed <span ui:field="closingDate" /> through <span ui:field="reopeningDate"/>) +}}} + +*Tagged* +{{{ +<ui:msg> + (closed <span ui:field="closingDate" ui:example="7/12/2008" /> + through <span ui:field="reopeningDate" ui:example="7/19/2008" />) +</ui:msg> +}}} + + +Note that there is no {{{ui:ph}}} attribute here, which would normally +be used to identify a span as a placeholder, and also to provide its +name. The {{{ui:field}}} attribute's appearance inside a child of a +{{{ui:msg}}} element is enough to make it clear that a placeholder is +required, and the field name can be used as the placeholder name. + +The {{{ui:example}}} attribute is optional, and allows more useful +text to be provided in the {...@example()}}} attribute of the generated +Mesages interface (and hence to the translator). Without it, the +parameters here would both be annotated {...@example("<span/>")}}} + + += Message containing widgets (HTMLPanel only) = + +*Original* +{{{ +<ui:HTMLPanel> + Meeting starts at + <my:TimePicker ui:field="startPicker"/> + and ends at + <my:TimePicker ui:field="endPicker"/>. +</ui:HTMLPanel> +}}} + +*Tagged* +{{{ +<ui:HTMLPanel> + <ui:msg>Meeting starts at + <my:TimePicker ui:field="startPicker"/> + and ends at + <my:TimePicker ui:field="endPicker"/>. + </ui:msg> +</ui:HTMLPanel> +}}} + + +Note that there is no {{{ui:ph}}} attribute on the widgets. There's no +need for them, as there is no ambiguity about what must be done when a +widget shows up in the middle of a message. Note also that you can +only do this kind of thing (widgets in messages) inside of an +HTMLPanel, the only widget in the GWT collection that intermixes +markup and child widgets. + += Message containing widgets with text bodies (HasText or HasHtml inside HTMLPanel) = + +*Original* +{{{ +<ui:HTMLPanel> + To do the thing, + <ui:HyperLink token="/doThe#thing">click + here</ui:HyperLink> and massage vigorously. +</ui:HTMLPanel> +}}} + +*Tagged* +{{{ +<ui:HTMLPanel> + <ui:msg>To do the thing, + <ui:HyperLink token="/doThe#thing">click + here</ui:HyperLink> and massage vigorously. + </ui:msg> +</ui:HTMLPanel> +}}} + + +*Generated* +{{{ +...@default("To do the thing, {0}click here{1} and massage vigorously.") +String message536( + @Example("<span>") String startWidget, + @Example("</span>") String endWidget +); + +HTMLPanel panel = ..." + messages.message536("<span id='xyz'>", "</span>") + "... +HyperLink link = new HyperLink(); +link.setHistoryToken("/doThe#thing"); +link.setText(panel.getElementById("xyz").getInnerText()); +panel.addAndReplace(link, "xyz"); +}}} + +Again, only an HTMLPanel can intermix markup and widgets, and so only +an HTMLPanel can hold a {{{<ui:msg>}}} in this style. Further, only the +bodies of HasHTML and HasText children can be recognized as part of +the message. + += HTML attributes that need translation = + +*Original* +{{{ +<th title="Keyword targeted"> + <ui:msg>Keyword</ui:msg> +</th> +}}} + +*Tagged* +{{{ +<th title="Keyword targeted"> + <ui:attribute ui:name="title" ui:description="Tooltip text for keyword column"/> + <ui:msg>Keyword</ui:msg> +</th> +}}} + + +*Generated* +{{{ +...@defaultlocale("en_US") +...@generatekeys +...@generatedfrom("com.foo.bar.MyUi.ul.xml") +public class MyUiBinderImpl implements MyUiBinder { + static final MyUiMessages messages = GWT.create(MyUiMessages.class); + + void bindUi(MyUi myUi) { + HTML html = new HTML(); + html.setHtml("<th title='" + messages.message1() + ">" + + messages.message2() + + "</th>"); + myUi.setElement(html.getElement()); + } +} + +public interface MyUiMessages extends com.google.gwt.i18.client.Messages { + @DefaultMessage("Keyword targetted") + @Description("Tooltip text for keyword column") + String message1(); + + @DefaultMessage("Keyword") + String message2(); +} +}}} + +Most common examples of this are the title attribute, for tool-tip +text, and the alt tag of an <img> element. + += Plural vs. Singular = + +*Original* +{{{ +<!-- Imagine code that shows or hides these spans as appropriate --> +<span ui:field="plural"> + You have <span ui:field="pluralFormCount"/> active campaigns. +</span> +<span ui:field="singular"> + You have one active campaign. +</span> +}}} + +*Tagged* +{{{ +<ui:msg> + You have <ui:ph name="campaignCount" example="5"/> active campaigns. + <ui:pluralText key="one">You have one active campaign.</ui:pluralText> +</ui:msg> +}}} + += Words with Multiple Meanings = + +*Original* +{{{ +Favorite Color: + <ui:RadioButton name="color">Red</ui:RadioButton> + <ui:RadioButton name="color">Orange</ui:RadioButton> + +Favorite Fruit: + <ui:RadioButton name="fruit">Apple</ui:RadioButton> + <ui:RadioButton name="fruit">Orange</ui:RadioButton> +}}} + +*Tagged* +{{{ +Favorite Color: + <ui:RadioButton name="color"><ui:msg>Red</ui:msg></ui:RadioButton> + <ui:RadioButton name="color"><ui:msg meaning="the color"/>Orange</ui:msg></ui:RadioButton> + +Favorite Fruit: + <ui:RadioButton name="fruit"><ui:msg>Apple</ui:msg></ui:RadioButton> + <ui:RadioButton name="fruit"><ui:msg meaning="the fruit">Orange</ui:msg></ui:RadioButton> +}}} + + +The punchline here is that a translator may well be working with no +more context than the attributes you set on an individual +message. Post processors that generate unique hashes on the contents +of the default message can use the optional {{{meaning}}} attribute to +modify the hash. In this example that ensures that we get two +different translations of the word "orange" in languages where it's +needed. --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---