UNPKG

closure-builder

Version:

Simple Closure, Soy and JavaScript Build system

362 lines (275 loc) 12.5 kB
# Message Localization [TOC] ## Message translation ### Marking a Message for Translation {#marking} 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](../reference/messages.md#msg). `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. ### Adding new messages {#newmessages} When a new message is added to a `.soy` file, there is no translation available for that message yet. If the message is immediately used in the product, it may appear in English to users of other languages. Therefore, the message should be introduced to be translated before it is actually used. There are two ways to do this, depending on whether you are adding a new message or modifying an existing message. For new messages, add the new message in an unused template: ```soy {template .unusedTemplateForTranslations} {msg desc="..."}...{/msg} {/template} ``` When modifying existing messages, use the `fallbackmsg` command described in the [commands documentation](../reference/messages.md#fallbackmsg). ### Extracting Messages {#extractingmessages} To parse and extract messages from a bundle of `.soy` files, download [`closure-templates-msg-extractor-latest.zip`](https://dl.google.com/closure-templates/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](http://docs.oasis-open.org/xliff/xliff-core/xliff-core.html). If you need to extract a message to a different message file format, follow the instructions in the [Message Plugins](plugins.md#message) 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 files `aaa.soy` and `bbb.soy`, and you want to extract messages to a file called `extracted_msgs.xlf`, run the following command: ```shell $ 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. ```shell $ 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. ```shell $ java -jar SoyMsgExtractor.jar --outputPathFormat "{INPUT_DIRECTORY}/../messages/{INPUT_FILE_NAME_NO_EXT}.xlf" ... ``` To see a description of all the flags, run ```shell $ java -jar SoyMsgExtractor.jar ``` without any options. ### Inserting Messages {#insertingmessages} 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`: ```java SoyMsgBundleHandler msgBundleHandler = new SoyMsgBundleHandler(new XliffMsgPlugin()); 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.Renderer` or `SoySauce.Renderer` object's `setMsgBundle()` method. #### 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: ```shell $ 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: ```shell $ 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 {#closurecompiler} 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 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 compiler so it can correctly handle [bidi functions and directives](#bidi_functions). For example, consider this `msg` block: ```soy {msg desc="Says hello and tells user to click a link."} Hello {$userName}! Please click <a href="{$url}">here</a>. {/msg} ``` If you compiled this template with the option `--should_generate_goog_msg_defs`, then the resulting `goog.getMsg` definition might be: ```js /** @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>'}); ``` ## Using Multiple Natural Languages (Bidi) {{product_name_templates}} support bidirectional text (bidi) with seven functions and two print directives. Note that you might suffer a slight performance loss when using some of these functions or directives because some of them can only be applied at render time. By default, the template compiler determines a global directionality (LTR or RTL) for the template content based on the language of the message bundle. Provide the message bundle at compile time for the JavaScript source backend and at render time for the Java object backend. If you don't provide a message bundle, the global directionality defaults to LTR. The global directionality only affects the operation of `Closure Templates` when you explicitly use bidi functions or directives. For an example of a template that uses Bidi functions, see [`examples/features.soy`](https://github.com/google/closure-templates/blob/master/examples/features.soy). ### Bidi Functions in `Closure Templates` {#bidi_functions} {{#external}} <table> <thead> <tr> <th>Function</th> <th>Usage</th> </tr> </thead> <tbody> <tr> <td><code>bidiGlobalDir()</code> </td> <td>Provides a way to check the current global directionality. Returns 1 for LTR or -1 for RTL.</td> </tr> <tr> <td><code>bidiDirAttr(text, opt_isHtml)</code> </td> <td>If the overall directionality of <code>text</code> is different from the global directionality, then this function generates the attribute <code>dir=ltr</code> or <code>dir=rtl</code>, which you can include in the HTML tag surrounding that piece of text. If the overall directionality of <code>text</code> is the same as the global directionality, this function returns the empty string. Set the optional second parameter to <code>true</code> if <code>text</code> contains or can contain HTML tags or HTML escape sequences (default <code>false</code>).</td> </tr> <tr> <td><code>bidiMark()</code> </td> <td>Generates the bidi mark formatting character (LRM or RLM) that corresponds to the global directionality. Note that if you don't want to insert this mark unconditionally, you should use <code>bidiMarkAfter(text)</code> instead.</td> </tr> <tr> <td><code>bidiMarkAfter(text, opt_isHtml)</code> </td> <td>If the exit (not overall) directionality of <code>text</code> is different from the global directionality, then this function generates either the LRM or RLM character that corresponds to the global directionality. If the exit directionality of <code>text</code> is the same as the global directionality, this function returns the empty string. Set the optional second parameter to <code>true</code> if <code>text</code> contains or can contain HTML tags or HTML escape sequences (default <code>false</code>). You should use this function for an inline section of text that might be opposite directionality from the global directionality. Also, set <code>text</code> to the text that precedes this function.</td> </tr> <tr> <td><code>bidiStartEdge()</code> </td> <td>Generates the string "left" or the string "right", if the global directionality is LTR or RTL, respectively.</td> </tr> <tr> <td><code>bidiEndEdge()</code> </td> <td>Generates the string "right" or the string "left", if the global directionality is LTR or RTL, respectively.</td> </tr> <tr> <td><code>bidiTextDir(text, opt_isHtml)</code> </td> <td>Checks the provided text for its overall (i.e. dominant) directionality. Returns 1 for LTR, -1 for RTL, or 0 for neutral (neither LTR nor RTL). Set the optional second parameter to <code>true</code> if <code>text</code> contains or can contain HTML tags or HTML "escapes" (default <code>false</code>).</td> </tr> </tbody> </table> <table> <thead> <tr> <th>Function</th> <th>Usage</th> </tr> </thead> <tbody> <tr> <td><code>│bidiSpanWrap</code> </td> <td>If the overall directionality of the <code>print</code> command is different from the global directionality, then the compiler wraps the <code>print</code> command output in a span with <code>dir=ltr</code> or <code>dir=rtl</code>.<br><br>The template compiler applies autoescaping before evaluating <code>│bidiSpanWrap</code>, which is safe because <code>│bidiSpanWrap</code> correctly handles HTML-escaped text. If you're manually escaping the output using <code>│escapeHtml</code>, be sure to put <code>│escapeHtml</code> before <code>│bidiSpanWrap</code>, or else you'll end up escaping any span tags that are generated.)</td> </tr> <tr> <td><code>│bidiUnicodeWrap</code> </td> <td>If the overall directionality the <code>print</code> command is different from the global directionality, then the compiler wraps the <code>print</code> command output with Unicode bidi formatting characters LRE or RLE at the start and PDF at the end.<br><br>This directive serves the same purpose as <code>│bidiSpanWrap</code>, but you should only use it in situations where HTML markup is not applicable, for example inside an HTML <code>&lt;option&gt;</code> element.</td> </tr> </tbody> </table> {{/external}} ## Message Plugins {#message_plugins} To write a plugin for a custom message type, use the package `xliffmsgplugin` as an example by writing a class that implements the `SoyMsgPlugin` interface. To use your message plugin with the executable jars, pass the plugin's Guice module class to the command-line flag `--messagePlugin`.