Skip to main content

Internationalization

To provide a better user experience, it is important to reach user with applicative messages that match his language.

Prerequisites

Locale information

Information about the user's language preference is intercepted by the SDK (from HTTP Accept-Language header) as a locale code: the locale code is expected to be in the format [LANGUAGE]-[COUNTRY], en-US or it-IT simply by language like en or it (although other combinations can be possible). The preferred locale code for the current user is picked as the first in the string. An example of locale code is: en-US,en;q=0.9,it;q=0.8.

SDK will look for the language part only of the first locale string token, to know which language file to load, and for now, the country part will be discarded. The language token will be used to load a [LANGUAGE].json file from the resources/lang directory. In the example above the picked language is en (that is stripped from en-US), so the SDK will look for a file named en.json.

Create a language file

A language file is a free-form JSON file.

// en.json
{
"auth": {
"unknownUser": "User ID is not known: {}",
}
}
// it.json
{
"auth": {
"unknownUser": "ID utente sconosciuto: {}"
}
}

The corresponding folder structure will be:

resources/
└── lang/
├── en.json
└── it.json

Each JSON value will be reachable by a path that is the concatenation of the JSON keys separated by a dot (.). In the example above the path of the message ID utente sconosciuto: {} is auth.unknownUser.

Use a message translation

The user language preference comes to the mEx encapsulated into an ExecutionContext object, that is the context of the currently executing FUN: since each FUN can come from different users each FUN can bring its own language preference. During ExecutionContext creation the chosen language file is loaded so it's ready to be used. Suppose to be in the presence of a variable context of type ExecutionContext; translation method is available by using:

String message = context.getLanguage().translate("auth.unknownUser");

If the user locale is it this will be the output of the message:

System.out.println(message);
// ID utente sconosciuto: {}

If the JSON path requested does not exist, the SDK will return a warning message (e.g. "Translation for '[PATH]' not found") when a translation of any key is attempted.

String message = context.getLanguage().translate("a.b.c");
System.out.println(message);
// Translation for 'a.b.c' not found"

Message interpolation

The message can contain placeholders that will be replaced by the SDK with the values passed as arguments. The placeholders are represented by {} and the values are passed as arguments to the translate method.

String message = context.getLanguage().translate("auth.unknownUser", "John");
System.out.println(message);
// ID utente sconosciuto: John

A better approach

Using an enum object is a better approach to store application messages, and it will be enough to implement LanguageMessageProvider.

public enum ApplicationMessage implements LanguageMessageProvider {
UNKNOWN_USER("auth.unknownUser");

private final String path;
ApplicationMessage(String path) { this.path = path; }

@Override
public String getPath() {
return path;
}
}

and use it in this way:

String message = context.getLanguage().translate(ApplicationMessage.UNKNOWN_USER);

Language fallback

When a language extracted from locale code does not match any language file, the SDK will fallback to the default language file. The default language file is en.json and it is always loaded from the directory resources/lang. If the fallback language file is not found, the SDK will return a warning message (e.g. "Translation file not found") when a translation of any key is attempted.

String message = context.getLanguage().translate("auth.unknownUser");
System.out.println(message);
// "Translation file not found"