From 71ec4eb3580c81927a2cbfc83f8340870614ac66 Mon Sep 17 00:00:00 2001 From: Rahul Ranjan Date: Fri, 13 Nov 2026 07:49:34 +0000 Subject: [PATCH 1/2] fix: don't let translated snippets clobber a page's sphinx metadata Sphinx's Locale transform parses each translated message with a new document whose source ends in ':'. When the snippet is a heading (Sphinx appends an '===' underline to translated titles), _render_finalise then overwrote env.metadata[docname]["myst_slugs"] with the snippet's own slugs, so local anchors like other.md#beta stopped resolving in translated builds (a suppressible myst.xref_missing warning per link, #844). The always-on wordcount metadata write had the same clobber and is guarded the same way. Skip both env.metadata writes when the document is a translation snippet; the snippet document's own attributes are unaffected. Fixes #844 Co-authored-by: rahul05ranjan <11793455+rahul05ranjan@users.noreply.github.com> --- myst_parser/mdit_to_docutils/base.py | 9 ++++-- .../sourcedirs/translated_slugs/conf.py | 3 ++ .../translated_slugs/fr/LC_MESSAGES/other.po | 6 ++++ .../sourcedirs/translated_slugs/index.md | 7 +++++ .../sourcedirs/translated_slugs/other.md | 9 ++++++ tests/test_sphinx/test_sphinx_builds.py | 28 +++++++++++++++++++ 6 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/test_sphinx/sourcedirs/translated_slugs/conf.py create mode 100644 tests/test_sphinx/sourcedirs/translated_slugs/fr/LC_MESSAGES/other.po create mode 100644 tests/test_sphinx/sourcedirs/translated_slugs/index.md create mode 100644 tests/test_sphinx/sourcedirs/translated_slugs/other.md diff --git a/myst_parser/mdit_to_docutils/base.py b/myst_parser/mdit_to_docutils/base.py index 7b1808af..192004fe 100644 --- a/myst_parser/mdit_to_docutils/base.py +++ b/myst_parser/mdit_to_docutils/base.py @@ -270,9 +270,14 @@ def _render_initialise(self) -> None: def _render_finalise(self) -> None: """Finalise the render of the document.""" + # when sphinx parses a translation snippet (``_publish_msgstr``), + # the document source ends with ":"; + # such a snippet must not overwrite the page's own metadata + is_translated = str(self.document["source"]).endswith(":") + # save for later reference resolution self.document.myst_slugs = self._heading_slugs - if self._heading_slugs and self.sphinx_env: + if self._heading_slugs and self.sphinx_env and not is_translated: self.sphinx_env.metadata[self.sphinx_env.docname]["myst_slugs"] = ( self._heading_slugs ) @@ -302,7 +307,7 @@ def _render_finalise(self) -> None: wordcount_metadata = self.md_env.get("wordcount", {}) if wordcount_metadata: # save the wordcount to the sphinx BuildEnvironment metadata - if self.sphinx_env is not None: + if self.sphinx_env is not None and not is_translated: meta = self.sphinx_env.metadata.setdefault(self.sphinx_env.docname, {}) meta["wordcount"] = wordcount_metadata diff --git a/tests/test_sphinx/sourcedirs/translated_slugs/conf.py b/tests/test_sphinx/sourcedirs/translated_slugs/conf.py new file mode 100644 index 00000000..099510ac --- /dev/null +++ b/tests/test_sphinx/sourcedirs/translated_slugs/conf.py @@ -0,0 +1,3 @@ +extensions = ["myst_parser"] +exclude_patterns = ["_build"] +myst_heading_anchors = 2 diff --git a/tests/test_sphinx/sourcedirs/translated_slugs/fr/LC_MESSAGES/other.po b/tests/test_sphinx/sourcedirs/translated_slugs/fr/LC_MESSAGES/other.po new file mode 100644 index 00000000..dbb721db --- /dev/null +++ b/tests/test_sphinx/sourcedirs/translated_slugs/fr/LC_MESSAGES/other.po @@ -0,0 +1,6 @@ +msgid "" +msgstr "" +"Content-Type: text/plain; charset=UTF-8\n" + +msgid "Gamma" +msgstr "Gama" diff --git a/tests/test_sphinx/sourcedirs/translated_slugs/index.md b/tests/test_sphinx/sourcedirs/translated_slugs/index.md new file mode 100644 index 00000000..8270de8d --- /dev/null +++ b/tests/test_sphinx/sourcedirs/translated_slugs/index.md @@ -0,0 +1,7 @@ +# Home + +```{toctree} +other +``` + +See [beta](other.md#beta). diff --git a/tests/test_sphinx/sourcedirs/translated_slugs/other.md b/tests/test_sphinx/sourcedirs/translated_slugs/other.md new file mode 100644 index 00000000..01d6d250 --- /dev/null +++ b/tests/test_sphinx/sourcedirs/translated_slugs/other.md @@ -0,0 +1,9 @@ +# Other + +## Alpha + +## Beta + +```{admonition} Gamma +Text. +``` diff --git a/tests/test_sphinx/test_sphinx_builds.py b/tests/test_sphinx/test_sphinx_builds.py index b579eed3..6569a463 100644 --- a/tests/test_sphinx/test_sphinx_builds.py +++ b/tests/test_sphinx/test_sphinx_builds.py @@ -647,6 +647,34 @@ def test_gettext_html( ) +@pytest.mark.sphinx( + buildername="html", + srcdir=os.path.join(SOURCE_DIR, "translated_slugs"), + freshenv=True, + confoverrides={"language": "fr", "gettext_compact": False, "locale_dirs": ["."]}, +) +def test_translated_local_id( + app, + status, + warning, + get_sphinx_app_output, +): + """A translated snippet must not clobber the page's heading-slug metadata. + + Sphinx's ``Locale`` transform parses each translated message with a new + document whose source ends in ``:``; when the snippet is a + heading (Sphinx appends an ``===`` underline to translated titles), the + snippet's own slugs must not replace ``env.metadata[docname]["myst_slugs"]``, + or local anchors in other documents stop resolving (#844). + """ + app.build() + assert "build succeeded" in status.getvalue() # Build succeeded + assert warning.getvalue().strip() == "" + # and the local id still resolves to the right anchor + output = get_sphinx_app_output(app, filename="index.html", buildername="html") + assert 'other.html#beta' in output + + @pytest.mark.sphinx( buildername="gettext", srcdir=os.path.join(SOURCE_DIR, "gettext"), From 4eba7dfeaf73f4d6aa50f6e5422473c561236d16 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 13 Nov 2026 07:50:29 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_sphinx/test_sphinx_builds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_sphinx/test_sphinx_builds.py b/tests/test_sphinx/test_sphinx_builds.py index 6569a463..9bb83b57 100644 --- a/tests/test_sphinx/test_sphinx_builds.py +++ b/tests/test_sphinx/test_sphinx_builds.py @@ -672,7 +672,7 @@ def test_translated_local_id( assert warning.getvalue().strip() == "" # and the local id still resolves to the right anchor output = get_sphinx_app_output(app, filename="index.html", buildername="html") - assert 'other.html#beta' in output + assert "other.html#beta" in output @pytest.mark.sphinx(