I18N¶
THIS SECTION IS OUT OF DATE & REQUIRES REVIEW
Introduction¶
Early in the development of Krail it was decided to support I18N as an integral part of the development. Although many applications only need to support one language, trying to add internationalisation (I18N) later can be a major challenge. I18N requires the separation of literal strings into files for translation, but this actually makes good sense even if only one language is required, since it keeps a good separation between the use of messages and the exact wording of them. In addition, the need for parameterised messages will occur regardless of the number of languages supported - so we concluded that it makes sense to always write an application as if I18N will be required. And if one day your single language application suddenly has to go multilingual, the only thing required will be the translations.
The Basics¶
In the context of I18N, each piece of text needs a pattern, optionally with placeholders for variable values, and a key to look up that pattern for one or more Locales - then there needs to be something to bring it all together to find the right pattern for a selected Locale, fill in variable values and provide the result.
The Pattern¶
The pattern needs to be of the form:
The {1} task completed {0} iterations in {2} seconds
Different languages may require the parameters to be in a different order - the number in the {0} represents the order in which the values should be assigned, so for this example values of 5, “last”, 20 will become:
The last task completed 5 iterations in 20 seconds
The Key¶
A key in Krail is an enum. This has many advantages over the usual approach of using String constants, especially when combining modules which may need to define their own keys in isolation from each other. They are also more refactor-friendly. An I18N key class must implement the I18NKey interface:
public enum LabelKey implements I18NKey { Yes, No, Cancel }
A key class represents a “bundle”.
The Bundle¶
The term “bundle” is used throughout native Java I18N support and Krail uses the term in a similar way. It represents an arbitrary set of keys and the collection of patterns, of potentially multiple languages, that go with the keys. An enum implementation I18NKey class therefore represents a set of keys for a bundle.
Bundle Reader¶
Patterns potentially come from different sources. Krail supports the property file system used by native Java. It also provides a class based implementation, and the Krail JPA module provides a database implementation. All of these - and others if required - implement a BundleReader interface to read a pattern from a file, class, database - perhaps a web service - or wherever the implementation is designed to work with.
Pattern Source¶
The PatternSource
combines inputs from potentially multiple
BundleReader`s into one source. This is configurable through `I18NModule
to query the readers in whatever order is required - the first to return
a value is used. If necessary a different order for each Bundle, so a
database source could be the primary for one bundle and secondary for
another.
Translate¶
The Translate
class is the final step in bringing the pieces together.
It looks up the pattern for a Locale
, via the PatternSource
, and
combines that with the parameter values it is given. For the example
above the call would be:
translate.from (MessageKey.Task_Completion, Locale.UK, 5, "last", 20)
If Translate
cannot find a pattern, it will default to using the key
name (with underscores replaced with spaces). This is useful when
prototyping, as the pattern can still be meaningful even if not strictly
accurate. That’s why you will find many of the Krail examples break with
the convention of using all uppercase for the I18NKey enum constants.
Note that if “last” also need to be translated, Translate
will accept
and perform a nested translation on an I18NKey (though the nested value
cannot have parameters - if that is required, two calls to Translate
will be needed)
translate.from (MessageKey.Task_Completion, Locale.UK, 5, LabelKey.last, 20)
You do not always have to specify the Locale - the default is to use CurrentLocale.
Current Locale¶
The CurrentLocale
implementation holds the currently selected locale
for the user. The default implementation checks things like the browser
locale and user options to decide which locale to use. CurrentLocale can
be injected anywhere it is required, and the Translate class will use it
if no specific Locale is supplied when calling the from() method.
Configuration¶
A number of things can be configured in the I18NModule
, part of the
Guice based configuration - it is worth checking the javadoc for this.
Some configuration is also available via Option
.
Managing Keys¶
To make it just a little easier to find values in what can be a long list, the Krail core uses 3 enum classes to define message patterns:
- Labels : short, usually one or two words, no parameters, generally used as captions
- Descriptions : longer, typically several words, no parameters, generally used in tooltips
- Messages : contains parameter(s).
Note that this is simply a convention - you can call them whatever you wish.
For each there is enum lookup key class:
- LabelKey,
- DescriptionKey,
- MessageKey.
For a class implementation there needs also to be a corresponding map of
value (default names of Labels, Descriptions and Messages) extended from
EnumResourceBundle
. For a property file implementation there needs
to be a file (or set of files for different languages)
Using enums as I18N keys has some advantages, particularly for type checking and refactoring - but it also has a disadvantage. Enums cannot be extended. To provide your own keys (which you will unless you only use those provided by Krail) you will need to define your own I18NKey implementation, as described in the Tutorial - Extending I18N.
Managing Locale¶
CurrentLocale¶
`CurrentLocale`
holds the locale setting for the current `VaadinSession`
. Once a user has logged in, it is also possible to set the locale for a specific component, using the annotations described below.
Using I18N with Components¶
A typical component will need a caption, description (tooltip) and potentially a value. These need to be set in a way which recognises the correct locale, and potentially to update if a change of locale occurs.
@Description¶
Similar to @Caption, but without the caption !
@Value¶
Usually, it is the caption and description which would be subject to internationalisation, but there are occasions when it is a component’s value which should be handled this way - a Label is commonly an example of this. Because the use of value is a little inconsistent in this context it has its own annotation.
Multiple annotations¶
You can apply multiple annotations - but note that if you define the locale differently in the two annotations, the result is indeterminate (that is, it could be either of the two locales that have been set).
Composite Components and Containers¶
There are occasions when an object contains components, and may not be a component itself, or possibly just not need translation.
For example, you have a composite component MyComposite
which itself does not need a caption or description - but it contains components which do. For these cases, simply annotate it with @18N without any parameters, and I18NProcessor
will scan MyComposite
for any fields which need processing.
If `MyComposite
is intended to be re-usable, it would probably be better to annotate the class with @I18N, so that it does not need to be annotated each time it is used.
Extending I18N¶
Annotation parameters cannot be generics, so will need to provide your own equivalent of @Caption, @Description and @Value to use your keys for annotating components for translation. The method for doing this is described in the Tutorial - Extending I18N.
Validation¶
The messages used in validation can be supported in the same way .. see the Validation section for details.