Internationalization / Localization
Content
How to use localization in your code
JEAF X-Fun provides a rather simple but powerful mechanism to localize all kinds of strings like messages, UI labels etc.
This mechanism is based on a resource where the actual messages in all localization are stored and a generated Java class with constants for all localized strings (so called message constants). Message constants will be generated by JEAF Generator, a simple Maven Plugin, that is integrated into your build process (also see section about JEAF Maven Plugins). As a resource for messages either plain XML files or Excel sheets can be used. Excel files will be transformed into XML files by JEAF Generator Maven Plugin to avoid dependencies on Excel libraries at runtime.
JEAF Generator will generate a Java class with message constants for every message resource. This class will have a constant for every message. Depending on the type of the message the constant will be of a different type.
JEAF knows the following types of messages:
LocalizedString
can used used for any kind of text that should be localized such as labels for UI etc.MessageID
Any kind of message that should be also be traceable.ErrorCode
should be used in case of business or technical errors and can be used to provide error message e.g. as part of exceptions.
Message Constants generated by JEAF Generator
/**
* Demo class for generation of message constants.
*
* @author JEAF Development Team
* @version 1.5.x
*/
@MessageResource(path = "accounting-messages.xml")
public final class AccountingMessages {
/**
* Localized text for user interface label showing transaction amount.
*/
public static final LocalizedString TX_AMOUNT_LABEL;
/**
* Trace message when executing SWIFT booking.
*/
public static final MessageID SWIFT_BOOKING;
/**
* Error message in case that a money transfer is not possible due to insufficient bank balance.
*/
public static final ErrorCode BANK_BALANCE_NOT_SUFFICIENT;
Usage of the messages is really simple as you can see in the example code below.
How to use message constants in your code
// No matter what type of message we have, all of them provide several variants of toString(...) that can be used to get the actual message
LocalizedString lLocalizedString = AccountingMessages.TX_AMOUNT_LABEL;
// If no specific locale is passed to toString() then the current locale as returned from the configured locale provider will be used.
lLocalizedString.toString();
// Of course it is also possible to explicitly define the locale that should be returned.
lLocalizedString.toString(Locale.GERMAN)
// Often messages need to be parameterized with some data from the application. Therefore we make use of notation as defined by class java.text.MessageFormat
// In the following case we would like to create a message like "Executing SWIFT booking from account '1234 0000 0000 9876 12' to '9999 0000 0000 1111 11'."
// Inside the message resource the message will be define as "Executing SWIFT booking from account ''{0}'' to ''{1}''."
// For usage of current default locale just use simple toString(...) with parameters of the message
AccountingMessages.SWIFT_BOOKING.lMessage.toString("1234 0000 0000 9876 12", "9999 0000 0000 1111 11"));
// If you want the message to be written in a specific locale then you can also provide it as an additional parameter.
AccountingMessages.SWIFT_BOOKING.lMessage.toString(Locale.GERMAN, "1234 0000 0000 9876 12", "9999 0000 0000 1111 11"));
// In addition to the already used message types (LocalizedString, MessageID) also a type for error codes
// (ErrorCode) is available. Whenever JEAF Generator detects a message of type error then it will generate an
// constant of type ErrorCode. Usage of errors code is just the same as for messages and localized text, but in
// addition error codes are part of JEAF mechanism of exception handling.
ErrorCode lErrorCode = AccountingMessages.BANK_BALANCE_NOT_SUFFICIENT;
assertEquals("Money transfer failed. Transaction amount: 100.00 EUR", lErrorCode.toString("100.00 EUR"));
// All types of message constants can also be passed to JEAF's tracing mechanism
XFun.getTrace().info(lErrorCode, "100.00 EUR");
How to define localized messages
Independent of the message resource where you want to define your messages the information itself is in both cases (XML or Excel) the same.
Field | Type | Multiplicity | Example | Description |
---|---|---|---|---|
|
|
|
| Message-ID is an unique numeric ID for a message. The number range from 1 - 19'999 is reserved for JEAF itself. Numbers starting from 20'000 and above can be used by applications |
|
|
|
| Name for the message. This name will also be used inside the generated code as the name for the constant. By convention all messages have uppercase only name like all constants in Java e.g. BANK_BALANCE_NOT_SUFFICIENT |
|
|
|
| There are three possible values:
|
|
|
|
| Defines the default trace level of the message. |
|
|
|
| Parameter defines if the generated constant should be marked as deprected. |
|
|
|
| Description of the message. This description will also be used as Javadoc comment for the generated constant. |
|
|
|
| Default value for message. This default will be used when more specific localizations are not available. |
|
|
|
| In addition to the default message your are free to provide as many additional localization of a message as you like. Here the way how you have to provide the information depends on the message resource format, but in both cases it's rather simple |
In addition to the messages itself you also have to define the name of the class that should be be created by JEAF Generator to hold all the message constants.
How to use plain XML files as resource for messages
Image below shows an example snippet of an XML based message resource. Full version of the file can be downloaded here
If you configured JEAF Generator Maven Plugin as described above then all you have to do is to put the XML file with the message resources to your resource directory (most likely src/main/resources
) and JEAF Generator will create message constants out of it during build process. You can also run the code generation by just executing phase generate-sources
.
Message resource definition with XML
<Message
messageID="20000"
name="SWIFT_BOOKING"
type="INFO"
traceLevel="INFO"
defaultText="Executing SWIFT booking from account ''{0}'' to ''{1}''."
description="Trace message when executing SWIFT booking." >
<LocalizedMessage language="de" country="" variant="" localizedText="Executing SWIFT booking from account ''{0}'' to ''{1}'' (Language: de)"/>
<LocalizedMessage language="de" country="CH" variant=""localizedText="Executing SWIFT booking from account ''{0}'' to ''{1}'' (Language: de_CH)" />
</Message>
How to use Microsoft Excel as resource for messages
If you prefer to use Microsoft Excel to manage your messages then this is as good as well. In case of Excel as message resource JEAF X-Fun provides an Excel workbook as template (messages-template.xlsx) that can should used as starting point (please also see the example file).
In case of Excel JEAF Generator will do two things during build:
First of all it will transform the provided Excel workbook into an XML file (same structure as if you wrote it manually)
Afterwards based on the generated XML file message constants will be generated
As before you will have to put the Excel file to your resource directory (e.g. src/main/resources
) and JEAF Generator will create an XML file and message constants out of it during build process. You can also run the code generation by just executing phase generate-sources
.
Locale Providers
Until now we already talked a lot about how to localize your application and how to provide the messages. However what is still missing is the way how we find out about the current language. As you saw in the examples from above it's not mandatory to provide a language when making use of JEAF's localization mechanism.
Whenever things need to be localized but a concrete locale is not provided as parameter (and this should be the default) then JEAF uses a so called LocaleProvider
to resolve the current language. Depending on the application this can simply be the language of the current operating system user or the language of the logged in user in an web application or anything else. Thus you can choose between multiple locale providers or implement your own. JEAF's default locale provider just uses the JDK default locale / language which by default is taken from the OS user under which the application is run.
If you not only make use of JEAF X-Fun but also of JEAF Core then you can also use a user-based locale provider without having to implement you own solution (please refer to JEAF Core and especially to class UserPrincipalBasedLocaleProvider
).
How to implement your own locale provider
To make all that little more concrete we will now see how to implement and configure our own LocaleProvider
. Therefore the following three steps need to be done:
Implement our own version of a
LocaleProvider
Implement our own version of interface
LocaleProviderFactory
Register your
LocaleProviderFactory
in@XFunConfig
Let's start with our implementation of a LocaleProvider
. First of all we have to implement our own locale provider MyLocaleProvider
.
LocaleProvider implementation
Next, we need a factory.
LocaleProviderFactoryImplementation
As mentioned before our factory needs to be registered in JEAF's X-Fun configuration. In general all configurations for JEAF are provided through annotations. As locale providers are something that belong to JEAF X-Fun it is configured via annotation @XFunConfig
. It's best practice to have a special or interface to be the holder for such kind of information. In case of the sample application this is class XFunSample
. This class is annotated with @XFunConfig
. Here we will add the reference to our LocaleProviderFactory
implementation MyLocaleProviderFactory
and that's it.
At build time JEAF Maven Plugin will detect the annotation and generate the required configuration.
Final step is to check if our LocaleProvider
is really used. Therefore we simply start JUnit tests and check the really used locale provider.
Test Code
Another way to check if our LocaleProvider
is really used, is to check the logs that are written by JEAF X-Fun during startup. There you will find an overview over the whole configuration. This also includes the used LocaleProviderFactory
.