|
2 | 2 |
|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
| 5 | +from operator import attrgetter |
5 | 6 | from re import DOTALL, match |
6 | 7 | from textwrap import indent |
7 | 8 | from typing import TYPE_CHECKING, Any, TypeVar |
|
28 | 29 | ) |
29 | 30 |
|
30 | 31 | if TYPE_CHECKING: |
31 | | - from collections.abc import Sequence |
| 32 | + from collections.abc import Callable, Sequence |
32 | 33 |
|
33 | 34 | from docutils.frontend import Values |
34 | 35 |
|
@@ -133,11 +134,30 @@ def compare_references( |
133 | 134 | old_refs: Sequence[nodes.Element], |
134 | 135 | new_refs: Sequence[nodes.Element], |
135 | 136 | warning_msg: str, |
| 137 | + *, |
| 138 | + key_func: Callable[[nodes.Element], Any] = attrgetter('rawsource'), |
| 139 | + ignore_order: bool = False, |
136 | 140 | ) -> None: |
137 | | - """Warn about mismatches between references in original and translated content.""" |
138 | | - old_ref_rawsources = [ref.rawsource for ref in old_refs] |
139 | | - new_ref_rawsources = [ref.rawsource for ref in new_refs] |
140 | | - if not self.noqa and old_ref_rawsources != new_ref_rawsources: |
| 141 | + """Warn about mismatches between references in original and translated content. |
| 142 | +
|
| 143 | + :param key_func: A function to extract the comparison key from each reference. |
| 144 | + Defaults to extracting the ``rawsource`` attribute. |
| 145 | + :param ignore_order: If True, ignore the order of references when comparing. |
| 146 | + This allows translators to reorder references while still catching |
| 147 | + missing or extra references. |
| 148 | + """ |
| 149 | + old_ref_keys = list(map(key_func, old_refs)) |
| 150 | + new_ref_keys = list(map(key_func, new_refs)) |
| 151 | + |
| 152 | + if ignore_order: |
| 153 | + # The ref_keys lists may contain ``None``, so compare hashes. |
| 154 | + # Recall objects which compare equal have the same hash value. |
| 155 | + old_ref_keys.sort(key=hash) |
| 156 | + new_ref_keys.sort(key=hash) |
| 157 | + |
| 158 | + if not self.noqa and old_ref_keys != new_ref_keys: |
| 159 | + old_ref_rawsources = [ref.rawsource for ref in old_refs] |
| 160 | + new_ref_rawsources = [ref.rawsource for ref in new_refs] |
141 | 161 | logger.warning( |
142 | 162 | warning_msg.format(old_ref_rawsources, new_ref_rawsources), |
143 | 163 | location=self.node, |
@@ -347,6 +367,10 @@ def update_pending_xrefs(self) -> None: |
347 | 367 | 'inconsistent term references in translated message.' |
348 | 368 | ' original: {0}, translated: {1}' |
349 | 369 | ), |
| 370 | + # Compare by reftarget only, allowing translated display text. |
| 371 | + # Ignore order since translators may legitimately reorder references. |
| 372 | + key_func=lambda ref: ref.get('reftarget'), |
| 373 | + ignore_order=True, |
350 | 374 | ) |
351 | 375 |
|
352 | 376 | xref_reftarget_map: dict[tuple[str, str, str] | None, dict[str, Any]] = {} |
|
0 commit comments