Note: All files of Ninja are encoded in UTF-8. Yes. Also the .properties files. If you edit the files make sure your editor actually uses UTF-8 and not the default ISO encoding!
First you need a definition of supported languages in your conf/application.conf
file.
This is a simple comma separated list (whitespaces are omitted).
application.languages=en,de
The languages are one or two part ISO coded languages. Usually they resemble language or language and country. Examples are “en”, “de”, “en-US”, “en-CA” and so on.
The message file name follows a convention. The convention is messages_LANGUAGE.property or messages_LANGUAGE-COUNTRY.property.
Some examples:
conf/messages_en.properties might look like:
# registration.ftl.html casinoRegistrationTitle=Register casinoRegistrationEmail=Your email casinoRegistrationConfirm=Confirm casinoRegistrationAcceptTermsOfService=Accept terms of service casinoRegistrationRegister=Register casinoRegistrationFlashError=An error occurred. casinoYourUsername=Your username is: {0} # registrationPending.ftl.html registrationPleaseVerifyEmailAddress=Please check your email inbox to verify your account. registrationPendingError=Error confirming email. registrationPendingSuccess=Success confirming email.
Internally we use MessageFormat.format(text, values) to format the messages. Therefore all the information from https://docs.oracle.com/javase/7/docs/api/java/text/MessageFormat.html does apply.
Ninja provides the message through the class Messages.
You can inject and use Messages in your application like so:
public class ApplicationController { Messages msg @Inject ApplicationController(Messages msg) { this.msg = msg } public Result controllerMethod(Context context) { Optional<String> language = Optional.of("en"); Optional<Result> optResult = Optional.absent(); // messages use messageFormat. If you use placeholders, messages can format them for you. Object [] messageParamters = {"kevin"}; String message1 = "localized message1: " + msg.get("casinoRegistrationTitle", language); // This will determine the language from context and result: String message2 = "localized message2: " + msg.get("casinoYourUsername", context, optResult, messageParamters); return Results.text(message1 + " " + message2); } }
Inside a Freemarker template (ftl.html) you can get internationalized messages by using
<html> <head> <title>${i18n("casinoRegistrationTitle")}</title> </head> <html>
You can also format messages automatically:
<html> <head> <title>${i18n("casinoYourUsername", username)}</title> </head> <html>
${i18n("my.message.key")}
will be displayed as "my.message.key" inside the template if your messages.properties
file is missing that key.
You can define fallback messages in the file message.properties.
Ninja always looks up messages from more specific to less specific.
Example: The user requests a message in “en-US”. The lookup then is
If you specify
application.languages=en
It makes sense to only have one message file called messages.properties in English. Therefore English acts as fallback for all languages - country combinations.
Ninja tries to do its best to determine the language from the Accept-Language header. But there are times, when it makes sense to ignore the header and force the usage of a certain language.
Ninja provides that possibility by a cookie. The cookie is usually called NINJA_LANG and contains only one value - the language to use for this user.
You can set the language by using the Lang tools like so:
@Inject Lang lang; public Result index() { Result result = Results.html().ok(); lang.setLanguage("de", result); return result; }
After setting the language all messages will be displayed in German.
The flash scope is available in the template via e.g. ${flash.error}. There is a simple rule regarding i18n: If the value of the flash scope key (eg “error”) can be found in the messages the translated version is used. Otherwise the value is used without any translation.
Consider the messages file introduced some sections above. If you'd use casinoRegistrationFlashError as error in your flash cookie it would be automatically translated into “An error occurred”. Using “An error occurred - please check your input” as value won't trigger any translation as the value cannot be found.
One note: This automatic translation facility cannot be used when placeholders aka {0} are used. In that case you have to translate the message in your controller and set the translated value yourself (See the demo application for more hints).
Translating your messages with placeholders inside your controller would look like:
public Result flashError(Context context) { Result result = Results.html(); Optional<String> flashMessage = messages.get("flashError", context, Optional.of(result), "PLACEHOLDER"); if (flashMessage.isPresent()) { context.getFlashScope().error(flashMessage.get()); } return result; }