From: David Lord Date: Sun, 26 Dec 2021 18:29:39 +0000 (-0700) Subject: specify context for translation block X-Git-Tag: 3.1.0~19^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F1559%2Fhead;p=thirdparty%2Fjinja.git specify context for translation block --- diff --git a/CHANGES.rst b/CHANGES.rst index d641712d..323fc80d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -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 diff --git a/docs/templates.rst b/docs/templates.rst index 89958b86..9071ad67 100644 --- a/docs/templates.rst +++ b/docs/templates.rst @@ -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: diff --git a/src/jinja2/ext.py b/src/jinja2/ext.py index d21b83aa..d5550540 100644 --- a/src/jinja2/ext.py +++ b/src/jinja2/ext.py @@ -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 diff --git a/tests/test_ext.py b/tests/test_ext.py index b54e905f..2e842e0a 100644 --- a/tests/test_ext.py +++ b/tests/test_ext.py @@ -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):