Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/utils/translation/__init__.py: 42%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

144 statements  

1""" 

2Internationalization support. 

3""" 

4 

5from contextlib import ContextDecorator 

6from decimal import ROUND_UP, Decimal 

7 

8from django.utils.autoreload import autoreload_started, file_changed 

9from django.utils.functional import lazy 

10from django.utils.regex_helper import _lazy_re_compile 

11 

12__all__ = [ 

13 "activate", 

14 "deactivate", 

15 "override", 

16 "deactivate_all", 

17 "get_language", 

18 "get_language_from_request", 

19 "get_language_info", 

20 "get_language_bidi", 

21 "check_for_language", 

22 "to_language", 

23 "to_locale", 

24 "templatize", 

25 "gettext", 

26 "gettext_lazy", 

27 "gettext_noop", 

28 "ngettext", 

29 "ngettext_lazy", 

30 "pgettext", 

31 "pgettext_lazy", 

32 "npgettext", 

33 "npgettext_lazy", 

34] 

35 

36 

37class TranslatorCommentWarning(SyntaxWarning): 

38 pass 

39 

40 

41# Here be dragons, so a short explanation of the logic won't hurt: 

42# We are trying to solve two problems: (1) access settings, in particular 

43# settings.USE_I18N, as late as possible, so that modules can be imported 

44# without having to first configure Django, and (2) if some other code creates 

45# a reference to one of these functions, don't break that reference when we 

46# replace the functions with their real counterparts (once we do access the 

47# settings). 

48 

49 

50class Trans: 

51 """ 

52 The purpose of this class is to store the actual translation function upon 

53 receiving the first call to that function. After this is done, changes to 

54 USE_I18N will have no effect to which function is served upon request. If 

55 your tests rely on changing USE_I18N, you can delete all the functions 

56 from _trans.__dict__. 

57 

58 Note that storing the function with setattr will have a noticeable 

59 performance effect, as access to the function goes the normal path, 

60 instead of using __getattr__. 

61 """ 

62 

63 def __getattr__(self, real_name): 

64 from django.conf import settings 

65 

66 if settings.USE_I18N: 

67 from django.utils.translation import trans_real as trans 

68 from django.utils.translation.reloader import ( 

69 translation_file_changed, 

70 watch_for_translation_changes, 

71 ) 

72 

73 autoreload_started.connect( 

74 watch_for_translation_changes, dispatch_uid="translation_file_changed" 

75 ) 

76 file_changed.connect( 

77 translation_file_changed, dispatch_uid="translation_file_changed" 

78 ) 

79 else: 

80 from django.utils.translation import trans_null as trans 

81 setattr(self, real_name, getattr(trans, real_name)) 

82 return getattr(trans, real_name) 

83 

84 

85_trans = Trans() 

86 

87# The Trans class is no more needed, so remove it from the namespace. 

88del Trans 

89 

90 

91def gettext_noop(message): 

92 return _trans.gettext_noop(message) 

93 

94 

95def gettext(message): 

96 return _trans.gettext(message) 

97 

98 

99def ngettext(singular, plural, number): 

100 return _trans.ngettext(singular, plural, number) 

101 

102 

103def pgettext(context, message): 

104 return _trans.pgettext(context, message) 

105 

106 

107def npgettext(context, singular, plural, number): 

108 return _trans.npgettext(context, singular, plural, number) 

109 

110 

111gettext_lazy = lazy(gettext, str) 

112pgettext_lazy = lazy(pgettext, str) 

113 

114 

115def lazy_number(func, resultclass, number=None, **kwargs): 

116 if isinstance(number, int): 

117 kwargs["number"] = number 

118 proxy = lazy(func, resultclass)(**kwargs) 

119 else: 

120 original_kwargs = kwargs.copy() 

121 

122 class NumberAwareString(resultclass): 

123 def __bool__(self): 

124 return bool(kwargs["singular"]) 

125 

126 def _get_number_value(self, values): 

127 try: 

128 return values[number] 

129 except KeyError: 

130 raise KeyError( 

131 "Your dictionary lacks key '%s'. Please provide " 

132 "it, because it is required to determine whether " 

133 "string is singular or plural." % number 

134 ) 

135 

136 def _translate(self, number_value): 

137 kwargs["number"] = number_value 

138 return func(**kwargs) 

139 

140 def format(self, *args, **kwargs): 

141 number_value = ( 

142 self._get_number_value(kwargs) if kwargs and number else args[0] 

143 ) 

144 return self._translate(number_value).format(*args, **kwargs) 

145 

146 def __mod__(self, rhs): 

147 if isinstance(rhs, dict) and number: 

148 number_value = self._get_number_value(rhs) 

149 else: 

150 number_value = rhs 

151 translated = self._translate(number_value) 

152 try: 

153 translated %= rhs 

154 except TypeError: 

155 # String doesn't contain a placeholder for the number. 

156 pass 

157 return translated 

158 

159 proxy = lazy(lambda **kwargs: NumberAwareString(), NumberAwareString)(**kwargs) 

160 proxy.__reduce__ = lambda: ( 

161 _lazy_number_unpickle, 

162 (func, resultclass, number, original_kwargs), 

163 ) 

164 return proxy 

165 

166 

167def _lazy_number_unpickle(func, resultclass, number, kwargs): 

168 return lazy_number(func, resultclass, number=number, **kwargs) 

169 

170 

171def ngettext_lazy(singular, plural, number=None): 

172 return lazy_number(ngettext, str, singular=singular, plural=plural, number=number) 

173 

174 

175def npgettext_lazy(context, singular, plural, number=None): 

176 return lazy_number( 

177 npgettext, str, context=context, singular=singular, plural=plural, number=number 

178 ) 

179 

180 

181def activate(language): 

182 return _trans.activate(language) 

183 

184 

185def deactivate(): 

186 return _trans.deactivate() 

187 

188 

189class override(ContextDecorator): 

190 def __init__(self, language, deactivate=False): 

191 self.language = language 

192 self.deactivate = deactivate 

193 

194 def __enter__(self): 

195 self.old_language = get_language() 

196 if self.language is not None: 

197 activate(self.language) 

198 else: 

199 deactivate_all() 

200 

201 def __exit__(self, exc_type, exc_value, traceback): 

202 if self.old_language is None: 

203 deactivate_all() 

204 elif self.deactivate: 

205 deactivate() 

206 else: 

207 activate(self.old_language) 

208 

209 

210def get_language(): 

211 return _trans.get_language() 

212 

213 

214def get_language_bidi(): 

215 return _trans.get_language_bidi() 

216 

217 

218def check_for_language(lang_code): 

219 return _trans.check_for_language(lang_code) 

220 

221 

222def to_language(locale): 

223 """Turn a locale name (en_US) into a language name (en-us).""" 

224 p = locale.find("_") 

225 if p >= 0: 

226 return locale[:p].lower() + "-" + locale[p + 1 :].lower() 

227 else: 

228 return locale.lower() 

229 

230 

231def to_locale(language): 

232 """Turn a language name (en-us) into a locale name (en_US).""" 

233 lang, _, country = language.lower().partition("-") 

234 if not country: 

235 return language[:3].lower() + language[3:] 

236 # A language with > 2 characters after the dash only has its first 

237 # character after the dash capitalized; e.g. sr-latn becomes sr_Latn. 

238 # A language with 2 characters after the dash has both characters 

239 # capitalized; e.g. en-us becomes en_US. 

240 country, _, tail = country.partition("-") 

241 country = country.title() if len(country) > 2 else country.upper() 

242 if tail: 

243 country += "-" + tail 

244 return lang + "_" + country 

245 

246 

247def get_language_from_request(request, check_path=False): 

248 return _trans.get_language_from_request(request, check_path) 

249 

250 

251def get_language_from_path(path): 

252 return _trans.get_language_from_path(path) 

253 

254 

255def get_supported_language_variant(lang_code, *, strict=False): 

256 return _trans.get_supported_language_variant(lang_code, strict) 

257 

258 

259def templatize(src, **kwargs): 

260 from .template import templatize 

261 

262 return templatize(src, **kwargs) 

263 

264 

265def deactivate_all(): 

266 return _trans.deactivate_all() 

267 

268 

269def get_language_info(lang_code): 

270 from django.conf.locale import LANG_INFO 

271 

272 try: 

273 lang_info = LANG_INFO[lang_code] 

274 if "fallback" in lang_info and "name" not in lang_info: 

275 info = get_language_info(lang_info["fallback"][0]) 

276 else: 

277 info = lang_info 

278 except KeyError: 

279 if "-" not in lang_code: 

280 raise KeyError("Unknown language code %s." % lang_code) 

281 generic_lang_code = lang_code.split("-")[0] 

282 try: 

283 info = LANG_INFO[generic_lang_code] 

284 except KeyError: 

285 raise KeyError( 

286 "Unknown language code %s and %s." % (lang_code, generic_lang_code) 

287 ) 

288 

289 if info: 

290 info["name_translated"] = gettext_lazy(info["name"]) 

291 return info 

292 

293 

294trim_whitespace_re = _lazy_re_compile(r"\s*\n\s*") 

295 

296 

297def trim_whitespace(s): 

298 return trim_whitespace_re.sub(" ", s.strip()) 

299 

300 

301def round_away_from_one(value): 

302 return int(Decimal(value - 1).quantize(Decimal("0"), rounding=ROUND_UP)) + 1