docs: Update documentation for FormatJS migration.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2021-04-10 00:38:17 -07:00 committed by Tim Abbott
parent d8a36d0702
commit 9cae3450ff
4 changed files with 32 additions and 66 deletions

View File

@ -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

View File

@ -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

View File

@ -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);
}
...
```

View File

@ -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");