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

1import enum 

2import logging 

3from datetime import timedelta as td 

4 

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 

10 

11 

12class Creator(enum.Enum): 

13 VIUR = "viur" 

14 USER = "user" 

15 

16 

17class TranslationSkel(Skeleton): 

18 kindName = KINDNAME 

19 

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 ) 

29 

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 ) 

45 

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 ) 

61 

62 default_text = StringBone( 

63 descr=translate( 

64 "core.translationskel.default_text.descr", 

65 "Fallback value", 

66 ), 

67 ) 

68 

69 hint = StringBone( 

70 descr=translate( 

71 "core.translationskel.hint.descr", 

72 "Hint / Context (internal only)", 

73 ), 

74 ) 

75 

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 ) 

83 

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 ) 

91 

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 ) 

100 

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 ) 

110 

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) 

116 

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) 

123 

124 

125class Translation(List): 

126 kindName = KINDNAME 

127 

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 } 

146 

147 roles = { 

148 "admin": "*", 

149 } 

150 

151 def onAdded(self, *args, **kwargs): 

152 super().onAdded(*args, **kwargs) 

153 self._reload_translations() 

154 

155 def onEdited(self, *args, **kwargs): 

156 super().onEdited(*args, **kwargs) 

157 self._reload_translations() 

158 

159 def onDeleted(self, *args, **kwargs): 

160 super().onDeleted(*args, **kwargs) 

161 self._reload_translations() 

162 

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() 

175 

176 _last_reload = None