Allow reordered refs and translated display text in pending_xref (#14144)

Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
This commit is contained in:
W. H. WangandAdam Turner authored and GitHub committed 2025-12-04 07:24:14 +00:00
1 parent f666208d78
commit 88a4a97873
6 files changed
+99 -5

No files matched your search

+3
View File
@@ -4,6 +4,9 @@ Release 9.0.4 (in development)
Bugs fixed
----------
* #14143: Fix spurious build warnings when translators reorder references
in strings, or use translated display text in references.
Patch by Matt Wang.
Release 9.0.3 (released Dec 04, 2025)
=====================================
+29 -5
View File
@@ -2,6 +2,7 @@
from __future__ import annotations
from operator import attrgetter
from re import DOTALL, match
from textwrap import indent
from typing import TYPE_CHECKING, Any, TypeVar
@@ -28,7 +29,7 @@ from sphinx.util.nodes import (
)
if TYPE_CHECKING:
from collections.abc import Sequence
from collections.abc import Callable, Sequence
from docutils.frontend import Values
@@ -133,11 +134,30 @@ class _NodeUpdater:
old_refs: Sequence[nodes.Element],
new_refs: Sequence[nodes.Element],
warning_msg: str,
*,
key_func: Callable[[nodes.Element], Any] = attrgetter('rawsource'),
ignore_order: bool = False,
) -> None:
"""Warn about mismatches between references in original and translated content."""
old_ref_rawsources = [ref.rawsource for ref in old_refs]
new_ref_rawsources = [ref.rawsource for ref in new_refs]
if not self.noqa and old_ref_rawsources != new_ref_rawsources:
"""Warn about mismatches between references in original and translated content.
:param key_func: A function to extract the comparison key from each reference.
Defaults to extracting the ``rawsource`` attribute.
:param ignore_order: If True, ignore the order of references when comparing.
This allows translators to reorder references while still catching
missing or extra references.
"""
old_ref_keys = list(map(key_func, old_refs))
new_ref_keys = list(map(key_func, new_refs))
if ignore_order:
# The ref_keys lists may contain ``None``, so compare hashes.
# Recall objects which compare equal have the same hash value.
old_ref_keys.sort(key=hash)
new_ref_keys.sort(key=hash)
if not self.noqa and old_ref_keys != new_ref_keys:
old_ref_rawsources = [ref.rawsource for ref in old_refs]
new_ref_rawsources = [ref.rawsource for ref in new_refs]
logger.warning(
warning_msg.format(old_ref_rawsources, new_ref_rawsources),
location=self.node,
@@ -347,6 +367,10 @@ class _NodeUpdater:
'inconsistent term references in translated message.'
' original: {0}, translated: {1}'
),
# Compare by reftarget only, allowing translated display text.
# Ignore order since translators may legitimately reorder references.
key_func=lambda ref: ref.get('reftarget'),
ignore_order=True,
)
xref_reftarget_map: dict[tuple[str, str, str] | None, dict[str, Any]] = {}
+1
View File
@@ -33,6 +33,7 @@ CONTENTS
topic
markup
backslashes
refs_reordered
.. toctree::
:maxdepth: 2
+16
View File
@@ -0,0 +1,16 @@
:tocdepth: 2
i18n with reordered and translated references
==============================================
.. glossary::
term one
First glossary term
term two
Second glossary term
1. Multiple refs reordered: :term:`term one` and :term:`term two`.
2. Single ref with translated display text: :term:`term one`.
@@ -0,0 +1,31 @@
# Test for reordered and translated references.
# These should NOT trigger inconsistency warnings.
#
msgid ""
msgstr ""
"Project-Id-Version: sphinx 1.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-01 00:00+0000\n"
"PO-Revision-Date: 2024-01-01 00:00+0000\n"
"Last-Translator: Test\n"
"Language-Team: xx\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "i18n with reordered and translated references"
msgstr "I18N WITH REORDERED AND TRANSLATED REFERENCES"
msgid "First glossary term"
msgstr "FIRST GLOSSARY TERM"
msgid "Second glossary term"
msgstr "SECOND GLOSSARY TERM"
# Reordered references - should NOT warn because targets are the same
msgid "Multiple refs reordered: :term:`term one` and :term:`term two`."
msgstr "MULTIPLE REFS REORDERED: :term:`term two` AND :term:`term one`."
# Translated display text - should NOT warn because reftarget is the same
msgid "Single ref with translated display text: :term:`term one`."
msgstr "SINGLE REF WITH TRANSLATED DISPLAY TEXT: :term:`TRANSLATED TERM ONE <term one>`."
+19
View File
@@ -378,6 +378,25 @@ def test_text_glossary_term_inconsistencies(app):
)
@sphinx_intl
@pytest.mark.sphinx('text', testroot='intl')
@pytest.mark.test_params(shared_result='test_intl_basic')
def test_text_refs_reordered_no_warning(app):
app.build()
# --- refs_reordered: verify no inconsistency warnings
result = (app.outdir / 'refs_reordered.txt').read_text(encoding='utf8')
# Verify the translation was applied
assert 'MULTIPLE REFS REORDERED' in result
assert 'SINGLE REF WITH TRANSLATED DISPLAY TEXT' in result
warnings = getwarning(app.warning)
# Should NOT have any inconsistent_references warnings for refs_reordered.txt
unexpected_warning_expr = '.*/refs_reordered.txt.*inconsistent.*references'
assert not re.search(unexpected_warning_expr, warnings), (
f'Unexpected warning found: {warnings!r}'
)
@sphinx_intl
@pytest.mark.sphinx('gettext', testroot='intl')
@pytest.mark.test_params(shared_result='test_intl_gettext')