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

67 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 

7import re 

8from typing import TYPE_CHECKING 

9 

10from django.utils.html import escape, format_html 

11from django.utils.translation import gettext, gettext_lazy 

12 

13from weblate.checks.base import TargetCheck 

14from weblate.utils.csv import ( 

15 PROHIBITED_INITIAL_CHARS, 

16 PROHIBITED_INITIAL_CHARS_FOR_DISPLAY, 

17) 

18from weblate.utils.html import format_html_join_comma 

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.trans.models import Unit 

22 

23 

24class GlossaryCheck(TargetCheck): 

25 default_disabled = True 

26 check_id = "check_glossary" 

27 name = gettext_lazy("Does not follow glossary") 

28 description = gettext_lazy( 

29 "The translation does not follow terms defined in a glossary." 

30 ) 

31 

32 def check_single(self, source: str, target: str, unit: Unit): 

33 from weblate.glossary.models import get_glossary_terms 

34 

35 forbidden = set() 

36 mismatched = set() 

37 matched = set() 

38 boundary = r"\b" if unit.translation.language.uses_whitespace() else "" 

39 for term in get_glossary_terms(unit, include_variants=False): 

40 term_source = term.source 

41 flags = term.all_flags 

42 expected = term_source if "read-only" in flags else term.target 

43 if "forbidden" in flags: 

44 if re.search( 

45 rf"{boundary}{re.escape(expected)}{boundary}", target, re.IGNORECASE 

46 ): 

47 forbidden.add(term_source) 

48 else: 

49 if term_source in matched: 

50 continue 

51 if re.search( 

52 rf"{boundary}{re.escape(expected)}{boundary}", target, re.IGNORECASE 

53 ): 

54 mismatched.discard(term_source) 

55 matched.add(term_source) 

56 else: 

57 mismatched.add(term_source) 

58 

59 return forbidden | mismatched 

60 

61 def get_description(self, check_obj): 

62 unit = check_obj.unit 

63 sources = unit.get_source_plurals() 

64 targets = unit.get_target_plurals() 

65 source = sources[0] 

66 results = set() 

67 # Check singular 

68 result = self.check_single(source, targets[0], unit) 

69 if result: 

70 results.update(result) 

71 # Do we have more to check? 

72 if len(sources) > 1: 

73 source = sources[1] 

74 # Check plurals against plural from source 

75 for target in targets[1:]: 

76 result = self.check_single(source, target, unit) 

77 if result: 

78 results.update(result) 

79 

80 if not results: 

81 return super().get_description(check_obj) 

82 

83 return format_html( 

84 escape( 

85 gettext("Following terms are not translated according to glossary: {}") 

86 ), 

87 format_html_join_comma("{}", ((term,) for term in sorted(results))), 

88 ) 

89 

90 

91class ProhibitedInitialCharacterCheck(TargetCheck): 

92 check_id = "prohibited_initial_character" 

93 name = gettext_lazy("Prohibited initial character") 

94 description = gettext_lazy("The string starts with a prohibited character in CSV.") 

95 # Process readonly (source) strings 

96 ignore_readonly = False 

97 glossary = True 

98 

99 def should_skip(self, unit: Unit) -> bool: 

100 if not unit.translation.component.is_glossary: 100 ↛ 101line 100 didn't jump to line 101 because the condition on line 100 was never true

101 return True 

102 return super().should_skip(unit) 

103 

104 def check_single(self, source: str, target: str, unit: Unit) -> bool: 

105 """Check if the source string starts with a prohibited character.""" 

106 return (target and target[0] in PROHIBITED_INITIAL_CHARS) or ( 

107 source and source[0] in PROHIBITED_INITIAL_CHARS 

108 ) 

109 

110 def get_description(self, check_obj) -> str: 

111 """Return description of the check.""" 

112 return format_html( 

113 escape( 

114 gettext( 

115 "The string starts with one or more of the following forbidden characters: {}" 

116 ) 

117 ), 

118 format_html_join_comma( 

119 "<code>{}</code>", PROHIBITED_INITIAL_CHARS_FOR_DISPLAY 

120 ), 

121 )