Coverage for app/venv/lib/python3.14/site-packages/weblate/checks/render.py: 37%

69 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 07:15 +0000

1# Copyright © Michal Čihař <michal@weblate.org> 

2# 

3# SPDX-License-Identifier: GPL-3.0-or-later 

4 

5from __future__ import annotations 

6 

7from typing import TYPE_CHECKING 

8 

9from django.core.cache import cache 

10from django.core.exceptions import ObjectDoesNotExist 

11from django.http import Http404, HttpResponse 

12from django.urls import reverse 

13from django.utils.html import format_html, format_html_join 

14from django.utils.translation import gettext, gettext_lazy 

15 

16from weblate.checks.base import TargetCheckParametrized 

17from weblate.checks.parser import multi_value_flag 

18from weblate.fonts.utils import check_render_size 

19 

20if TYPE_CHECKING: 20 ↛ 21line 20 didn't jump to line 21 because the condition on line 20 was never true

21 from weblate.auth.models import AuthenticatedHttpRequest 

22 from weblate.trans.models import Unit 

23 

24IMAGE = '<a href="{0}" class="thumbnail img-check"><img class="img-responsive" src="{0}" /></a>' 

25 

26 

27class MaxSizeCheck(TargetCheckParametrized): 

28 """Check for maximum size of rendered text.""" 

29 

30 check_id = "max-size" 

31 name = gettext_lazy("Maximum size of translation") 

32 description = gettext_lazy( 

33 "Translation rendered text should not exceed given size." 

34 ) 

35 default_disabled = True 

36 last_font = None 

37 always_display = True 

38 

39 @property 

40 def param_type(self): 

41 return multi_value_flag(int, 1, 2) 

42 

43 def get_params(self, unit: Unit) -> tuple[str, int | None, int, int]: 

44 all_flags = unit.all_flags 

45 return ( 

46 all_flags.get_value_fallback("font-family", "sans"), 

47 all_flags.get_value_fallback("font-weight", None), 

48 all_flags.get_value_fallback("font-size", 10), 

49 all_flags.get_value_fallback("font-spacing", 0), 

50 ) 

51 

52 def load_font(self, project, language, name: str) -> str: 

53 try: 

54 group = project.fontgroup_set.get(name=name) 

55 except ObjectDoesNotExist: 

56 return "sans" 

57 try: 

58 override = group.fontoverride_set.get(language=language) 

59 except ObjectDoesNotExist: 

60 return f"{group.font.family} {group.font.style}" 

61 return f"{override.font.family} {override.font.style}" 

62 

63 def check_target_params( 

64 self, sources: list[str], targets: list[str], unit: Unit, value 

65 ): 

66 if len(value) == 2: 

67 width, lines = value 

68 else: 

69 width = value[0] 

70 lines = 1 

71 font_group, weight, size, spacing = self.get_params(unit) 

72 font = self.last_font = self.load_font( 

73 unit.translation.component.project, unit.translation.language, font_group 

74 ) 

75 replace = self.get_replacement_function(unit) 

76 return any( 

77 ( 

78 not check_render_size( 

79 text=replace(target), 

80 font=font, 

81 weight=weight, 

82 size=size, 

83 spacing=spacing, 

84 width=width, 

85 lines=lines, 

86 cache_key=self.get_cache_key(unit, i), 

87 ) 

88 for i, target in enumerate(targets) 

89 ) 

90 ) 

91 

92 def get_description(self, check_obj): 

93 url = reverse( 

94 "render-check", 

95 kwargs={"check_id": self.check_id, "unit_id": check_obj.unit_id}, 

96 ) 

97 images = format_html_join( 

98 "\n", 

99 IMAGE, 

100 ( 

101 (f"{url}?pos={i}",) 

102 for i in range(len(check_obj.unit.get_target_plurals())) 

103 ), 

104 ) 

105 if not check_obj.id: 

106 return format_html( 

107 "{}{}", 

108 gettext( 

109 "It fits into given boundaries. The rendering is shown for your convenience." 

110 ), 

111 images, 

112 ) 

113 return images 

114 

115 def render(self, request: AuthenticatedHttpRequest, unit: Unit): 

116 try: 

117 pos = int(request.GET.get("pos", "0")) 

118 except ValueError: 

119 pos = 0 

120 key = self.get_cache_key(unit, pos) 

121 result = cache.get(key) 

122 if result is None: 

123 self.check_target_unit( 

124 unit.get_source_plurals(), unit.get_target_plurals(), unit 

125 ) 

126 result = cache.get(key) 

127 if result is None: 

128 msg = "Invalid check" 

129 raise Http404(msg) 

130 response = HttpResponse(content_type="image/png") 

131 response.write(result) 

132 return response