mirror of
https://github.com/zulip/zulip.git
synced 2025-10-22 20:42:14 +00:00
docs: Explain web app translations before server, and use those terms.
This seems good for prioritizing what is important first, and should make it easier to add the Flutter app in the future.
This commit is contained in:
@@ -73,92 +73,15 @@ A few general notes:
|
||||
extract the strings in a project to send to translators will not
|
||||
find your string.
|
||||
|
||||
- Zulip makes use of the [Jinja2][] templating system for the backend
|
||||
and [Handlebars][] for the frontend. Our [HTML templates][html-templates]
|
||||
- Zulip makes use of the [Jinja2][] templating system for the server
|
||||
and [Handlebars][] for the web app. Our [HTML templates][html-templates]
|
||||
documentation includes useful information on the syntax and
|
||||
behavior of these systems.
|
||||
|
||||
### Backend translations
|
||||
### Web application translations
|
||||
|
||||
#### Jinja2 templates
|
||||
|
||||
All user-facing text in the Zulip UI should be generated by an Jinja2 HTML
|
||||
template so that it can be translated.
|
||||
|
||||
To mark a string for translation in a Jinja2 template, you
|
||||
can use the `_()` function in the templates like this:
|
||||
|
||||
```jinja
|
||||
{{ _("English text") }}
|
||||
```
|
||||
|
||||
If a piece of text contains both a literal string component and variables, use a
|
||||
block translation. This puts in placeholders for variables, to allow translators
|
||||
to translate an entire sentence.
|
||||
|
||||
To tag a block for translation, Jinja2 uses the [trans][trans] tag, like this:
|
||||
|
||||
```jinja
|
||||
{% trans %}This string will have {{ value }} inside.{% endtrans %}
|
||||
```
|
||||
|
||||
Never break up a sentence like this, as it will make it impossible to translate
|
||||
correctly:
|
||||
|
||||
```jinja
|
||||
# Don't do this!
|
||||
{{ _("This string will have") }} {{ value }} {{ _("inside") }}
|
||||
```
|
||||
|
||||
#### Python
|
||||
|
||||
A string in Python can be marked for translation using the `_()` function,
|
||||
which can be imported as follows:
|
||||
|
||||
```python
|
||||
from django.utils.translation import gettext as _
|
||||
```
|
||||
|
||||
Zulip expects all the error messages to be translatable as well. To
|
||||
ensure this, the error message passed to `JsonableError`
|
||||
should always be a literal string enclosed by `_()`
|
||||
function, for example:
|
||||
|
||||
```python
|
||||
JsonableError(_('English text'))
|
||||
```
|
||||
|
||||
If you're declaring a user-facing string at top level or in a class, you need to
|
||||
use `gettext_lazy` instead, to ensure that the translation happens at
|
||||
request-processing time when Django knows what language to use, for example:
|
||||
|
||||
```python
|
||||
from zproject.backends import check_password_strength, email_belongs_to_ldap
|
||||
|
||||
AVATAR_CHANGES_DISABLED_ERROR = gettext_lazy("Avatar changes are disabled in this organization.")
|
||||
|
||||
def confirm_email_change(request: HttpRequest, confirmation_key: str) -> HttpResponse:
|
||||
...
|
||||
```
|
||||
|
||||
```python
|
||||
class Realm(models.Model):
|
||||
MAX_REALM_NAME_LENGTH = 40
|
||||
MAX_REALM_SUBDOMAIN_LENGTH = 40
|
||||
|
||||
...
|
||||
...
|
||||
|
||||
STREAM_EVENTS_NOTIFICATION_TOPIC = gettext_lazy("channel events")
|
||||
```
|
||||
|
||||
To ensure we always internationalize our JSON error messages, the
|
||||
Zulip linter (`tools/lint`) attempts to verify correct usage.
|
||||
|
||||
### Frontend translations
|
||||
|
||||
We use the [FormatJS][] library for frontend translations when dealing
|
||||
with [Handlebars][] templates or JavaScript.
|
||||
We use the [FormatJS][] library for translations in the Zulip web app,
|
||||
both in [Handlebars][] templates and JavaScript.
|
||||
|
||||
To mark a string translatable in JavaScript files, pass it to the
|
||||
`intl.formatMessage` function, which we alias to `$t` in `intl.js`:
|
||||
@@ -258,19 +181,102 @@ custom HTML tag like this:
|
||||
{{/tr}}
|
||||
```
|
||||
|
||||
### Server translations
|
||||
|
||||
Strings in the server primarily comprise two areas:
|
||||
|
||||
- Error strings and other values returned by the API.
|
||||
- Strings in portico pages, such as the login flow, that are not
|
||||
rendered using JavaScript or Handlebars.
|
||||
|
||||
#### Jinja2 templates
|
||||
|
||||
All user-facing text in the Zulip UI should be generated by an Jinja2 HTML
|
||||
template so that it can be translated.
|
||||
|
||||
To mark a string for translation in a Jinja2 template, you
|
||||
can use the `_()` function in the templates like this:
|
||||
|
||||
```jinja
|
||||
{{ _("English text") }}
|
||||
```
|
||||
|
||||
If a piece of text contains both a literal string component and variables, use a
|
||||
block translation. This puts in placeholders for variables, to allow translators
|
||||
to translate an entire sentence.
|
||||
|
||||
To tag a block for translation, Jinja2 uses the [trans][trans] tag, like this:
|
||||
|
||||
```jinja
|
||||
{% trans %}This string will have {{ value }} inside.{% endtrans %}
|
||||
```
|
||||
|
||||
Never break up a sentence like this, as it will make it impossible to translate
|
||||
correctly:
|
||||
|
||||
```jinja
|
||||
# Don't do this!
|
||||
{{ _("This string will have") }} {{ value }} {{ _("inside") }}
|
||||
```
|
||||
|
||||
#### Python
|
||||
|
||||
A string in Python can be marked for translation using the `_()` function,
|
||||
which can be imported as follows:
|
||||
|
||||
```python
|
||||
from django.utils.translation import gettext as _
|
||||
```
|
||||
|
||||
Zulip expects all the error messages to be translatable as well. To
|
||||
ensure this, the error message passed to `JsonableError`
|
||||
should always be a literal string enclosed by `_()`
|
||||
function, for example:
|
||||
|
||||
```python
|
||||
JsonableError(_('English text'))
|
||||
```
|
||||
|
||||
If you're declaring a user-facing string at top level or in a class, you need to
|
||||
use `gettext_lazy` instead, to ensure that the translation happens at
|
||||
request-processing time when Django knows what language to use, for example:
|
||||
|
||||
```python
|
||||
from zproject.backends import check_password_strength, email_belongs_to_ldap
|
||||
|
||||
AVATAR_CHANGES_DISABLED_ERROR = gettext_lazy("Avatar changes are disabled in this organization.")
|
||||
|
||||
def confirm_email_change(request: HttpRequest, confirmation_key: str) -> HttpResponse:
|
||||
...
|
||||
```
|
||||
|
||||
```python
|
||||
class Realm(models.Model):
|
||||
MAX_REALM_NAME_LENGTH = 40
|
||||
MAX_REALM_SUBDOMAIN_LENGTH = 40
|
||||
|
||||
...
|
||||
...
|
||||
|
||||
STREAM_EVENTS_NOTIFICATION_TOPIC = gettext_lazy("channel events")
|
||||
```
|
||||
|
||||
To ensure we always internationalize our JSON error messages, the
|
||||
Zulip linter (`tools/lint`) attempts to verify correct usage.
|
||||
|
||||
## Translation process
|
||||
|
||||
The end-to-end tooling process for translations in Zulip is as follows.
|
||||
|
||||
1. The strings are marked for translation (see sections for
|
||||
[backend](#backend-translations) and
|
||||
[frontend](#frontend-translations) translations for details on
|
||||
[server](#server-translations) and
|
||||
[web app](#web-application-translations) translations for details on
|
||||
this).
|
||||
|
||||
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.
|
||||
web app strings and `django.po` for the server strings.
|
||||
|
||||
The `makemessages` command is idempotent in that:
|
||||
|
||||
@@ -304,11 +310,10 @@ sense of how everything fits together.
|
||||
## Translation resource files
|
||||
|
||||
All the translation magic happens through resource files, which hold
|
||||
the translated text. Backend resource files are located at
|
||||
`locale/<lang_code>/LC_MESSAGES/django.po`, while frontend
|
||||
resource files are located at
|
||||
`locale/<lang_code>/translations.json` (and mobile at
|
||||
`mobile.json`).
|
||||
the translated text. Server resource files are located at
|
||||
`locale/<lang_code>/LC_MESSAGES/django.po`, while web app resource
|
||||
files are located at `locale/<lang_code>/translations.json` (and
|
||||
mobile at `mobile.json`).
|
||||
|
||||
These files are uploaded to [Transifex][], where they can be translated.
|
||||
|
||||
|
@@ -43,7 +43,7 @@ if __name__ == "__main__":
|
||||
|
||||
print(
|
||||
WARNING
|
||||
+ "See https://zulip.readthedocs.io/en/latest/translating/internationalization.html#frontend-translations "
|
||||
+ "See https://zulip.readthedocs.io/en/latest/translating/internationalization.html#web-application-translations "
|
||||
"on how you can insert variables in the frontend translatable "
|
||||
"strings." + ENDC
|
||||
)
|
||||
|
Reference in New Issue
Block a user