From 9cae3450ff5b4793b0de8cc6b54d310ffd9b9f4d Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Sat, 10 Apr 2021 00:38:17 -0700 Subject: [PATCH] docs: Update documentation for FormatJS migration. Signed-off-by: Anders Kaseorg --- docs/subsystems/html-css.md | 2 - docs/translating/internationalization.md | 89 ++++++++---------------- docs/tutorials/writing-views.md | 2 +- frontend_tests/node_tests/i18n.js | 5 +- 4 files changed, 32 insertions(+), 66 deletions(-) diff --git a/docs/subsystems/html-css.md b/docs/subsystems/html-css.md index fb1b7d4f05..032d680299 100644 --- a/docs/subsystems/html-css.md +++ b/docs/subsystems/html-css.md @@ -277,8 +277,6 @@ function in those scenarios, add it to `zulip_test`. This is also [Jinja2]: http://jinja.pocoo.org/ [Handlebars]: https://handlebarsjs.com/ [trans]: http://jinja.pocoo.org/docs/dev/templates/#i18n -[i18next]: https://www.i18next.com -[official]: https://www.i18next.com/plurals.html [jconditionals]: http://jinja.pocoo.org/docs/2.9/templates/#list-of-control-structures [hconditionals]: https://handlebarsjs.com/guide/#block_helpers.html [translation]: ../translating/translating.md diff --git a/docs/translating/internationalization.md b/docs/translating/internationalization.md index 00d1a361b6..0d1a65ee52 100644 --- a/docs/translating/internationalization.md +++ b/docs/translating/internationalization.md @@ -77,7 +77,7 @@ The end-to-end tooling process for translations in Zulip is as follows. [frontend](#frontend-translations) translations for details on this). -2. Translation [resource][] files are created using the `./manage.py +2. Translation resource files are created using the `./manage.py makemessages` command. This command will create, for each language, a resource file called `translations.json` for the frontend strings and `django.po` for the backend strings. @@ -208,73 +208,46 @@ Zulip linter (`tools/lint`) attempts to verify correct usage. ## Frontend translations -We use the [i18next][] library for frontend translations when dealing +We use the [FormatJS][] library for frontend translations when dealing with [Handlebars][] templates or JavaScript. To mark a string translatable in JavaScript files, pass it to the -`i18n.t` function. +`intl.formatMessage` function, which we alias to `$t` in `intl.js`: -``` -i18n.t('English Text', context); +```js +$t({defaultMessage: "English Text"}) ``` -Variables in a translated frontend string are enclosed in -double-underscores, like `__variable__`: +The string to be translated must be a constant literal string, but +variables can be interpolated by enclosing them in braces (like +`{variable}`) and passing a context object: -``` -i18n.t('English text with a __variable__', {'variable': 'Variable value'}); +```js +$t({defaultMessage: "English text with a {variable}"}, {variable: "Variable value"}) ``` -`i18next` also supports plural translations. To support plurals make -sure your resource file contains the related keys: +FormatJS uses the standard [ICU MessageFormat][], which includes +useful features such as plural translations. +`$t` does not escape any variables, so if your translated string is +eventually going to be used as HTML, use `$t_html` instead. + +```js +$("#foo").html( + $t_html({defaultMessage: "HTML with a {variable}"}, {variable: "Variable value"}) +); ``` -{ - "en": { - "translation": { - "key": "item", - "key_plural": "items", - "keyWithCount": "__count__ item", - "keyWithCount_plural": "__count__ items" - } - } -} -``` - -With this resource you can show plurals like this: - -``` -i18n.t('key', {count: 0}); // output: 'items' -i18n.t('key', {count: 1}); // output: 'item' -i18n.t('key', {count: 5}); // output: 'items' -i18n.t('key', {count: 100}); // output: 'items' -i18n.t('keyWithCount', {count: 0}); // output: '0 items' -i18n.t('keyWithCount', {count: 1}); // output: '1 item' -i18n.t('keyWithCount', {count: 5}); // output: '5 items' -i18n.t('keyWithCount', {count: 100}); // output: '100 items' -``` - -For further reading on plurals, read the [official] documentation. - -By default, all text is escaped by i18next. To unescape a text you can use -double-underscores followed by a dash and space `__- ` like this: - -``` -i18n.t('English text with a __- variable__', {'variable': 'Variable value'}); -``` - -For more information, you can read the official [unescape] documentation. ### Handlebars templates -For translations in Handlebars templates we also use `i18n.t`, through two +For translations in Handlebars templates we also use FormatJS, through two Handlebars [helpers][] that Zulip registers. The syntax for simple strings is: ``` {{t 'English Text' }} ``` -If you are passing a translated string to a Handlebars Partial, you can use: +If you are passing a translated string to a Handlebars partial, you can use: ``` {{> template_name @@ -291,25 +264,23 @@ The syntax for block strings or strings containing variables is: var context = {'variable': 'variable value'}; {{#tr context}} - Block of English text with a __variable__. + Block of English text with a {variable}. {{/tr}} ``` -Just like in JavaScript code, variables are enclosed in double -underscores `__`. +Just like in JavaScript code, variables are enclosed in *single* +braces (rather than the usual Handlebars double braces). Unlike in +JavaScript code, variables are automatically escaped by our Handlebars +helper. Handlebars expressions like `{{variable}}` or blocks like `{{#if}}...{{/if}}` aren't permitted inside a `{{#tr}}...{{/tr}}` translated block, because they don't work properly with translation. The Handlebars expression would be evaluated before the string is -processed by `i18n.t`, so that the string to be translated wouldn't be +processed by FormatJS, so that the string to be translated wouldn't be constant. We have a linter to enforce that translated blocks don't contain handlebars. -The rules for plurals are same as for JavaScript files. You just have -to declare the appropriate keys in the resource file and then include -the `count` in the context. - ## Transifex config The config file that maps the resources from Zulip to Transifex is @@ -339,11 +310,9 @@ organizations from the command line. [Jinja2]: http://jinja.pocoo.org/ [Handlebars]: https://handlebarsjs.com/ [trans]: http://jinja.pocoo.org/docs/dev/templates/#i18n -[i18next]: https://www.i18next.com -[official]: https://www.i18next.com/plurals.html -[unescape]: https://www.i18next.com/interpolation.html#unescape +[FormatJS]: https://formatjs.io/ +[ICU MessageFormat]: https://formatjs.io/docs/intl-messageformat [helpers]: https://handlebarsjs.com/guide/block-helpers.html -[resource]: https://www.i18next.com/add-or-load-translations.html [Transifex]: https://transifex.com [transifexrc]: https://docs.transifex.com/client/client-configuration#transifexrc [html-templates]: ../subsystems/html-css.html#html-templates diff --git a/docs/tutorials/writing-views.md b/docs/tutorials/writing-views.md index 2c99efbcba..604e7fd541 100644 --- a/docs/tutorials/writing-views.md +++ b/docs/tutorials/writing-views.md @@ -291,7 +291,7 @@ channel.patch({ data: data, success: function (response_data) { if (response_data.name !== undefined) { - ui_report.success(i18n.t("Name changed!"), name_status); + ui_report.success($t({defaultMessage: "Name changed!"}), name_status); } ... ``` diff --git a/frontend_tests/node_tests/i18n.js b/frontend_tests/node_tests/i18n.js index 5462655c1f..4ef65d7292 100644 --- a/frontend_tests/node_tests/i18n.js +++ b/frontend_tests/node_tests/i18n.js @@ -20,9 +20,8 @@ page_params.translation_data = { // All of our other tests stub out i18n activity; // here we do a quick sanity check on the engine itself. -// We use `i18n.js` to initialize `i18next` and -// to set `i18n` to `i18next` on the global namespace -// for `templates.js`. +// `i18n.js` initializes FormatJS and is imported by +// `templates.js`. unmock_module("../../static/js/i18n"); const {$t, $t_html} = zrequire("i18n"); zrequire("templates");