mirror of
https://gitee.com/openharmony/third_party_jinja2
synced 2024-11-23 15:19:46 +00:00
add pgettext and npgettext
This commit is contained in:
parent
beabf304b0
commit
3fba898098
@ -46,6 +46,8 @@ Unreleased
|
||||
available in a template before using it. Test functions can be
|
||||
decorated with ``@environmentfunction``, ``@evalcontextfunction``,
|
||||
or ``@contextfunction``. :issue:`842`, :pr:`1248`
|
||||
- Support ``pgettext`` and ``npgettext`` (message contexts) in i18n
|
||||
extension. :issue:`441`
|
||||
|
||||
|
||||
Version 2.11.3
|
||||
|
@ -34,9 +34,11 @@ 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, 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.
|
||||
After enabling, an application has to provide functions for ``gettext``,
|
||||
``ngettext``, and optionally ``pgettext`` and ``npgettext``, either
|
||||
globally or when rendering. A ``_()`` function is added as an alias to
|
||||
the ``gettext`` function.
|
||||
|
||||
|
||||
Environment Methods
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
@ -47,11 +49,16 @@ additional methods:
|
||||
.. method:: jinja2.Environment.install_gettext_translations(translations, newstyle=False)
|
||||
|
||||
Installs a translation globally for the environment. The
|
||||
``translations`` object must implement ``gettext`` and ``ngettext``.
|
||||
``translations`` object must implement ``gettext``, ``ngettext``,
|
||||
and optionally ``pgettext`` and ``npgettext``.
|
||||
:class:`gettext.NullTranslations`, :class:`gettext.GNUTranslations`,
|
||||
and `Babel`_\s ``Translations`` are supported.
|
||||
|
||||
.. versionchanged:: 2.5 Added new-style gettext support.
|
||||
.. versionchanged:: 3.0
|
||||
Added ``pgettext`` and ``npgettext``.
|
||||
|
||||
.. versionchanged:: 2.5
|
||||
Added new-style gettext support.
|
||||
|
||||
.. method:: jinja2.Environment.install_null_translations(newstyle=False)
|
||||
|
||||
@ -61,16 +68,21 @@ additional methods:
|
||||
|
||||
.. versionchanged:: 2.5 Added new-style gettext support.
|
||||
|
||||
.. method:: jinja2.Environment.install_gettext_callables(gettext, ngettext, newstyle=False)
|
||||
.. method:: jinja2.Environment.install_gettext_callables(gettext, ngettext, newstyle=False, pgettext=None, npgettext=None)
|
||||
|
||||
Install the given ``gettext`` and ``ngettext`` callables into the
|
||||
environment. They should behave exactly like
|
||||
:func:`gettext.gettext` and :func:`gettext.ngettext`.
|
||||
Install the given ``gettext``, ``ngettext``, ``pgettext``, and
|
||||
``npgettext`` callables into the environment. They should behave
|
||||
exactly like :func:`gettext.gettext`, :func:`gettext.ngettext`,
|
||||
:func:`gettext.pgettext` and :func:`gettext.npgettext`.
|
||||
|
||||
If ``newstyle`` is activated, the callables are wrapped to work like
|
||||
newstyle callables. See :ref:`newstyle-gettext` for more information.
|
||||
|
||||
.. versionadded:: 2.5 Added new-style gettext support.
|
||||
.. versionchanged:: 3.0
|
||||
Added ``pgettext`` and ``npgettext``.
|
||||
|
||||
.. versionadded:: 2.5
|
||||
Added new-style gettext support.
|
||||
|
||||
.. method:: jinja2.Environment.uninstall_gettext_translations()
|
||||
|
||||
@ -154,6 +166,10 @@ done with the ``|format`` filter. This requires duplicating work for
|
||||
{{ ngettext(
|
||||
"%(num)d apple", "%(num)d apples", apples|count
|
||||
)|format(num=apples|count) }}
|
||||
{{ pgettext("greeting", "Hello, World!") }}
|
||||
{{ npgettext(
|
||||
"fruit", "%(num)d apple", "%(num)d apples", apples|count
|
||||
)|format(num=apples|count) }}
|
||||
|
||||
New style ``gettext`` make formatting part of the call, and behind the
|
||||
scenes enforce more consistency.
|
||||
@ -163,6 +179,8 @@ scenes enforce more consistency.
|
||||
{{ gettext("Hello, World!") }}
|
||||
{{ gettext("Hello, %(name)s!", name=name) }}
|
||||
{{ ngettext("%(num)d apple", "%(num)d apples", apples|count) }}
|
||||
{{ pgettext("greeting", "Hello, World!") }}
|
||||
{{ npgettext("fruit", "%(num)d apple", "%(num)d apples", apples|count) }}
|
||||
|
||||
The advantages of newstyle gettext are:
|
||||
|
||||
|
@ -30,7 +30,7 @@ from .utils import import_string
|
||||
|
||||
# I18N functions available in Jinja templates. If the I18N library
|
||||
# provides ugettext, it will be assigned to gettext.
|
||||
GETTEXT_FUNCTIONS = ("_", "gettext", "ngettext")
|
||||
GETTEXT_FUNCTIONS = ("_", "gettext", "ngettext", "pgettext", "npgettext")
|
||||
_ws_re = re.compile(r"\s*\n\s*")
|
||||
|
||||
|
||||
@ -167,6 +167,37 @@ def _make_new_ngettext(func):
|
||||
return ngettext
|
||||
|
||||
|
||||
def _make_new_pgettext(func):
|
||||
@contextfunction
|
||||
def pgettext(__context, __string_ctx, __string, **variables):
|
||||
variables.setdefault("context", __string_ctx)
|
||||
rv = __context.call(func, __string_ctx, __string)
|
||||
|
||||
if __context.eval_ctx.autoescape:
|
||||
rv = Markup(rv)
|
||||
|
||||
# Always treat as a format string, see gettext comment above.
|
||||
return rv % variables
|
||||
|
||||
return pgettext
|
||||
|
||||
|
||||
def _make_new_npgettext(func):
|
||||
@contextfunction
|
||||
def npgettext(__context, __string_ctx, __singular, __plural, __num, **variables):
|
||||
variables.setdefault("context", __string_ctx)
|
||||
variables.setdefault("num", __num)
|
||||
rv = __context.call(func, __string_ctx, __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 npgettext
|
||||
|
||||
|
||||
class InternationalizationExtension(Extension):
|
||||
"""This extension adds gettext support to Jinja."""
|
||||
|
||||
@ -200,23 +231,43 @@ class InternationalizationExtension(Extension):
|
||||
ngettext = getattr(translations, "ungettext", None)
|
||||
if ngettext is None:
|
||||
ngettext = translations.ngettext
|
||||
self._install_callables(gettext, ngettext, newstyle)
|
||||
|
||||
pgettext = getattr(translations, "pgettext", None)
|
||||
npgettext = getattr(translations, "npgettext", None)
|
||||
self._install_callables(
|
||||
gettext, ngettext, newstyle=newstyle, pgettext=pgettext, npgettext=npgettext
|
||||
)
|
||||
|
||||
def _install_null(self, newstyle=None):
|
||||
self._install_callables(
|
||||
lambda x: x, lambda s, p, n: s if n == 1 else p, newstyle
|
||||
lambda s: s,
|
||||
lambda s, p, n: s if n == 1 else p,
|
||||
newstyle=newstyle,
|
||||
pgettext=lambda c, s: s,
|
||||
npgettext=lambda c, s, p, n: s if n == 1 else p,
|
||||
)
|
||||
|
||||
def _install_callables(self, gettext, ngettext, newstyle=None):
|
||||
def _install_callables(
|
||||
self, gettext, ngettext, newstyle=None, pgettext=None, npgettext=None
|
||||
):
|
||||
if newstyle is not None:
|
||||
self.environment.newstyle_gettext = newstyle
|
||||
if self.environment.newstyle_gettext:
|
||||
gettext = _make_new_gettext(gettext)
|
||||
ngettext = _make_new_ngettext(ngettext)
|
||||
self.environment.globals.update(gettext=gettext, ngettext=ngettext)
|
||||
|
||||
if pgettext is not None:
|
||||
pgettext = _make_new_pgettext(pgettext)
|
||||
|
||||
if npgettext is not None:
|
||||
npgettext = _make_new_npgettext(npgettext)
|
||||
|
||||
self.environment.globals.update(
|
||||
gettext=gettext, ngettext=ngettext, pgettext=pgettext, npgettext=npgettext
|
||||
)
|
||||
|
||||
def _uninstall(self, translations):
|
||||
for key in "gettext", "ngettext":
|
||||
for key in ("gettext", "ngettext", "pgettext", "npgettext"):
|
||||
self.environment.globals.pop(key, None)
|
||||
|
||||
def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
|
||||
|
@ -40,6 +40,9 @@ newstyle_i18n_templates = {
|
||||
"ngettext.html": '{{ ngettext("%(num)s apple", "%(num)s apples", apples) }}',
|
||||
"ngettext_long.html": "{% trans num=apples %}{{ num }} apple{% pluralize %}"
|
||||
"{{ num }} apples{% endtrans %}",
|
||||
"pgettext.html": '{{ pgettext("fruit", "Apple") }}',
|
||||
"npgettext.html": '{{ npgettext("fruit", "%(num)s apple", "%(num)s apples",'
|
||||
" apples) }}",
|
||||
"transvars1.html": "{% trans %}User: {{ num }}{% endtrans %}",
|
||||
"transvars2.html": "{% trans num=count %}User: {{ num }}{% endtrans %}",
|
||||
"transvars3.html": "{% trans count=num %}User: {{ count }}{% endtrans %}",
|
||||
@ -57,41 +60,88 @@ languages = {
|
||||
"%(user_count)s users online": "%(user_count)s Benutzer online",
|
||||
"User: %(num)s": "Benutzer: %(num)s",
|
||||
"User: %(count)s": "Benutzer: %(count)s",
|
||||
"%(num)s apple": "%(num)s Apfel",
|
||||
"%(num)s apples": "%(num)s Äpfel",
|
||||
"Apple": {None: "Apfel", "fruit": "Apple"},
|
||||
"%(num)s apple": {None: "%(num)s Apfel", "fruit": "%(num)s Apple"},
|
||||
"%(num)s apples": {None: "%(num)s Äpfel", "fruit": "%(num)s Apples"},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def _get_with_context(value, ctx=None):
|
||||
if isinstance(value, dict):
|
||||
return value.get(ctx, value)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
@contextfunction
|
||||
def gettext(context, string):
|
||||
language = context.get("LANGUAGE", "en")
|
||||
return languages.get(language, {}).get(string, string)
|
||||
value = languages.get(language, {}).get(string, string)
|
||||
return _get_with_context(value)
|
||||
|
||||
|
||||
@contextfunction
|
||||
def ngettext(context, s, p, n):
|
||||
language = context.get("LANGUAGE", "en")
|
||||
|
||||
if n != 1:
|
||||
return languages.get(language, {}).get(p, p)
|
||||
return languages.get(language, {}).get(s, s)
|
||||
value = languages.get(language, {}).get(p, p)
|
||||
return _get_with_context(value)
|
||||
|
||||
value = languages.get(language, {}).get(s, s)
|
||||
return _get_with_context(value)
|
||||
|
||||
|
||||
@contextfunction
|
||||
def pgettext(context, c, s):
|
||||
language = context.get("LANGUAGE", "en")
|
||||
value = languages.get(language, {}).get(s, s)
|
||||
return _get_with_context(value, c)
|
||||
|
||||
|
||||
@contextfunction
|
||||
def npgettext(context, c, s, p, n):
|
||||
language = context.get("LANGUAGE", "en")
|
||||
|
||||
if n != 1:
|
||||
value = languages.get(language, {}).get(p, p)
|
||||
return _get_with_context(value, c)
|
||||
|
||||
value = languages.get(language, {}).get(s, s)
|
||||
return _get_with_context(value, c)
|
||||
|
||||
|
||||
i18n_env = Environment(
|
||||
loader=DictLoader(i18n_templates), extensions=["jinja2.ext.i18n"]
|
||||
)
|
||||
i18n_env.globals.update({"_": gettext, "gettext": gettext, "ngettext": ngettext})
|
||||
i18n_env.globals.update(
|
||||
{
|
||||
"_": gettext,
|
||||
"gettext": gettext,
|
||||
"ngettext": ngettext,
|
||||
"pgettext": pgettext,
|
||||
"npgettext": npgettext,
|
||||
}
|
||||
)
|
||||
i18n_env_trimmed = Environment(extensions=["jinja2.ext.i18n"])
|
||||
|
||||
i18n_env_trimmed.policies["ext.i18n.trimmed"] = True
|
||||
i18n_env_trimmed.globals.update(
|
||||
{"_": gettext, "gettext": gettext, "ngettext": ngettext}
|
||||
{
|
||||
"_": gettext,
|
||||
"gettext": gettext,
|
||||
"ngettext": ngettext,
|
||||
"pgettext": pgettext,
|
||||
"npgettext": npgettext,
|
||||
}
|
||||
)
|
||||
|
||||
newstyle_i18n_env = Environment(
|
||||
loader=DictLoader(newstyle_i18n_templates), extensions=["jinja2.ext.i18n"]
|
||||
)
|
||||
newstyle_i18n_env.install_gettext_callables( # type: ignore
|
||||
gettext, ngettext, newstyle=True
|
||||
gettext, ngettext, newstyle=True, pgettext=pgettext, npgettext=npgettext
|
||||
)
|
||||
|
||||
|
||||
@ -401,6 +451,20 @@ class TestInternationalization:
|
||||
(6, "ngettext", ("%(users)s user", "%(users)s users", None), ["third"]),
|
||||
]
|
||||
|
||||
def test_extract_context(self):
|
||||
from jinja2.ext import babel_extract
|
||||
|
||||
source = BytesIO(
|
||||
b"""
|
||||
{{ pgettext("babel", "Hello World") }}
|
||||
{{ npgettext("babel", "%(users)s user", "%(users)s users", users) }}
|
||||
"""
|
||||
)
|
||||
assert list(babel_extract(source, ("pgettext", "npgettext", "_"), [], {})) == [
|
||||
(2, "pgettext", ("babel", "Hello World"), []),
|
||||
(3, "npgettext", ("babel", "%(users)s user", "%(users)s users", None), []),
|
||||
]
|
||||
|
||||
|
||||
class TestScope:
|
||||
def test_basic_scope_behavior(self):
|
||||
@ -525,6 +589,15 @@ class TestNewstyleInternationalization:
|
||||
t = newstyle_i18n_env.get_template("explicitvars.html")
|
||||
assert t.render() == "%(foo)s"
|
||||
|
||||
def test_context(self):
|
||||
tmpl = newstyle_i18n_env.get_template("pgettext.html")
|
||||
assert tmpl.render(LANGUAGE="de") == "Apple"
|
||||
|
||||
def test_context_newstyle_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"
|
||||
|
||||
|
||||
class TestAutoEscape:
|
||||
def test_scoped_setting(self):
|
||||
|
Loading…
Reference in New Issue
Block a user