diff --git a/docs/changes.rst b/docs/changes.rst index 7dfa2e554692..5fdf0e67c071 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -9,6 +9,7 @@ Weblate 5.15.2 * :ref:`addon-weblate.generate.generate` is now triggered upon installation. * Screenshots updated from the repository have proper history. +* :ref:`check-rst-syntax` now reports unintended list conversion. .. rubric:: Bug fixes diff --git a/docs/locales/ja/LC_MESSAGES/docs.po b/docs/locales/ja/LC_MESSAGES/docs.po index 534c4a543ea3..15648b854da8 100644 --- a/docs/locales/ja/LC_MESSAGES/docs.po +++ b/docs/locales/ja/LC_MESSAGES/docs.po @@ -48921,7 +48921,7 @@ msgstr "" #: ../../security/passwords.rst:33 msgid "Social or third-party authentication" -msgstr "- ソーシャル認証またはサードパーティ認証" +msgstr "ソーシャル認証またはサードパーティ認証" #: ../../security/passwords.rst:35 msgid "" diff --git a/weblate/checks/markup.py b/weblate/checks/markup.py index c64c367aa922..b163a32118b2 100644 --- a/weblate/checks/markup.py +++ b/weblate/checks/markup.py @@ -114,6 +114,8 @@ re.compile(r"""Interpreted text role "([^"]*)" not implemented\."""), ] +RST_LIST_START = ("- ", "* ", "+ ") + def strip_entities(text): """Strip all HTML entities (we don't care about them).""" @@ -577,7 +579,7 @@ def get_rst_publisher() -> Publisher: @lru_cache(maxsize=512) def validate_rst_snippet( snippet: str, source_tags: tuple[str] | None = None -) -> tuple[list[str], list[str]]: +) -> tuple[tuple[str, ...], tuple[str, ...]]: publisher = get_rst_publisher() document = utils.new_document("", publisher.settings) @@ -632,7 +634,7 @@ def error_collector(data: system_message) -> None: transform = transform_class(transformer.document, startnode=pending) transform.apply(**kwargs) transformer.applied.append((priority, transform_class, pending, kwargs)) - return errors, roles + return tuple(errors), tuple(roles) class RSTSyntaxCheck(RSTBaseCheck): @@ -644,7 +646,14 @@ def check_single( self, source: str, target: str, unit: Unit ) -> bool | MissingExtraDict: _errors, source_roles = validate_rst_snippet(source) - errors, _target_roles = validate_rst_snippet(target, tuple(source_roles)) + rst_errors, _target_roles = validate_rst_snippet(target, source_roles) + errors = list(rst_errors) + + # This is valid RST, but might mess up the document + if not source.startswith(RST_LIST_START) and target.startswith(RST_LIST_START): + errors.append( + gettext("The translation should not start with a list marker.") + ) if errors: return {"errors": errors} diff --git a/weblate/checks/tests/test_markup_checks.py b/weblate/checks/tests/test_markup_checks.py index d04731a752ef..c2a5e69598ac 100644 --- a/weblate/checks/tests/test_markup_checks.py +++ b/weblate/checks/tests/test_markup_checks.py @@ -904,3 +904,61 @@ def test_substitution(self) -> None: "rst-text", ), ) + + def test_list(self) -> None: + self.do_test( + False, + ( + "Text", + "Text", + "rst-text", + ), + ) + self.do_test( + True, + ( + "Text", + "* Text", + "rst-text", + ), + ) + self.do_test( + True, + ( + "Text", + "- Text", + "rst-text", + ), + ) + self.do_test( + True, + ( + "Text", + "+ Text", + "rst-text", + ), + ) + self.do_test( + False, + ( + "- Text", + "- Text", + "rst-text", + ), + ) + self.do_test( + False, + ( + "- Text", + "* Text", + "rst-text", + ), + ) + self.do_test( + False, + ( + "- Text", + "+ Text", + "rst-text", + ), + )