diff --git a/sphinx/environment.py b/sphinx/environment.py
index d203f29e1..2c28cf975 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -195,44 +195,6 @@ class Locale(Transform):
"""
default_priority = 0
- @classmethod
- def _collect_nodes(cls, nodelist, node_types, custom_cond_func=None):
- if custom_cond_func is None:
- custom_cond_func = lambda x: True
-
- collected = [node for node in nodelist
- if isinstance(node, node_types)
- and custom_cond_func(node)]
- return collected
-
- @classmethod
- def _collect_footnote_ref_nodes(cls, nodelist):
- return cls._collect_nodes(
- nodelist,
- nodes.footnote_reference,
- lambda n: n.get('auto') == 1)
-
- @classmethod
- def _collect_ref_nodes(cls, nodelist):
- return cls._collect_nodes(nodelist, nodes.reference)
-
- def _is_ref_inconsistency(self, node1, node2):
- """
- check equality of the number of reference in both the translated
- form and the untranslated form.
- """
- env = self.document.settings.env
- for f in [self._collect_footnote_ref_nodes,
- self._collect_ref_nodes,
- ]:
- if len(f(node1.children)) != len(f(node2.children)):
- env.warn_node('The number of reference are inconsistent '
- 'in both the translated form and the '
- 'untranslated form. skip translation.', node1)
- return True
-
- return False
-
def apply(self):
env = self.document.settings.env
settings, source = self.document.settings, self.document['source']
@@ -265,32 +227,40 @@ class Locale(Transform):
if not isinstance(patch, nodes.paragraph):
continue # skip for now
- if self._is_ref_inconsistency(node, patch):
- continue #skip translation.
+ # auto-numbered foot note reference should use original 'ids'.
+ is_autonumber_footnote_ref = lambda node: \
+ isinstance(node, nodes.footnote_reference) \
+ and node.get('auto') == 1
+ old_foot_refs = node.traverse(is_autonumber_footnote_ref)
+ new_foot_refs = patch.traverse(is_autonumber_footnote_ref)
+ if len(old_foot_refs) != len(new_foot_refs):
+ env.warn_node('The number of reference are inconsistent '
+ 'in both the translated form and the '
+ 'untranslated form. skip translation.', node)
+ for old, new in zip(old_foot_refs, new_foot_refs):
+ new['ids'] = old['ids']
+ self.document.autofootnote_refs.remove(old)
+ self.document.note_autofootnote_ref(new)
- footnote_refs = self._collect_footnote_ref_nodes(node.children)
- refs = self._collect_ref_nodes(node.children)
+ # reference should use original 'refname'.
+ # * reference target ".. _Python: ..." is not translatable.
+ # * section refname is not translatable.
+ # * inline reference "`Python <...>`_" has no 'refname'.
+ is_refnamed_ref = lambda node: \
+ isinstance(node, nodes.reference) \
+ and 'refname' in node
+ old_refs = node.traverse(is_refnamed_ref)
+ new_refs = patch.traverse(is_refnamed_ref)
+ if len(old_refs) != len(new_refs):
+ env.warn_node('The number of reference are inconsistent '
+ 'in both the translated form and the '
+ 'untranslated form. skip translation.', node)
+ for old, new in zip(old_refs, new_refs):
+ new['refname'] = old['refname']
+ self.document.note_refname(new)
- for i, child in enumerate(patch.children): # update leaves
- if isinstance(child, nodes.footnote_reference) \
- and child.get('auto') == 1:
- # use original 'footnote_reference' object.
- # this object is already registered in self.document.autofootnote_refs
- patch.children[i] = footnote_refs.pop(0)
-
- elif isinstance(child, nodes.reference):
- # reference should use original 'refname'.
- # * reference target ".. _Python: ..." is not translatable.
- # * section refname is not translatable.
- # * inline reference "`Python <...>`_" has no 'refname'.
- if refs and 'refname' in refs[0]:
- refname = child['refname'] = refs.pop(0)['refname']
- self.document.refnames.setdefault(
- refname, []).append(child)
- # if number of reference nodes had been changed, that
- # would often generate unknown link target warning.
-
- for child in patch.children: # update leaves
+ # update leaves
+ for child in patch.children:
child.parent = node
node.children = patch.children
diff --git a/tests/test_intl.py b/tests/test_intl.py
index 47f6f1f5e..6d084d13b 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -111,8 +111,8 @@ def test_i18n_warn_for_number_of_references_inconsistency(app):
result = (app.outdir / 'i18n' / 'refs_inconsistency.txt').text(encoding='utf-8')
expect = (u"\nI18N WITH REFS INCONSISTENCY"
u"\n****************************\n"
- u"\n* [100] for [1] footnote [ref2].\n"
- u"\n* for reference.\n"
+ u"\n* FOR FOOTNOTE [ref2].\n"
+ u"\n* reference FOR reference.\n"
u"\n[1] THIS IS A AUTO NUMBERED FOOTNOTE.\n"
u"\n[ref2] THIS IS A NAMED FOOTNOTE.\n"
u"\n[100] THIS IS A NUMBERED FOOTNOTE.\n")
@@ -123,6 +123,22 @@ def test_i18n_warn_for_number_of_references_inconsistency(app):
assert len(re.findall(expected_warning_expr, warnings)) == 2
+@with_app(buildername='html', cleanenv=True,
+ confoverrides={'language': 'xx', 'locale_dirs': ['.'],
+ 'gettext_compact': False})
+def test_i18n_link_to_undefined_reference(app):
+ app.builder.build(['i18n/refs_inconsistency'])
+ result = (app.outdir / 'i18n' / 'refs_inconsistency.html').text(encoding='utf-8')
+
+ expected_expr = """reference"""
+ assert len(re.findall(expected_expr, result)) == 1
+
+ # the 2nd 'reference_' is to be internal-link instead of external-link.
+ # TODO: Can we re-use the same name named-reference?
+ expected_expr = """reference"""
+ assert len(re.findall(expected_expr, result)) == 1
+
+
@with_app(buildername='html', cleanenv=True,
confoverrides={'language': 'xx', 'locale_dirs': ['.'],
'gettext_compact': False})