Coverage for app/venv/lib/python3.14/site-packages/weblate/trans/autofixes/chars.py: 48%

64 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.translation import gettext_lazy 

11 

12from weblate.checks.chars import ( 

13 FRENCH_PUNCTUATION_FIXUP_RE_NBSP, 

14 FRENCH_PUNCTUATION_FIXUP_RE_NNBSP, 

15 EndEllipsisCheck, 

16 PunctuationSpacingCheck, 

17 ZeroWidthSpaceCheck, 

18) 

19from weblate.checks.same import RST_MATCH 

20from weblate.formats.helpers import CONTROLCHARS_TRANS 

21from weblate.trans.autofixes.base import AutoFix 

22 

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

24 from weblate.trans.models import Unit 

25 

26 

27class ReplaceTrailingDotsWithEllipsis(AutoFix): 

28 """Replace trailing dots with an ellipsis.""" 

29 

30 fix_id = "end-ellipsis" 

31 name = gettext_lazy("Trailing ellipsis") 

32 

33 @staticmethod 

34 def get_related_checks(): 

35 return [EndEllipsisCheck()] 

36 

37 def fix_single_target( 

38 self, target: str, source: str, unit: Unit 

39 ) -> tuple[str, bool]: 

40 if source and source[-1] == "…" and target.endswith("..."): 

41 return f"{target[:-3]}…", True 

42 return target, False 

43 

44 

45class RemoveZeroSpace(AutoFix): 

46 """Remove zero width space if there is none in the source.""" 

47 

48 fix_id = "zero-width-space" 

49 name = gettext_lazy("Zero-width space") 

50 

51 @staticmethod 

52 def get_related_checks(): 

53 return [ZeroWidthSpaceCheck()] 

54 

55 def fix_single_target( 

56 self, target: str, source: str, unit: Unit 

57 ) -> tuple[str, bool]: 

58 if unit.translation.language.base_code == "km": 

59 return target, False 

60 if "\u200b" not in source and "\u200b" in target: 

61 return target.replace("\u200b", ""), True 

62 return target, False 

63 

64 

65class RemoveControlChars(AutoFix): 

66 """Remove control characters from the string.""" 

67 

68 fix_id = "control-chars" 

69 name = gettext_lazy("Control characters") 

70 

71 def fix_single_target( 

72 self, target: str, source: str, unit: Unit 

73 ) -> tuple[str, bool]: 

74 result = target.translate(CONTROLCHARS_TRANS) 

75 return result, result != target 

76 

77 

78class DevanagariDanda(AutoFix): 

79 """Fixes Bangla sentence ender.""" 

80 

81 fix_id = "devanadari-danda" 

82 name = gettext_lazy("Devanagari danda") 

83 

84 def fix_single_target( 

85 self, target: str, source: str, unit: Unit 

86 ) -> tuple[str, bool]: 

87 if ( 

88 unit.translation.language.is_base({"hi", "bn", "or"}) 

89 and "_Latn" not in unit.translation.language.code 

90 and source.endswith(".") 

91 and target.endswith((".", "\u09f7", "|")) 

92 ): 

93 return f"{target[:-1]}\u0964", True 

94 return target, False 

95 

96 

97class PunctuationSpacing(AutoFix): 

98 """Ensures French and Breton use correct punctuation spacing.""" 

99 

100 fix_id = "punctuation-spacing" 

101 name = gettext_lazy("Punctuation spacing") 

102 

103 @staticmethod 

104 def get_related_checks(): 

105 return [PunctuationSpacingCheck()] 

106 

107 def fix_single_target( 

108 self, target: str, source: str, unit: Unit 

109 ) -> tuple[str, bool]: 

110 def spacing_replace(matchobj: re.Match) -> str: 

111 if "rst-text" in unit.all_flags: 

112 offset = matchobj.start(2) 

113 rst_position = RST_MATCH.search(target, offset) 

114 if rst_position is not None and rst_position.start(0) == offset: 

115 # Skip escaping inside rst tag 

116 return matchobj.group(0) 

117 return f"\u00a0{matchobj.group(2)}" 

118 

119 if ( 

120 unit.translation.language.is_base({"fr"}) 

121 and unit.translation.language.code != "fr_CA" 

122 and "ignore-punctuation-spacing" not in unit.all_flags 

123 ): 

124 # Fix existing 

125 new_target = re.sub( 

126 FRENCH_PUNCTUATION_FIXUP_RE_NBSP, spacing_replace, target 

127 ) 

128 new_target = re.sub( 

129 FRENCH_PUNCTUATION_FIXUP_RE_NNBSP, "\u202f\\2", new_target 

130 ) 

131 # Do not add missing as that is likely to trigger issues with other content 

132 # such as URLs or Markdown syntax. 

133 return new_target, new_target != target 

134 return target, False