|
0
|
1 |
package hirondelle.web4j.ui.translate;
|
|
|
2 |
|
|
|
3 |
import hirondelle.web4j.request.LocaleSource;
|
|
|
4 |
|
|
|
5 |
import java.util.Locale;
|
|
|
6 |
|
|
|
7 |
/**
|
|
|
8 |
Translate base text into a localized form.
|
|
|
9 |
|
|
|
10 |
<P>See {@link hirondelle.web4j.BuildImpl} for important information on how this item is configured.
|
|
|
11 |
{@link hirondelle.web4j.BuildImpl#forTranslator()} returns the configured implementation of this interface.
|
|
|
12 |
|
|
|
13 |
<P>Here, "<b>base text</b>" refers to either :
|
|
|
14 |
<ul>
|
|
|
15 |
<li>a snippet of user-presentable text in the language being used to build the application
|
|
|
16 |
<li>a "coder key", such as <tt>"image.title"</tt> or <tt>"button.label"</tt>.
|
|
|
17 |
Coder keys are never presented to the end user, and serve as an alias or shorthand
|
|
|
18 |
for something else. They are intended for the exclusive use of the programmer, and may
|
|
|
19 |
be thought of as being in a special "language" understood only by the programmer.
|
|
|
20 |
</ul>
|
|
|
21 |
|
|
|
22 |
<P><span class="highlight">In code, it is likely best to use user-presentable text
|
|
|
23 |
instead of coder keys, whenever possible - since lookups are never needed, it makes
|
|
|
24 |
code clearer at the point of call.</span>
|
|
|
25 |
|
|
|
26 |
<P>Please see the package <a href="package-summary.html">overview</a> for an interesting way of
|
|
|
27 |
evolving the implementation of this interface during development, allowing applications to assist in their
|
|
|
28 |
own translation.
|
|
|
29 |
|
|
|
30 |
<P>One might validly object to a framework which requires an implementation of this
|
|
|
31 |
interface even for single-language applications. However, arguments in favor of
|
|
|
32 |
such a style include :
|
|
|
33 |
<ul>
|
|
|
34 |
<li>providing a do-nothing implementation for a single-language application is trivial.
|
|
|
35 |
<li>changing from a single language to multiple languages is a common requirement for
|
|
|
36 |
web applications. The presence of this implementation makes it clear to the maintainer
|
|
|
37 |
where such changes are made, if they become necessary.
|
|
|
38 |
<li>some programmers might prefer to name items as <tt>'emailAddr'</tt> instead of
|
|
|
39 |
<tt>'Email Address'</tt>, for instance. In this case, a {@link Translator} can also
|
|
|
40 |
be used, even in a single-language application, to translate from such 'coder keys'
|
|
|
41 |
to user-presentable text.
|
|
|
42 |
</ul>
|
|
|
43 |
|
|
|
44 |
<P>The recommended style is to implement this interface with a database, and to
|
|
|
45 |
<a href="http://www.javapractices.com/Topic208.cjp">avoid <tt>ResourceBundle</tt></a>.
|
|
|
46 |
|
|
|
47 |
<P><b>Guidance For Implementing With A Database</b><br>
|
|
|
48 |
Example of a web application with the following particular requirements
|
|
|
49 |
(see the example application for further illustration):
|
|
|
50 |
<ul>
|
|
|
51 |
<li>English is the base development language, used by the programmers and development team
|
|
|
52 |
<li>the user interface needs to be in both English and French
|
|
|
53 |
<li>in source code, the programmers use both regular text snippets in
|
|
|
54 |
user-presentable English, and (occasionally) 'coder keys', according to the needs of each particular case
|
|
|
55 |
</ul>
|
|
|
56 |
|
|
|
57 |
Here is a style of <tt>ResultSet</tt> that can be used to implement this interface :
|
|
|
58 |
<P><table border=1 cellspacing=0 cellpadding=3>
|
|
|
59 |
<tr>
|
|
|
60 |
<th>BaseText</th><th>Locale</th><th>Translation</th>
|
|
|
61 |
</tr>
|
|
|
62 |
<tr><td>Fish And Chips</td><td>en</td><td>Fish And Chips</td></tr>
|
|
|
63 |
<tr><td>Fish And Chips</td><td>fr</td><td>Poisson et Frites</td></tr>
|
|
|
64 |
<tr><td>delete</td><td>en</td><td>delete</td></tr>
|
|
|
65 |
<tr><td>delete</td><td>fr</td><td>supprimer</td></tr>
|
|
|
66 |
<tr><td>add.edit.button</td><td>en</td><td>Add/Edit</td></tr>
|
|
|
67 |
<tr><td>add.edit.button</td><td>fr</td><td>Ajouter/Changer</td></tr>
|
|
|
68 |
</table>
|
|
|
69 |
|
|
|
70 |
<P>Only the last two rows use a "coder key".
|
|
|
71 |
The <tt>BaseText</tt> column holds either the coder key, or the user-presentable English.
|
|
|
72 |
The <tt>BaseText</tt> is the lookup key. The fact that there is
|
|
|
73 |
repetition of data between the <tt>BaseText</tt> and <tt>English</tt> columns is not a real duplication problem,
|
|
|
74 |
since <em>this is a <tt>ResultSet</tt>, not a table</em> - the underlying tables will not have such
|
|
|
75 |
repetition (if designed properly, of course).
|
|
|
76 |
|
|
|
77 |
<P>For example, such a <tt>ResultSet</tt> can be constructed from three underlying tables -
|
|
|
78 |
<tt>BaseText</tt>, <tt>Locale</tt>, and <tt>Translation</tt>. <tt>Translation</tt> is a cross-reference
|
|
|
79 |
table, with foreign keys to both <tt>BaseText</tt> and <tt>Locale</tt>. When such a scheme is
|
|
|
80 |
normalized, it will have no repeated data. (In underlying queries, however, the base
|
|
|
81 |
language will be necessarily treated differently than the other languages.)
|
|
|
82 |
|
|
|
83 |
<P>Upon startup, the tables are read, the above <tt>ResultSet</tt> is created and
|
|
|
84 |
stored in a <tt>private static Map</tt>, represented schematically as
|
|
|
85 |
<tt>Map[BaseText, Map[Locale, Translation]]</tt>. When a translation is required,
|
|
|
86 |
this <tt>Map</tt> is used as an in-memory lookup table. This avoids
|
|
|
87 |
repeated fetching from the database of trivial data that rarely changes.
|
|
|
88 |
|
|
|
89 |
<P><b>Note on Thread Safety</b>
|
|
|
90 |
<br>In the suggested implementation style, the <tt>private static Map</tt> that stores translation data is
|
|
|
91 |
<tt>static</tt>, and thus shared by multiple threads. Implementations are free to implement
|
|
|
92 |
any desired policy regarding thread safety, from relaxed to strict. A relaxed implementation
|
|
|
93 |
might completely ignore the possibility of a mistaken read/write, since no writes are expected in
|
|
|
94 |
production, or simply because the consequences are usually trivial. A strict implementation would
|
|
|
95 |
take the opposite approach, and demand that the <tt>private static Map</tt> be used in an fully
|
|
|
96 |
thread-safe manner.
|
|
|
97 |
*/
|
|
|
98 |
public interface Translator {
|
|
|
99 |
|
|
|
100 |
/**
|
|
|
101 |
Translate <tt>aBaseText</tt> into text appropriate for <tt>aLocale</tt>.
|
|
|
102 |
|
|
|
103 |
<P><tt>aBaseText</tt> is either user-presentable text in the base language of
|
|
|
104 |
development, or a "coder key" known only to the programmer.
|
|
|
105 |
|
|
|
106 |
<P>If <tt>aBaseText</tt> is unknown, then the implementation is free to define
|
|
|
107 |
any desired behavior. For example, this method might return
|
|
|
108 |
<ul>
|
|
|
109 |
<li><tt>aBaseText</tt> passed to this method, in its raw, untranslated form
|
|
|
110 |
<li><tt>aBaseText</tt> with special surrounding text, such as <tt>???some text???</tt>, with
|
|
|
111 |
leading and trailing characters
|
|
|
112 |
<li>a fixed <tt>String</tt> such as <tt>'???'</tt> or <tt>'?untranslated?'</tt>
|
|
|
113 |
<li>it is also possible for an implementation to throw a {@link RuntimeException} when
|
|
|
114 |
an item is missing, but this is not recommended for applications in production
|
|
|
115 |
</ul>
|
|
|
116 |
|
|
|
117 |
<P>If <tt>aBaseText</tt> is known, but there is no explicit translation for the
|
|
|
118 |
given {@link Locale}, then an implementation might choose to mimic the behavior of
|
|
|
119 |
{@link java.util.ResourceBundle}, and choose a translation from the "nearest available"
|
|
|
120 |
{@link Locale} instead.
|
|
|
121 |
|
|
|
122 |
@param aBaseText is not <tt>null</tt>, but may be empty
|
|
|
123 |
@param aLocale comes from the configured {@link LocaleSource}.
|
|
|
124 |
*/
|
|
|
125 |
String get(String aBaseText, Locale aLocale);
|
|
|
126 |
|
|
|
127 |
}
|