Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: General fixing and clean-up on translate() #1364

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/viur/core/bones/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def __init__(
] = None,
values: dict | list | tuple | t.Callable | enum.EnumMeta = (),
translation_key_prefix: str | t.Callable[[t.Self], str] = "",
add_missing_translations: bool = False,
**kwargs
):
"""
Expand All @@ -61,6 +62,7 @@ def __init__(
"""
super().__init__(defaultValue=defaultValue, **kwargs)
self.translation_key_prefix = translation_key_prefix
self.add_missing_translations = add_missing_translations

# handle list/tuple as dicts
if isinstance(values, (list, tuple)):
Expand Down Expand Up @@ -98,8 +100,10 @@ def __getattribute__(self, item):

values = {
key: label if isinstance(label, translate) else translate(
f"{prefix}{label}", str(label),
f"value {key} for {self.name}<{type(self).__name__}> in {self.skel_cls.__name__} in {self.skel_cls}"
f"{prefix}{key}", str(label),
f"value {key} for {self.name}<{type(self).__name__}> "
+ f"in {self.skel_cls.__name__} in {self.skel_cls}",
add_missing=self.add_missing_translations,
)
for key, label in values.items()
}
Expand Down
8 changes: 6 additions & 2 deletions src/viur/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,13 +606,17 @@ def available_dialects(self) -> list[str]:
res |= self.language_alias_map
return list(res.keys())

add_missing_translations: bool = False
"""Add missing translation into datastore.
add_missing_translations: bool | str | t.Iterable[str] = False
"""Add missing translation into datastore, optionally with given fnmatch-patterns.

If a key is not found in the translation table when a translation is
rendered, a database entry is created with the key and hint and
default value (if set) so that the translations
can be entered in the administration.

Instead of setting add_missing_translations to a boolean, it can also be set to
a pattern or iterable of fnmatch-patterns; Only translation keys matching these
patterns will be automatically added.
"""


Expand Down
62 changes: 46 additions & 16 deletions src/viur/core/i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
on your own. Just use the TranslateSkel).
""" # FIXME: grammar, rst syntax
import datetime
import fnmatch
import logging
import sys
import traceback
Expand Down Expand Up @@ -161,6 +162,8 @@ class translate:
"public",
"filename",
"lineno",
"add_missing",
"default_variables",
)

def __init__(
Expand All @@ -170,6 +173,8 @@ def __init__(
hint: str = None,
force_lang: str = None,
public: bool = False,
add_missing: bool = False,
default_variables: dict[str, t.Any] | None = None,
caller_is_jinja: bool = False,
):
"""
Expand All @@ -183,6 +188,7 @@ def __init__(
target language.
:param force_lang: Use this language instead the one of the request.
:param public: Flag for public translations, which can be obtained via /json/_translate/get_public.
:param default_variables: Default values for variable substitution.
:param caller_is_jinja: Is the call caused by our jinja method?
"""
super().__init__()
Expand All @@ -200,6 +206,8 @@ def __init__(
self.translationCache = None
self.force_lang = force_lang
self.public = public
self.add_missing = add_missing
self.default_variables = default_variables or {}
self.filename, self.lineno = None, None

if self.key not in systemTranslations and conf.i18n.add_missing_translations:
Expand Down Expand Up @@ -227,38 +235,56 @@ def __str__(self) -> str:
if self.translationCache is None:
global systemTranslations

if self.key not in systemTranslations and conf.i18n.add_missing_translations:
# This translation seems to be new and should be added

add_missing_translation(
key=self.key,
hint=self.hint,
default_text=self.defaultText,
filename=self.filename,
lineno=self.lineno,
public=self.public,
)
if self.key not in systemTranslations:
# either the translate()-object has add_missing set
if not (add_missing := self.add_missing):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this behaviour confusing.

  • So I set add_missing set to False, but if conf.i18n.add_missing_translations is True, it is added anyway.
    If add_missing is set to True, it doesn't matter what is in conf.i18n.add_missing_translations and it's added too.

I would see this as a common condition:
If self.add_missing and conf.i18n.add_missing_translations are both True, add, otherwise not.

# otherwise, use configuration flag
add_missing = conf.i18n.add_missing_translations

# match against fnmatch pattern, when given
if isinstance(add_missing, str):
add_missing = fnmatch.fnmatch(self.key, add_missing)
elif isinstance(add_missing, t.Iterable):
add_missing = any(fnmatch.fnmatch(self.key, pat) for pat in add_missing)
else:
add_missing = bool(add_missing)

if add_missing:
# This translation seems to be new and should be added
add_missing_translation(
key=self.key,
hint=self.hint,
default_text=self.defaultText,
filename=self.filename,
lineno=self.lineno,
public=self.public,
)

self.translationCache = self.merge_alias(systemTranslations.get(self.key, {}))

if (lang := self.force_lang) is None:
# The default case: use the request language
lang = current.language.get()

if value := self.translationCache.get(lang):
return value
return self.substitute_vars(value, **self.default_variables)

# Use the default text from datastore or from the caller arguments
return self.translationCache.get("_default_text_") or self.defaultText
return self.substitute_vars(
self.translationCache.get("_default_text_") or self.defaultText,
**self.default_variables
)

def translate(self, **kwargs) -> str:
"""Substitute the given kwargs in the translated or default text."""
return self.substitute_vars(str(self), **kwargs)
return self.substitute_vars(str(self), **(self.default_variables | kwargs))

def __call__(self, **kwargs):
def __call__(self, **kwargs) -> str:
"""Just an alias for translate"""
return self.translate(**kwargs)

@staticmethod
def substitute_vars(value: str, **kwargs):
def substitute_vars(value: str, **kwargs) -> str:
"""Substitute vars in a translation

Variables has to start with two braces (`{{`), followed by the variable
Expand Down Expand Up @@ -432,6 +458,10 @@ def add_missing_translation(
public: bool = False,
) -> None:
"""Add missing translations to datastore"""

logging.error(f"{key=} {hint=} {default_text=} {filename=} {lineno=} {variables=} {public=}")
raise RuntimeError("ICH WILL DAS NICHT")

try:
from viur.core.modules.translation import TranslationSkel, Creator
except ImportError as exc:
Expand Down
2 changes: 1 addition & 1 deletion src/viur/core/modules/moduleconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class ModuleConfScriptSkel(skeleton.RelSkel):
access = SelectBone(
descr="Required access rights",
values=lambda: {
right: i18n.translate(f"viur.modules.user.accessright.{right}", defaultText=right)
right: i18n.translate(f"viur.core.modules.user.accessright.{right}", defaultText=right)
for right in sorted(conf.user.access_rights)
},
multiple=True,
Expand Down
2 changes: 1 addition & 1 deletion src/viur/core/modules/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class ScriptLeafSkel(BaseScriptAbstractSkel):
access = SelectBone(
descr="Required access rights to run this Script",
values=lambda: {
right: translate(f"viur.modules.user.accessright.{right}", defaultText=right)
right: translate(f"viur.core.modules.user.accessright.{right}", defaultText=right)
for right in sorted(conf.user.access_rights)
},
multiple=True,
Expand Down
Loading
Loading