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
« 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
5from __future__ import annotations
7from typing import TYPE_CHECKING
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
16from weblate.checks.base import TargetCheckParametrized
17from weblate.checks.parser import multi_value_flag
18from weblate.fonts.utils import check_render_size
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
24IMAGE = '<a href="{0}" class="thumbnail img-check"><img class="img-responsive" src="{0}" /></a>'
27class MaxSizeCheck(TargetCheckParametrized):
28 """Check for maximum size of rendered text."""
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
39 @property
40 def param_type(self):
41 return multi_value_flag(int, 1, 2)
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 )
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}"
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 )
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
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