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
« 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
7import re
8from typing import TYPE_CHECKING
10from django.utils.translation import gettext_lazy
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
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
27class ReplaceTrailingDotsWithEllipsis(AutoFix):
28 """Replace trailing dots with an ellipsis."""
30 fix_id = "end-ellipsis"
31 name = gettext_lazy("Trailing ellipsis")
33 @staticmethod
34 def get_related_checks():
35 return [EndEllipsisCheck()]
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
45class RemoveZeroSpace(AutoFix):
46 """Remove zero width space if there is none in the source."""
48 fix_id = "zero-width-space"
49 name = gettext_lazy("Zero-width space")
51 @staticmethod
52 def get_related_checks():
53 return [ZeroWidthSpaceCheck()]
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
65class RemoveControlChars(AutoFix):
66 """Remove control characters from the string."""
68 fix_id = "control-chars"
69 name = gettext_lazy("Control characters")
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
78class DevanagariDanda(AutoFix):
79 """Fixes Bangla sentence ender."""
81 fix_id = "devanadari-danda"
82 name = gettext_lazy("Devanagari danda")
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
97class PunctuationSpacing(AutoFix):
98 """Ensures French and Breton use correct punctuation spacing."""
100 fix_id = "punctuation-spacing"
101 name = gettext_lazy("Punctuation spacing")
103 @staticmethod
104 def get_related_checks():
105 return [PunctuationSpacingCheck()]
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)}"
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