specify context for translation block

This commit is contained in:
David Lord 2021-12-26 11:29:39 -07:00
parent 896a62135b
commit b6f50df85b
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
4 changed files with 63 additions and 19 deletions

View File

@ -23,6 +23,9 @@ Unreleased
- ``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

View File

@ -1732,11 +1732,35 @@ to disable it for a block.
.. 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:

View File

@ -354,13 +354,19 @@ class InternationalizationExtension(Extension):
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":
@ -455,6 +461,7 @@ class InternationalizationExtension(Extension):
node = self._make_node(
singular,
plural,
context,
variables,
plural_expr,
bool(referenced),
@ -510,6 +517,7 @@ class InternationalizationExtension(Extension):
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,
@ -526,21 +534,18 @@ class InternationalizationExtension(Extension):
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

View File

@ -43,6 +43,9 @@ newstyle_i18n_templates = {
"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 %}",
@ -593,11 +596,20 @@ class TestNewstyleInternationalization:
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):