Coverage for /home/runner/work/viur-core/viur-core/viur/src/viur/core/modules/translation.py: 0%
52 statements
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-16 22:16 +0000
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-16 22:16 +0000
1import enum
2import logging
3from datetime import timedelta as td
5from viur.core import conf, db, utils
6from viur.core.bones import *
7from viur.core.i18n import KINDNAME, initializeTranslations, systemTranslations, translate
8from viur.core.prototypes.list import List
9from viur.core.skeleton import Skeleton, SkeletonInstance
12class Creator(enum.Enum):
13 VIUR = "viur"
14 USER = "user"
17class TranslationSkel(Skeleton):
18 kindName = KINDNAME
20 tr_key = StringBone(
21 descr=translate(
22 "core.translationskel.tr_key.descr",
23 "Translation key",
24 ),
25 searchable=True,
26 unique=UniqueValue(UniqueLockMethod.SameValue, False,
27 "This translation key exist already"),
28 )
30 translations = StringBone(
31 descr=translate(
32 "core.translationskel.translations.descr",
33 "Translations",
34 ),
35 searchable=True,
36 languages=conf.i18n.available_dialects,
37 params={
38 "tooltip": translate(
39 "core.translationskel.translations.tooltip",
40 "The languages {{main}} are required,\n {{accent}} can be filled out"
41 )(main=", ".join(conf.i18n.available_languages),
42 accent=", ".join(conf.i18n.language_alias_map.keys())),
43 }
44 )
46 translations_missing = SelectBone(
47 descr=translate(
48 "core.translationskel.translations_missing.descr",
49 "Translation missing for language",
50 ),
51 multiple=True,
52 readOnly=True,
53 values=conf.i18n.available_dialects,
54 compute=Compute(
55 fn=lambda skel: [lang
56 for lang in conf.i18n.available_dialects
57 if not skel["translations"].get(lang)],
58 interval=ComputeInterval(ComputeMethod.OnWrite),
59 ),
60 )
62 default_text = StringBone(
63 descr=translate(
64 "core.translationskel.default_text.descr",
65 "Fallback value",
66 ),
67 )
69 hint = StringBone(
70 descr=translate(
71 "core.translationskel.hint.descr",
72 "Hint / Context (internal only)",
73 ),
74 )
76 usage_filename = StringBone(
77 descr=translate(
78 "core.translationskel.usage_filename.descr",
79 "Used and added from this file",
80 ),
81 readOnly=True,
82 )
84 usage_lineno = NumericBone(
85 descr=translate(
86 "core.translationskel.usage_lineno.descr",
87 "Used and added from this lineno",
88 ),
89 readOnly=True,
90 )
92 usage_variables = StringBone(
93 descr=translate(
94 "core.translationskel.usage_variables.descr",
95 "Receives these substitution variables",
96 ),
97 readOnly=True,
98 multiple=True,
99 )
101 creator = SelectBone(
102 descr=translate(
103 "core.translationskel.creator.descr",
104 "Creator",
105 ),
106 readOnly=True,
107 values=Creator,
108 defaultValue=Creator.USER,
109 )
111 @classmethod
112 def toDB(cls, skelValues: SkeletonInstance, **kwargs) -> db.Key:
113 # Ensure we have only lowercase keys
114 skelValues["tr_key"] = skelValues["tr_key"].lower()
115 return super().toDB(skelValues, **kwargs)
117 @classmethod
118 def preProcessSerializedData(cls, skelValues: SkeletonInstance, entity: db.Entity) -> db.Entity:
119 # Backward-compatibility: re-add the key for viur-core < v3.6
120 # TODO: Remove in ViUR4
121 entity["key"] = skelValues["tr_key"]
122 return super().preProcessSerializedData(skelValues, entity)
125class Translation(List):
126 kindName = KINDNAME
128 def adminInfo(self):
129 return {
130 "name": translate("translations"),
131 "icon": "translate",
132 "display": "hidden" if len(conf.i18n.available_dialects) <= 1 else "default",
133 "views": [
134 {
135 "name": translate(
136 "core.translations.view.missing",
137 "Missing translations for {{lang}}",
138 )(lang=lang),
139 "filter": {
140 "translations_missing": lang,
141 },
142 }
143 for lang in conf.i18n.available_dialects
144 ],
145 }
147 roles = {
148 "admin": "*",
149 }
151 def onAdded(self, *args, **kwargs):
152 super().onAdded(*args, **kwargs)
153 self._reload_translations()
155 def onEdited(self, *args, **kwargs):
156 super().onEdited(*args, **kwargs)
157 self._reload_translations()
159 def onDeleted(self, *args, **kwargs):
160 super().onDeleted(*args, **kwargs)
161 self._reload_translations()
163 def _reload_translations(self):
164 if (
165 self._last_reload is not None
166 and self._last_reload - utils.utcNow() < td(minutes=10)
167 ):
168 # debounce: translations has been reload recently, skip this
169 return None
170 logging.info("Reload translations")
171 # TODO: this affects only the current instance
172 self._last_reload = utils.utcNow()
173 systemTranslations.clear()
174 initializeTranslations()
176 _last_reload = None