- ``unicode_urlencode`` is renamed to ``url_quote``.
- Add support for native types in macros. :issue:`1510`
+- The ``{% trans %}`` tag can use ``pgettext`` and ``npgettext`` by
+ passing a context string as the first token in the tag, like
+ ``{% trans "title" %}``. :issue:`1430`
Version 3.0.3
.. versionadded:: 2.10
The ``trimmed`` and ``notrimmed`` modifiers have been added.
+If the translation depends on the context that the message appears in,
+the ``pgettext`` and ``npgettext`` functions take a ``context`` string
+as the first argument, which is used to select the appropriate
+translation. To specify a context with the ``{% trans %}`` tag, provide
+a string as the first token after ``trans``.
+
+.. code-block:: jinja
+
+ {% trans "fruit" %}apple{% endtrans %}
+ {% trans "fruit" trimmed count -%}
+ 1 apple
+ {%- pluralize -%}
+ {{ count }} apples
+ {%- endtrans %}
+
+.. versionadded:: 3.1
+ A context can be passed to the ``trans`` tag to use ``pgettext`` and
+ ``npgettext``.
+
It's possible to translate strings in expressions with these functions:
-- ``gettext``: translate a single string
-- ``ngettext``: translate a pluralizable string
-- ``_``: alias for ``gettext``
+- ``_(message)``: Alias for ``gettext``.
+- ``gettext(message)``: Translate a message.
+- ``ngettext(singluar, plural, n)``: Translate a singular or plural
+ message based on a count variable.
+- ``pgettext(context, message)``: Like ``gettext()``, but picks the
+ translation based on the context string.
+- ``npgettext(context, singular, plural, n)``: Like ``npgettext()``,
+ but picks the translation based on the context string.
You can print a translated string like this:
def parse(self, parser: "Parser") -> t.Union[nodes.Node, t.List[nodes.Node]]:
"""Parse a translatable tag."""
lineno = next(parser.stream).lineno
- num_called_num = False
+
+ context = None
+ context_token = parser.stream.next_if("string")
+
+ if context_token is not None:
+ context = context_token.value
# find all the variables referenced. Additionally a variable can be
# defined in the body of the trans block too, but this is checked at
# a later state.
plural_expr: t.Optional[nodes.Expr] = None
plural_expr_assignment: t.Optional[nodes.Assign] = None
+ num_called_num = False
variables: t.Dict[str, nodes.Expr] = {}
trimmed = None
while parser.stream.current.type != "block_end":
node = self._make_node(
singular,
plural,
+ context,
variables,
plural_expr,
bool(referenced),
self,
singular: str,
plural: t.Optional[str],
+ context: t.Optional[str],
variables: t.Dict[str, nodes.Expr],
plural_expr: t.Optional[nodes.Expr],
vars_referenced: bool,
if plural:
plural = plural.replace("%%", "%")
- # singular only:
- if plural_expr is None:
- gettext = nodes.Name("gettext", "load")
- node = nodes.Call(gettext, [nodes.Const(singular)], [], None, None)
+ func_name = "gettext"
+ func_args: t.List[nodes.Expr] = [nodes.Const(singular)]
- # singular and plural
- else:
- ngettext = nodes.Name("ngettext", "load")
- node = nodes.Call(
- ngettext,
- [nodes.Const(singular), nodes.Const(plural), plural_expr],
- [],
- None,
- None,
- )
+ if context is not None:
+ func_args.insert(0, nodes.Const(context))
+ func_name = f"p{func_name}"
+
+ if plural_expr is not None:
+ func_name = f"n{func_name}"
+ func_args.extend((nodes.Const(plural), plural_expr))
+
+ node = nodes.Call(nodes.Name(func_name, "load"), func_args, [], None, None)
# in case newstyle gettext is used, the method is powerful
# enough to handle the variable expansion and autoescape
"pgettext.html": '{{ pgettext("fruit", "Apple") }}',
"npgettext.html": '{{ npgettext("fruit", "%(num)s apple", "%(num)s apples",'
" apples) }}",
+ "pgettext_block": "{% trans 'fruit' num=apples %}Apple{% endtrans %}",
+ "npgettext_block": "{% trans 'fruit' num=apples %}{{ num }} apple"
+ "{% pluralize %}{{ num }} apples{% endtrans %}",
"transvars1.html": "{% trans %}User: {{ num }}{% endtrans %}",
"transvars2.html": "{% trans num=count %}User: {{ num }}{% endtrans %}",
"transvars3.html": "{% trans count=num %}User: {{ count }}{% endtrans %}",
tmpl = newstyle_i18n_env.get_template("pgettext.html")
assert tmpl.render(LANGUAGE="de") == "Apple"
- def test_context_newstyle_plural(self):
+ def test_context_plural(self):
tmpl = newstyle_i18n_env.get_template("npgettext.html")
assert tmpl.render(LANGUAGE="de", apples=1) == "1 Apple"
assert tmpl.render(LANGUAGE="de", apples=5) == "5 Apples"
+ def test_context_block(self):
+ tmpl = newstyle_i18n_env.get_template("pgettext_block")
+ assert tmpl.render(LANGUAGE="de") == "Apple"
+
+ def test_context_plural_block(self):
+ tmpl = newstyle_i18n_env.get_template("npgettext_block")
+ assert tmpl.render(LANGUAGE="de", apples=1) == "1 Apple"
+ assert tmpl.render(LANGUAGE="de", apples=5) == "5 Apples"
+
class TestAutoEscape:
def test_scoped_setting(self):