1. Marking a Message for Translation
  2. Extracting Messages
  3. Inserting Messages

Marking a Message for Translation

In Closure Templates, messages for translation are inlined rather than stored in separate files. To mark a message for translation, surround the message text with the msg tag as described in the Commands chapter. Closure Templates can extract your message into the XLIFF. Furthermore, use translated message files in the same format and insert the translated text back into your template.

Extracting Messages

To parse and extract messages from a bundle of Soy files, download closure-templates-msg-extractor-latest.zip and extract the latest revision of SoyMsgExtractor.jar. By default, SoyMsgExtractor uses the plugin XliffMsgPlugin, which supports the industry standard XLIFF message file format. If you need to extract a message to a different message file format, follow the instructions in the Message Plugins section of the Plugins chapter.

After you have downloaded the message extractor, run it to extract your messages by passing in the source Soy file and the filename for the extracted messages. For example, if you have two Soy files aaa.soy and bbb.soy, and you want to extract messages to a file called extracted_msgs.xlf, run the following command:

$ java -jar SoyMsgExtractor.jar  --outputFile extracted_msgs.xlf  aaa.soy bbb.soy

To specify the target language as seen by message plugins, use the --targetLocaleString flag.

$ java -jar SoyMsgExtractor.jar --outputFile extracted_msgs.xlf --targetLocaleString zh-TW *.soy

Instead of specifying a single output file, you can use --outputPathFormat to derive it from the input file paths.

$ java -jar SoyMsgExtractor.jar --outputPathFormat "{INPUT_DIRECTORY}/../messages/{INPUT_FILE_NAME_NO_EXT}.xlf" ...

To see a description of all the flags, run

$ java -jar SoyMsgExtractor.jar

without any options.

Inserting Messages

Because the template compiler can use the translated messages file directly with the help of an appropriate message plugin, you don't need to run an additional preprocessing step to change them to a different format.

In Java usage

To insert messages in Java usage, use a SoyMsgBundleHandler to create a SoyMsgBundle that contains your translated messages. For example, if you have a translated messages file called translated_msgs_pt-BR.xlf for the locale pt-BR, use the code snippets below to create a corresponding SoyMsgBundle:

  • If you're not using Guice:

    SoyMsgBundleHandler msgBundleHandler = new SoyMsgBundleHandler(new XliffMsgPlugin());
    SoyMsgBundle msgBundle = msgBundleHandler.createFromFile(new File("translated_msgs_pt-BR.xlf"));
  • If you are using Guice:

    First, add the message plugin's Guice module to your injector:

    Injector injector = Guice.createInjector(..., new SoyModule(), new XliffMsgPluginModule());

    Then inject a SoyMsgBundleHandler:

    @inject SoyMsgBundleHandler msgBundleHandler;
    SoyMsgBundle msgBundle = msgBundleHandler.createFromFile(new File("translated_msgs_pt-BR.xlf"));

After you have a SoyMsgBundle object that contains your translated messages, you can pass it to your SoyTofu object's render() method as the third parameter.

In JavaScript usage

To insert messages in JavaScript usage, pass the translated messages file to SoyToJsSrcCompiler, which generates JavaScript code that contains the translated messages. For example, if you want to create a translated messages file called translated_msgs_pt-BR.xlf for locale pt-BR, run this command:

$ java -jar SoyToJsSrcCompiler.jar  --locales pt-BR  --messageFilePathFormat translated_msgs_pt-BR.xlf  \
        --outputPathFormat '{INPUT_FILE_NAME_NO_EXT}_pt-BR.js'  aaa.soy bbb.soy

This command generates the files aaa_pt-BR.js and bbb_pt-BR.js.

The SoyToJsSrcCompiler can simultaneously compile to multiple locales. For example, to compile to locales en, de, and pt-BR (assuming your translated messages files are named translated_msgs_en.xlf, translated_msgs_de.xlf, and translated_msgs_pt-BR.xlf), run this command:

$ java -jar SoyToJsSrcCompiler.jar  --locales en,de,pt-BR  --messageFilePathFormat translated_msgs_{LOCALE}.xlf \
        --outputPathFormat '{INPUT_FILE_NAME_NO_EXT}_{LOCALE}.js'  aaa.soy bbb.soy

This command generates six files, one for each combination of Soy source file and locale: aaa_en.js, bbb_en.js, aaa_de.js, bbb_de.js, aaa_pt-BR.js, and bbb_pt-BR.js.

Letting Closure Compiler Handle Translation

If your project only uses Closure from JavaScript, an alternative translation solution is to let Closure Compiler handle the translation of messages, just as it would for your hand-written JavaScript. However, if you share templates between Java and JavaScript, you should always use Closure Templates to handle messages because of correctness issues.

To let Closure Compiler handle the extraction and insertion of messages, run the SoyToJsSrcCompiler with these options:

  • --should_generate_goog_msg_defs: causes the Soy compiler to turn all msg blocks into goog.getMsg definitions (and their corresponding usages). These goog.getMsg definitions can be translated by the JS Compiler.

  • --bidi_global_dir=<1/-1>: provides the bidi global directionality (ltr=1, rtl=-1) to the Soy compiler so it can correctly handle bidi functions and directives.

For example, consider this msg block:

{msg desc="Says hello and tells user to click a link."}
  Hello {$userName}! Please click <a href="{$url}">here</a>.

If you compiled this template with the option --should_generate_goog_msg_defs, then the resulting goog.getMsg definition might be:

/** @desc Says hello and tells user to click a link. */
var MSG_UNNAMED_42 = goog.getMsg(
    'Hello {$userName}! Please click {$startLink}here{$endLink}.',
    {'userName': soy.$$escapeHtml(opt_data.userName),
     'startLink': '<a href="' + soy.$$escapeHtml(opt_data.url) + '">',
     'endLink': '</a>'});