Localization</> <para> One of the most powerful and useful features of the Tapestry framework is the way in which it assists with localization of a web application. This is normally an ugly area in web applications, with tremendous amounts of ad-hoc coding necessary. </> <para> Because Tapestry does such a strong job of seperating the presentation of a component (its HTML template) from its control logic (its specification and Java class) it becomes easy for it to perform localization automatically. It's as simple as providing additional localized HTML templates for the component, and letting the framework select the proper one. </> <para> However, the static text of an application, provided by the HTML templates, is not all. </> <para> Applications also have assets (images, stylesheets and the like) that must also be localized: that fancy button labeled "Search" is fine for your English clients, but your French clients will require a similar button labeled "Recherche". </> <para> Again, the framework assists, because it can look for localized versions of the assets as it runs. </> <para> The locale application demostrates this. It is a very simply application that demonstrates changing the locale of a running application <footnote> <para> All the translations were performed using <Ulink url="http://world.altavista.com/">Babelfish</>, and are probably quite laughable to someone who actually speaks the alternate languages. </> </> </> <para> The Home page of the application allows you to select a new language for the application: </> <figure> <title>Locale Home Page</> <mediaobject> <imageobject> <imagedata fileref="images/localize-home-english.jpg" format="jpg"> </imageobject> </> </figure> <para> Selecting "German" from the list and clicking the "Change" button brings you to a new page that acknowledges your selection: </> <figure> <title>Locale Changed (German)</> <mediaobject> <imageobject> <imagedata fileref="images/localize-changed-german.jpg" format="jpg"> </imageobject> </> </figure> <para> Clicking the button (it's labeled "Select Another" in German) returns you to the Home page to select a new language: </> <figure> <title>Locale Home Page (German)</> <mediaobject> <imageobject> <imagedata fileref="images/localize-home-german.jpg" format="jpg"> </imageobject> </> </figure> <para> The neat thing here is that the <classname>Home</> page has been localized into German as well; it shows equivalent German text, the options in the popup list are in German, and the "Change" button has been replaced with a German equivalent. </> <section id="locale.home"> <title>Home Page</> <para> The <classname>Home</> page consists of a single component specification, four versions of the HTML template and four image assets. </> <figure> <title>Home.jwc (excerpt)</> <programlisting><![CDATA[ . . . <component> <id>inputLocale</id> <type>PropertySelection</type> <bindings> <binding> <name>value</name> <property-path>selectedLocale</property-path> </binding> <binding> <name>model</name> <property-path>localeModel</property-path> </binding> </bindings> </component> <component> <id>changeButton</id> <type>ImageButton</type> <bindings> <binding> <name>image</name> <property-path>assets.change-button</property- path> </binding> </bindings> </component> </components> <assets> <private-asset> <name>change-button</name> <resource-path>/tutorial/locale/Change.gif</resource-path> </private-asset> </assets>]]></PROGRAMLISTING></figure> <para> The <varname>changeButton</> component is an <classname>ImageButton</>, a Tapestry version of an <sgmltag class=starttag>input type="image"</> HTML form element. We provide it with an image, an asset that will be used as the src attribute of the HTML element. </> <para> The property path <varname>assets.change-button</> is a convienience; each component may have a number of named assets and has an <varname>assets</> property that is a <classname>Map</> of those assets. </> <para> We also must define the asset, naming it change-button. We declare it as a private asset, an asset that is not directly visible to the servlet container, but is instead packaged with the Java classes in a JAR or in the <filename>WEB-INF/classes</> directory of a WAR. </> <para> In fact, there are four files in that directory, named <filename>Change.gif</>, <filename>Change_de.gif</>, <filename>Change_fr.gif</> and <filename>Change_it.gif</>. Those suffixes (_de, _fr, etc.) identify the locale for which the image is appropriate. </> <para> More information on those suffixes is available from the <classname>java.util.Locale</> documentation. </> <para> When Tapestry is rendering the page, it knows what locale is currently selected (it's a property of the engine object) and chooses the correct file based on that. </> <para> Along with the four images, there are four HTML templates. </> <figure> <title>Home.html (English)</> <programlisting><![CDATA[<jwc id="border"> This tutorial demonstration how to dynamically change the locale for the running application. <p> Select a new locale: <jwc id="form"> <jwc id="inputLocale"/> <jwc id="changeButton"/> </jwc> </jwc>]]></programlisting></figure> <para> The alternate locale versions are named in the same pattern as the image asset files. </> <figure> <title>Home_de.html (German)</> <programlisting><![CDATA[<jwc id="border"> Diese Referentendemonstration, wie man dynamisch das locale für die laufende Anwendung ändert. <p> Wählen Sie ein neues locale aus: <jwc id="form"> <jwc id="inputLocale"/> <jwc id="changeButton"/> </jwc> </jwc>]]></programlisting></figure> <para> The ids of components are consistent regardless of the locale used; these are internal ids (the equivalent of variable names) and are not shown to the end user. In addition, there's only one specification file and the ids here must match the ids in the specification. </> <para> The only real different between the files is the static text which, here, is in German. </> <para> Again, when Tapestry is rendering the page, it first chooses the correct localized HTML template. When it is rendering the <varname>changeButton</> component, it finds the correct localized file. It all comes together to consistently display the localized images and text. </> <para> What if there isn't a localization of a template or file? Tapestry will use the more general file. For instance, if we somehow managed to convince the application that we spoke Spanish we would see mostly English text since we didn't provide Spanish localized templates or assets. </> <para> The Java code for the Home page is simple enough that we can largely skip it. The only interesting parts are providing a property selection model for the inputLocale component and responding when the form is submitted: </> <figure> <title>Home.java (excerpt)</> <programlisting><![CDATA[ public void actionTriggered(IComponent component, IRequestCycle cycle) throws RequestCycleException { getEngine().setLocale(selectedLocale); cycle.setPage("Change"); }]]></PROGRAMLISTING></FIGURE> </section> <section id="locale.change"> <title>Change page</> <para> After the user selects a language, the application switches to the <classname>Change</> page for a response, which includes a link back to the <classname>Home</> page (as a localized image button). </> <figure> <title>Change.html</> <programlisting><![CDATA[<jwc id="border"> Congratulations, you've changed the locale to <jwc id="insertLocaleName"/>. <p><jwc id="home"><jwc id="chooseAgainImage"/></jwc> </jwc>]]></programlisting></figure> <para> This template combines with the specification that identifies the images. </> <figure> <title>Change.jwc (excerpt) <programlisting><![CDATA[ . . . <component> <id>chooseAgainImage</id> <type>Image</type> <bindings> <binding> <name>image</name> <property-path>assets.choose-again</property-path> </binding> </bindings> </component> </components> <assets> <context-asset> <name>choose-again</name> <path>/images/locale/ChooseAgain.gif</path> </context-asset> </assets>]]></programlisting></figure> <para> This is similar to the previous example, in that we've provided four versions of the <filename>ChooseAgain.gif</> image asset. However, we've put the images in a different place. This time, the asset is a context asset, an asset that is visible to the servlet container. In this example, the file <filename>ChooseAgain.gif</> is located in the <filename>/images/locale</> directory of the WAR. Tapestry makes sure that the correct prefix (<filename>/tutorial</>) is prepended to the path when the HTML is rendered. </> <para> Context assets are the most common assets used. Private assets (as used on the Home page) are used mostly when creating libraries of components for reuse. When building an application that stands on its own, context assets are easier and more efficient. </> </section> <section id="locale.other-options"> <title>Other Options for Localization</> <para> In some cases, different localizations of the a component will be very similar, perhaps having only one or two small snippets of text that is different. In those cases, it may be easier on the developer to not localize the HTML template, but to replace the variant text with an <classname>Insert</> component. </> <para> The page can read a localized strings file (a <filename>.properties</> file) to get appropriate localized text. This saves the bother of maintaining multiple HTML templates. </> <para> All components on a page share the single locale for the page, but each performs its own search for its HTML template. This means that some components may not have to be localized, if they never contain any static HTML text. This is sometimes the case for reusable components, even navigational borders. </> </section> </chapter>