]> git.ipfire.org Git - thirdparty/jinja.git/commitdiff
clean up i18n documentation 1084/head
authorDavid Lord <davidism@gmail.com>
Sat, 19 Oct 2019 14:30:39 +0000 (07:30 -0700)
committerDavid Lord <davidism@gmail.com>
Sat, 19 Oct 2019 15:04:23 +0000 (08:04 -0700)
explain enabling newstyle
organize newstyle advantages
mention escaping percent signs for formatting
mention marking format parameters as safe
fix newstyle ref

docs/extensions.rst
docs/templates.rst
jinja2/ext.py

index b21b111332d3e5f6e93aa8ca2adfb9b09b75122d..15732841d2ed31dcda7338f38652ca4883405823 100644 (file)
@@ -27,14 +27,13 @@ i18n Extension
 
 **Import name:** ``jinja2.ext.i18n``
 
-The i18n extension can be used in combination with `gettext`_ or `babel`_.  If
-the i18n extension is enabled Jinja2 provides a ``trans`` statement that marks
-the wrapped string as translatable and calls ``gettext``.
+The i18n extension can be used in combination with `gettext`_ or
+`Babel`_.  When it's enabled, Jinja provides a ``trans`` statement that
+marks a block as translatable and calls ``gettext``.
 
-After enabling, dummy ``_`` function that forwards calls to ``gettext`` is added
-to the environment globals.  An internationalized application then has to
-provide a ``gettext`` function and optionally an ``ngettext`` function into the
-namespace, either globally or for each rendering.
+After enabling, an application has to provide ``gettext`` and
+``ngettext`` functions, either globally or when rendering. A ``_()``
+function is added as an alias to the ``gettext`` function.
 
 Environment Methods
 ~~~~~~~~~~~~~~~~~~~
@@ -44,36 +43,37 @@ additional methods:
 
 .. method:: jinja2.Environment.install_gettext_translations(translations, newstyle=False)
 
-    Installs a translation globally for that environment.  The translations
-    object provided must implement at least ``ugettext`` and ``ungettext``.
-    The ``gettext.NullTranslations`` and ``gettext.GNUTranslations`` classes
-    as well as `Babel`_\s ``Translations`` class are supported.
+    Installs a translation globally for the environment. The
+    ``translations`` object must implement ``gettext`` and ``ngettext``
+    (or ``ugettext`` and ``ungettext`` for Python 2).
+    :class:`gettext.NullTranslations`, :class:`gettext.GNUTranslations`,
+    and `Babel`_\s ``Translations`` are supported.
 
-    .. versionchanged:: 2.5 newstyle gettext added
+    .. versionchanged:: 2.5 Added new-style gettext support.
 
 .. method:: jinja2.Environment.install_null_translations(newstyle=False)
 
-    Install dummy gettext functions.  This is useful if you want to prepare
-    the application for internationalization but don't want to implement the
-    full internationalization system yet.
+    Install no-op gettext functions. This is useful if you want to
+    prepare the application for internationalization but don't want to
+    implement the full system yet.
 
-    .. versionchanged:: 2.5 newstyle gettext added
+    .. versionchanged:: 2.5 Added new-style gettext support.
 
 .. method:: jinja2.Environment.install_gettext_callables(gettext, ngettext, newstyle=False)
 
-    Installs the given ``gettext`` and ``ngettext`` callables into the
-    environment as globals.  They are supposed to behave exactly like the
-    standard library's :func:`gettext.ugettext` and
-    :func:`gettext.ungettext` functions.
+    Install the given ``gettext`` and ``ngettext`` callables into the
+    environment. They should behave exactly like
+    :func:`gettext.gettext` and :func:`gettext.ngettext` (or
+    ``ugettext`` and ``ungettext`` for Python 2).
 
     If ``newstyle`` is activated, the callables are wrapped to work like
     newstyle callables.  See :ref:`newstyle-gettext` for more information.
 
-    .. versionadded:: 2.5
+    .. versionadded:: 2.5 Added new-style gettext support.
 
 .. method:: jinja2.Environment.uninstall_gettext_translations()
 
-    Uninstall the translations again.
+    Uninstall the environment's globally installed translation.
 
 .. method:: jinja2.Environment.extract_translations(source)
 
@@ -82,88 +82,102 @@ additional methods:
     For every string found this function yields a ``(lineno, function,
     message)`` tuple, where:
 
-    * ``lineno`` is the number of the line on which the string was found,
-    * ``function`` is the name of the ``gettext`` function used (if the
-      string was extracted from embedded Python code), and
-    *  ``message`` is the string itself (a ``unicode`` object, or a tuple
-       of ``unicode`` objects for functions with multiple string arguments).
+    -   ``lineno`` is the number of the line on which the string was
+        found.
+    -   ``function`` is the name of the ``gettext`` function used (if
+        the string was extracted from embedded Python code).
+    -   ``message`` is the string itself (``unicode`` on Python 2), or a
+        tuple of strings for functions with multiple arguments.
 
-    If `Babel`_ is installed, :ref:`the babel integration <babel-integration>`
-    can be used to extract strings for babel.
+    If `Babel`_ is installed, see :ref:`babel-integration` to extract
+    the strings.
 
-For a web application that is available in multiple languages but gives all
-the users the same language (for example a multilingual forum software
-installed for a French community) may load the translations once and add the
-translation methods to the environment at environment generation time::
+For a web application that is available in multiple languages but gives
+all the users the same language (for example, multilingual forum
+software installed for a French community), the translation may be
+installed when the environment is created.
+
+.. code-block:: python
 
     translations = get_gettext_translations()
-    env = Environment(extensions=['jinja2.ext.i18n'])
+    env = Environment(extensions=["jinja2.ext.i18n"])
     env.install_gettext_translations(translations)
 
-The ``get_gettext_translations`` function would return the translator for the
-current configuration.  (For example by using ``gettext.find``)
+The ``get_gettext_translations`` function would return the translator
+for the current configuration, for example by using ``gettext.find``.
 
-The usage of the ``i18n`` extension for template designers is covered as part
-:ref:`of the template documentation <i18n-in-templates>`.
+The usage of the ``i18n`` extension for template designers is covered in
+:ref:`the template documentation <i18n-in-templates>`.
 
 .. _gettext: https://docs.python.org/3/library/gettext.html
 .. _Babel: http://babel.pocoo.org/
 
-.. _newstyle-gettext:
 
 Whitespace Trimming
 ~~~~~~~~~~~~~~~~~~~
 
 .. versionadded:: 2.10
 
-Linebreaks and surrounding whitespace can be automatically trimmed by enabling
-the ``ext.i18n.trimmed`` :ref:`policy <ext-i18n-trimmed>`.
+Within ``{% trans %}`` blocks, it can be useful to trim line breaks and
+whitespace so that the block of text looks like a simple string with
+single spaces in the translation file.
+
+Linebreaks and surrounding whitespace can be automatically trimmed by
+enabling the ``ext.i18n.trimmed`` :ref:`policy <ext-i18n-trimmed>`.
 
 
-Newstyle Gettext
-~~~~~~~~~~~~~~~~
+.. _newstyle-gettext:
+
+New Style Gettext
+~~~~~~~~~~~~~~~~~
 
 .. versionadded:: 2.5
 
-Starting with version 2.5 you can use newstyle gettext calls.  These are
-inspired by trac's internal gettext functions and are fully supported by
-the babel extraction tool.  They might not work as expected by other
-extraction tools in case you are not using Babel's.
+New style gettext calls are less to type, less error prone, and support
+autoescaping better.
 
-What's the big difference between standard and newstyle gettext calls?  In
-general they are less to type and less error prone.  Also if they are used
-in an autoescaping environment they better support automatic escaping.
-Here are some common differences between old and new calls:
+You can use "new style" gettext calls by setting
+``env.newstyle_gettext = True`` or passing ``newstyle=True`` to
+``env.install_translations``. They are fully supported by the Babel
+extraction tool, but might not work as expected with other extraction
+tools.
 
-standard gettext:
+With standard ``gettext`` calls, string formatting is a separate step
+done with the ``|format`` filter. This requires duplicating work for
+``ngettext`` calls.
 
-.. sourcecode:: html+jinja
+.. sourcecode:: jinja
 
-    {{ gettext('Hello World!') }}
-    {{ gettext('Hello %(name)s!')|format(name='World') }}
-    {{ ngettext('%(num)d apple', '%(num)d apples', apples|count)|format(
-        num=apples|count
-    )}}
+    {{ gettext("Hello, World!") }}
+    {{ gettext("Hello, %(name)s!")|format(name=name) }}
+    {{ ngettext(
+           "%(num)d apple", "%(num)d apples", apples|count
+       )|format(num=apples|count) }}
 
-newstyle gettext looks like this instead:
+New style ``gettext`` make formatting part of the call, and behind the
+scenes enforce more consistency.
 
-.. sourcecode:: html+jinja
+.. sourcecode:: jinja
+
+    {{ gettext("Hello, World!") }}
+    {{ gettext("Hello, %(name)s!", name=name) }}
+    {{ ngettext("%(num)d apple", "%(num)d apples", apples|count) }}
+
+The advantages of newstyle gettext are:
+
+-   There's no separate formatting step, you don't have to remember to
+    use the ``|format`` filter.
+-   Only named placeholders are allowed. This solves a common problem
+    translators face because positional placeholders can't switch
+    positions meaningfully. Named placeholders always carry semantic
+    information about what value goes where.
+-   String formatting is used even if no placeholders are used, which
+    makes all strings use a consistent format. Remember to escape any
+    raw percent signs as ``%%``, such as ``100%%``.
+-   The translated string is marked safe, formatting performs escaping
+    as needed. Mark a parameter as ``|safe`` if it has already been
+    escaped.
 
-    {{ gettext('Hello World!') }}
-    {{ gettext('Hello %(name)s!', name='World') }}
-    {{ ngettext('%(num)d apple', '%(num)d apples', apples|count) }}
-
-The advantages of newstyle gettext are that you have less to type and that
-named placeholders become mandatory.  The latter sounds like a
-disadvantage but solves a lot of troubles translators are often facing
-when they are unable to switch the positions of two placeholder.  With
-newstyle gettext, all format strings look the same.
-
-Furthermore with newstyle gettext, string formatting is also used if no
-placeholders are used which makes all strings behave exactly the same.
-Last but not least are newstyle gettext calls able to properly mark
-strings for autoescaping which solves lots of escaping related issues many
-templates are experiencing over time when using autoescaping.
 
 Expression Statement
 --------------------
index 409751a39a523a5db01dc26a5a9ba0ea8662051a..b2ce444020f0f4516e8f6be84d32632dbd388974 100644 (file)
@@ -1569,33 +1569,43 @@ extensions not covered by this documentation; in which case there should
 be a separate document explaining said :ref:`extensions
 <jinja-extensions>`.
 
+
 .. _i18n-in-templates:
 
 i18n
 ~~~~
 
-If the i18n extension is enabled, it's possible to mark parts in the template
-as translatable.  To mark a section as translatable, you can use `trans`::
+If the :ref:`i18n-extension` is enabled, it's possible to mark text in
+the template as translatable. To mark a section as translatable, use a
+``trans`` block:
+
+.. code-block:: jinja
+
+    {% trans %}Hello, {{ user }}!{% endtrans %}
+
+Inside the block, no statements are allowed, only text and simple
+variable tags.
+
+Variable tags can only be a name, not attribute access, filters, or
+other expressions. To use an expression, bind it to a name in the
+``trans`` tag for use in the block.
 
-    <p>{% trans %}Hello {{ user }}!{% endtrans %}</p>
+.. code-block:: jinja
 
-To translate a template expression --- say, using template filters, or by just
-accessing an attribute of an object --- you need to bind the expression to a
-name for use within the translation block::
+    {% trans user=user.username %}Hello, {{ user }}!{% endtrans %}
 
-    <p>{% trans user=user.username %}Hello {{ user }}!{% endtrans %}</p>
+To bind more than one expression, separate each with a comma (``,``).
 
-If you need to bind more than one expression inside a `trans` tag, separate
-the pieces with a comma (``,``)::
+.. code-block:: jinja
 
     {% trans book_title=book.title, author=author.name %}
     This is {{ book_title }} by {{ author }}
     {% endtrans %}
 
-Inside trans tags no statements are allowed, only variable tags are.
+To pluralize, specify both the singular and plural forms separated by
+the ``pluralize`` tag.
 
-To pluralize, specify both the singular and plural forms with the `pluralize`
-tag, which appears between `trans` and `endtrans`::
+.. code-block:: jinja
 
     {% trans count=list|length %}
     There is {{ count }} {{ name }} object.
@@ -1603,60 +1613,70 @@ tag, which appears between `trans` and `endtrans`::
     There are {{ count }} {{ name }} objects.
     {% endtrans %}
 
-By default, the first variable in a block is used to determine the correct
-singular or plural form.  If that doesn't work out, you can specify the name
-which should be used for pluralizing by adding it as parameter to `pluralize`::
+By default, the first variable in a block is used to determine whether
+to use singular or plural form. If that isn't correct, specify the
+variable used for pluralizing as a parameter to ``pluralize``.
+
+.. code-block:: jinja
 
     {% trans ..., user_count=users|length %}...
     {% pluralize user_count %}...{% endtrans %}
 
-When translating longer blocks of text, whitespace and linebreaks result in
-rather ugly and error-prone translation strings.  To avoid this, a trans block
-can be marked as trimmed which will replace all linebreaks and the whitespace
-surrounding them with a single space and remove leading/trailing whitespace::
+When translating blocks of text, whitespace and linebreaks result in
+hard to read and error-prone translation strings. To avoid this, a trans
+block can be marked as trimmed, which will replace all linebreaks and
+the whitespace surrounding them with a single space and remove leading
+and trailing whitespace.
+
+.. code-block:: jinja
 
     {% trans trimmed book_title=book.title %}
         This is {{ book_title }}.
         You should read it!
     {% endtrans %}
 
-If trimming is enabled globally, the `notrimmed` modifier can be used to
-disable it for a `trans` block.
+This results in ``This is %(book_title)s. You should read it!`` in the
+translation file.
+
+If trimming is enabled globally, the ``notrimmed`` modifier can be used
+to disable it for a block.
 
 .. versionadded:: 2.10
-   The `trimmed` and `notrimmed` modifiers have been added.
+   The ``trimmed`` and ``notrimmed`` modifiers have been added.
 
-It's also possible to translate strings in expressions.  For that purpose,
-three functions exist:
+It's possible to translate strings in expressions with these functions:
 
--   `gettext`: translate a single string
--   `ngettext`: translate a pluralizable string
--   `_`: alias for `gettext`
+-   ``gettext``: translate a single string
+-   ``ngettext``: translate a pluralizable string
+-   ``_``: alias for ``gettext``
 
-For example, you can easily print a translated string like this::
+You can print a translated string like this:
 
-    {{ _('Hello World!') }}
+.. code-block:: jinja
 
-To use placeholders, use the `format` filter::
+    {{ _("Hello, World!") }}
 
-    {{ _('Hello %(user)s!')|format(user=user.username) }}
+To use placeholders, use the ``format`` filter.
 
-For multiple placeholders, always use keyword arguments to `format`,
-as other languages may not use the words in the same order.
+.. code-block:: jinja
 
-.. versionchanged:: 2.5
+    {{ _("Hello, %(user)s!")|format(user=user.username) }}
 
-If newstyle gettext calls are activated (:ref:`newstyle-gettext`), using
-placeholders is a lot easier:
+Always use keyword arguments to ``format``, as other languages may not
+use the words in the same order.
 
-.. sourcecode:: html+jinja
+If :ref:`newstyle-gettext` calls are activated, using placeholders is
+easier. Formatting is part of the ``gettext`` call instead of using the
+``format`` filter.
+
+.. sourcecode:: jinja
 
     {{ gettext('Hello World!') }}
     {{ gettext('Hello %(name)s!', name='World') }}
     {{ ngettext('%(num)d apple', '%(num)d apples', apples|count) }}
 
-Note that the `ngettext` function's format string automatically receives
-the count as a `num` parameter in addition to the regular parameters.
+The ``ngettext`` function's format string automatically receives the
+count as a ``num`` parameter in addition to the given parameters.
 
 
 Expression Statement
index 8488e63f774a5c7673e105220dd6c53f25543893..54b0f7780d4c1d3e312f632367488312442c2ff8 100644 (file)
@@ -148,6 +148,9 @@ def _make_new_gettext(func):
         rv = __context.call(func, __string)
         if __context.eval_ctx.autoescape:
             rv = Markup(rv)
+        # Always treat as a format string, even if there are no
+        # variables. This makes translation strings more consistent
+        # and predictable. This requires escaping
         return rv % variables
     return gettext
 
@@ -159,13 +162,14 @@ def _make_new_ngettext(func):
         rv = __context.call(func, __singular, __plural, __num)
         if __context.eval_ctx.autoescape:
             rv = Markup(rv)
+        # Always treat as a format string, see gettext comment above.
         return rv % variables
     return ngettext
 
 
 class InternationalizationExtension(Extension):
     """This extension adds gettext support to Jinja2."""
-    tags = set(['trans'])
+    tags = {'trans'}
 
     # TODO: the i18n extension is currently reevaluating values in a few
     # situations.  Take this example: