| <?xml version="1.0" encoding="UTF-8"?> |
| <!-- |
| Copyright 2005 The Apache Software Foundation |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| <document> |
| <properties> |
| <title>Localization</title> |
| </properties> |
| <body> |
| <section name="Localization"> |
| <p> |
| Proper localization is a pervasive aspect of web application development. Supporting |
| users from different countries, with different languages, can be a tricky |
| proposition ... it is more than just text that must be localized, but more subtle |
| aspects of the application such as date and currency formats. It is also more than |
| text ... in some cases, a localized application will want to change images or even |
| color schemes. |
| </p> |
| <p>Localization support in Tapestry is likewise pervasive.</p> |
| <subsection name="Component Message Catalogs"> |
| |
| <p> |
| The most fundamental aspect of localization in Tapestry are component message |
| catalogs (remember that pages are components too). A message catalog is a |
| mapping from a logical key (that may appear in Java code or in OGNL expressions) |
| to a literal string. Tapestry message catalogs are similar to Java's |
| ResourceBundle class, except there is more flexibility in the character set of |
| the files, and the location of the files. |
| </p> |
| <p> |
| Each component |
| <em>may</em> |
| have a message catalog, consisting of a set of localized message properties |
| files. |
| </p> |
| <p> |
| These files are stored with the page or component specification file. They are |
| named the same as the specification file, but with a different extension |
| (".properties" instead of ".jwc" or ".page"). |
| </p> |
| <p> |
| In addition, this is a |
| <em>set</em> |
| of files; a locale string may be inserted just before the extension. For |
| example, |
| <code>WEB-INF/Home_fr.properties</code> |
| to contain the French language localization of the keys. |
| </p> |
| <p> |
| As with Java's ResourceBundle, resolution of a key to a message starts with the |
| most |
| <em>specific</em> |
| properties file. Any key not found there will be searched for in less specific |
| files. For example, the search path could be |
| <code>Home_fr_BE.properties</code> |
| , |
| <code>Home_fr.properties</code> |
| , |
| <code>Home.properties</code> |
| . |
| </p> |
| <p> |
| If a properties file does not exist, that's perfectly ok, the search will |
| continue. |
| </p> |
| <p> |
| When a key can not be found even in the most general properties file, a search |
| occurs in the |
| <a href="#localization.namespace">namespace</a> |
| . In this way, very common strings can be stored and localized once, and used |
| throughout a library or application. |
| </p> |
| <p> |
| We'll describe how to use the message catalog shortly, but first some notes on |
| how the message catalogs are read. |
| </p> |
| <subsection name="Properties file encoding"> |
| |
| <p> |
| For Java's ResourceBundle, the properties files must be in UTF-8 character |
| set. This can be problematic, as in non-western languages it is necessary to |
| use Java's native2ascii tool to convert from non-native files into an ASCII |
| encoding of UTF-8. |
| </p> |
| <p> |
| Tapestry can read properties files in alternate character sets, but must be |
| told what character set the file is encoded in (internally, the contents |
| must be converted into standard multi-byte Unicode). |
| </p> |
| <p> |
| This is accomplished by providing some metadata inside the component (or |
| page) specification. Metadata is specified using the |
| <a href="spec.html#spec.meta"><meta></a> |
| element. |
| </p> |
| <p> |
| The resolution of the character set is somewhat complicated; it is possible |
| that each properties file will use a different character set. At the same |
| time, repetition is bad ... therefore it is possible to specify some of this |
| information in the namespace meta data (in the containing application or |
| library specification) so that it can apply to all pages and components |
| within the namespace. |
| </p> |
| |
| <p> |
| The basic meta-data property name searched for is |
| <code>org.apache.tapestry.messages-encoding</code> |
| . The value for this name is the name of the charset for the properties |
| file. |
| </p> |
| |
| <p> |
| However, the base name is modified to reflect the locale for the file being |
| read; the locale string is appended to the key, thus |
| <code>org.apache.tapestry.messages-encoding_fr</code> |
| will define the character set for the file |
| <code>WEB-INF/Home_fr.properties</code> |
| </p> |
| |
| <p> |
| For each localization of the base property name, a search of the following |
| locations takes place. |
| </p> |
| <ul> |
| <li>The page or component specification.</li> |
| <li> |
| The namespace (library or application) specification for the namespace |
| containing the page or component. |
| </li> |
| <li> |
| The |
| <a href="configuration.html#configuration.global-property-source"> |
| global property source |
| </a> |
| . |
| </li> |
| </ul> |
| |
| <p> |
| Because localization of templates is similar to localization of message |
| properties files, a second search occurs if the search for (variations of) |
| <code>org.apache.tapestry.messages-encoding</code> |
| fails; this time for |
| <code>org.apache.tapestry.template-encoding</code> |
| occurs (again, with variations for each locale). |
| </p> |
| |
| <p> |
| The ultimate default for encoding character set is ISO-8859-1; in other |
| words, the same behavior as reading an ordinary Java ResourceBundle. |
| </p> |
| |
| </subsection> |
| </subsection> |
| <subsection name="Missing keys"> |
| |
| |
| <p> |
| While developing, you may occasionally reference a key that does not exist. |
| Rather than fail with an exception, Tapestry will fabricate a missing key value. |
| This is the key, converted to upper-case, and surrounded with brackets. For |
| example, |
| <code>[A-MISSING-KEY]</code> |
| . This allows missing key values to stand out an demand to be fixed, without |
| completely subverting your application. |
| </p> |
| |
| </subsection><!-- localization.missing-keys --> |
| <subsection name="Namespace message catalogs"> |
| |
| |
| <p> |
| It is very likely that you'll have a number of strings that are used, and |
| re-used, throughout your application. Rather than duplicate the same message |
| keys and localized values in all your page and component message catalogs, you |
| can put these into your |
| <em>namespace</em> |
| catalog. |
| </p> |
| |
| <p> |
| Each page and component is part of a |
| <code>namespace</code> |
| , identified by a library specification or component specification. |
| </p> |
| |
| <p> |
| The specification may also have a message catalog; for instance, for |
| <code>WEB-INF/myapp.application</code> |
| , the files would be named |
| <code>WEB-INF/myapp.properties</code> |
| , etc. Again, the name of the file is based on the servlet name ("myapp"). |
| </p> |
| |
| <p> |
| Very simple applications may not have an application specification, but may |
| still have properties, just as if the application specification existed. |
| </p> |
| |
| </subsection><!-- localization.namespace --> |
| <!-- localization.component-catalog --> |
| <subsection name="Template text localization"> |
| |
| <p> |
| As described in |
| <a href="template.html#template.directives.l10n"> |
| the discussion of Tapestry templates |
| </a> |
| , static text in an HTML template can be enclosed in a specialized <span> |
| tag. |
| </p> |
| |
| </subsection> |
| |
| <subsection name="Localized templates"> |
| |
| |
| <p> |
| In some cases, the entire layout of a page (or component) must change due to |
| locale. For example, because of differences between western languages (which |
| read left to right) and many eastern languages (which read right to left). |
| </p> |
| |
| <p> |
| In this case, it is possible to have multiple HTML templates. If a localized |
| template (e.g., |
| <code>Home_jp.html</code> |
| for a Japanese locale) exists, it will be used as appropriate. |
| </p> |
| |
| <p> |
| Page and component |
| <em>specifications</em> |
| are never localized, just |
| <em>templates</em> |
| . |
| </p> |
| |
| <p> |
| It is a good idea to make use of declared components, rather than implicit |
| components, when using localized templates ... it reduces duplication in the |
| templates. |
| </p> |
| </subsection> |
| |
| <subsection name="Template encoding"> |
| |
| |
| <p> |
| Like |
| <a href="#localization.component-catalog.encoding">message catalogs</a> |
| , each template may be written in a different character set. |
| </p> |
| |
| <p> |
| For each localization of the base key ( |
| <code>org.apache.tapestry.template-encoding</code> |
| , a search of the following locations takes place. |
| </p> |
| <ul> |
| <li>The page or component specification.</li> |
| <li> |
| The namespace specification for the namespace containing the page or |
| component. |
| </li> |
| <li> |
| The |
| <a href="configuration.html#configuration.search-path"> |
| application property search path |
| </a> |
| </li> |
| </ul> |
| </subsection> |
| |
| <subsection name="Using the message: binding prefix"> |
| |
| |
| <p> |
| When specifying a parameter binding, the |
| <code>message:</code> |
| prefix is used to reference a localized message key. For example: |
| </p> |
| |
| <source xml:space="preserve"> |
| <html jwcid="@<a href="../components/general/shell.html">Shell</a>" title="message:page-title"> |
| . . . |
| </html> |
| </source> |
| |
| </subsection> |
| |
| <subsection name="Localization of Assets"> |
| |
| |
| <p> |
| Assets may also be localized. Classpath and context assets will automatically |
| search for a locale-specific match (this is very similar to how localized |
| templates work). |
| </p> |
| </subsection><!-- localization.assets --> |
| |
| <subsection name="Formatting messages"> |
| |
| |
| <p> |
| Messages may contain |
| <em>arguments</em> |
| , strings of the form |
| <code>{0}</code> |
| (or some other number). The argument are handled exactly the same as with Java's |
| MessageFormat class (in fact, under the covers, MessageFormat does the work). |
| </p> |
| |
| <p> |
| Components include a |
| <code>messages</code> |
| property for accessing localized messages. This property is of type Messages, |
| and includes two methods: |
| </p> |
| |
| <ul> |
| <li> |
| <code>getMessage()</code> |
| takes a string parameter and returns a localized message |
| </li> |
| <li> |
| <code>format()</code> |
| takes a string parameter (the key) and then takes a number of additional |
| parameters as arguments. The arguments are just objects. If you have more |
| than three arguments, then specify them as an object array. |
| </li> |
| </ul> |
| |
| <p>It is common to format messages using OGNL expessions, i.e.:</p> |
| |
| <source xml:space="preserve"> |
| <span jwcid="@<a href="../components/general/insert.html">Insert</a>" value="ognl:messages.format('billing-info', amountDue)"/> |
| </source> |
| |
| <p> |
| The above example would get the amountDue property and pass it in as argument 0 |
| to the message format retrieved from the message catalog as key 'billing-info'. |
| </p> |
| |
| </subsection> |
| |
| <subsection name="Changing the locale"> |
| |
| |
| <p> |
| In order to change the locale, you must obtain the |
| <a href="../apidocs/org/apache/tapestry/IEngine.html"> |
| IEngine |
| </a> |
| and invoke |
| <code>setLocale()</code> |
| on it. This will change the value stored in the engine (which is used when |
| loading new pages), and: |
| </p> |
| |
| <ul> |
| <li> |
| Update the hivemind.ThreadLocale service, allowing localized messages from |
| services to be generated in the correct locale |
| </li> |
| <li> |
| Cause an HTTP Cookie to be added to the request so that future requests from |
| the same client will be in the same locale |
| </li> |
| </ul> |
| |
| <p> |
| Changing the locale |
| <a href="#localization.engine-locale"> |
| does not affect any pages loaded in the current request. |
| </a> |
| </p> |
| |
| |
| </subsection> |
| |
| <subsection name="Engine locale vs. page locale"> |
| |
| |
| <p> |
| When pages are created, or obtained from the page pool, the engine's locale is |
| taken into account. Pages are obtained when they are used by a service, or when |
| accessed via |
| <a |
| href="../apidocs/org/apache/tapestry/IRequestCycle.html"> |
| IRequestCycle |
| </a> |
| .getPage(). |
| </p> |
| |
| <p> |
| A page is loaded for a particular locale, and the page's locale never changes. |
| This is because of the degree to which localization creeps into the properties |
| of the page and the components within the page. |
| </p> |
| |
| <p> |
| Additionally, once a page is loaded during a request cycle, it is kept for the |
| duration of the cycle ... even if the engine locale changes. |
| </p> |
| |
| <p> |
| If you have a listener method on a page that changes the engine's locale, it is |
| necessary to activate a |
| <em>different</em> |
| page to render the response. This new page will be loaded in the new locale. |
| </p> |
| |
| <span class="info"> |
| <strong>Note:</strong> |
| <p> |
| This may be addressed somewhat in Tapestry 4.0. Two options are possible: a |
| service for changing the locale before rendering a page, and a way to force |
| Tapestry to re-load a page, in a new locale. |
| </p> |
| </span> |
| |
| </subsection> |
| |
| <subsection name="Limiting accepted locales"> |
| |
| |
| <p> |
| By default, Tapestry accepts incoming locales (as specified in the request HTTP |
| header) as-is. The requested locale is used as-is. This has some implications, |
| primarily in terms of resource usage. |
| </p> |
| |
| <p> |
| Imagine an application that is being accessed by users in the US, the UK and in |
| Canada. The incoming request locales will be "en_US", "en_UK" and "en_CA" |
| (respectively). However, it is likely that you will only have created a single |
| localization, for English in general (locale "en"). Despite this, there will be |
| several different versions of each page in the page pool: one for each of the |
| above locales, even though they will be functionally identical. |
| </p> |
| |
| <p> |
| Ideally, what we want is to limit incoming requests so that all of the listed |
| locales ("en_US", "en_UK" and "en_CA") will be 'filtered down' to just "en". |
| </p> |
| |
| <p> |
| That functionality is controlled by the org.apache.tapestry.accepted-locales |
| <a href="configuration.html#configuration.properties">configuration property</a> |
| . By setting this property to a comma-seperated list of local names, incoming |
| requests will be converted to the closest match. For example, the the property |
| could be configured to "en,fr,de" to support English, French and German. |
| </p> |
| |
| <p> |
| Matching takes place by stripping off "terms" (the locale variant, then the |
| locale country code) from the locale name. So "en_US" would be stripped to "en" |
| (which would match). When no match can be found, the |
| <em>first</em> |
| locale in the list is treated as the default. In the prior example, Russian |
| users would be matched to the "en" locale. |
| </p> |
| |
| </subsection> |
| </section> |
| </body> |
| </document> |