mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Fix multiple cross references (term, ref, doc) in the same line return the same link with i18n. refs #1090, #1193
This commit is contained in:
parent
472a3a83b2
commit
deb01d8e30
2
CHANGES
2
CHANGES
@ -32,6 +32,8 @@ Bugs fixed
|
|||||||
characters to "Project name" on quickstart.
|
characters to "Project name" on quickstart.
|
||||||
* #1190: Output TeX/texinfo/man filename has no basename (only extention)
|
* #1190: Output TeX/texinfo/man filename has no basename (only extention)
|
||||||
when using multibyte characters to "Project name" on quickstart.
|
when using multibyte characters to "Project name" on quickstart.
|
||||||
|
* #1090: Fix multiple cross references (term, ref, doc) in the same line
|
||||||
|
return the same link with i18n.
|
||||||
|
|
||||||
|
|
||||||
Release 1.2 (beta1 released Mar 31, 2013)
|
Release 1.2 (beta1 released Mar 31, 2013)
|
||||||
|
@ -205,6 +205,41 @@ class OptionXRefRole(XRefRole):
|
|||||||
return title, target
|
return title, target
|
||||||
|
|
||||||
|
|
||||||
|
def make_termnodes_from_paragraph_node(env, node):
|
||||||
|
gloss_entries = env.temp_data.setdefault('gloss_entries', set())
|
||||||
|
objects = env.domaindata['std']['objects']
|
||||||
|
|
||||||
|
termtext = node.astext()
|
||||||
|
new_id = 'term-' + nodes.make_id(termtext)
|
||||||
|
if new_id in gloss_entries:
|
||||||
|
new_id = 'term-' + str(len(gloss_entries))
|
||||||
|
gloss_entries.add(new_id)
|
||||||
|
objects['term', termtext.lower()] = env.docname, new_id
|
||||||
|
|
||||||
|
# add an index entry too
|
||||||
|
indexnode = addnodes.index()
|
||||||
|
indexnode['entries'] = [('single', termtext, new_id, 'main')]
|
||||||
|
new_termnodes = []
|
||||||
|
new_termnodes.append(indexnode)
|
||||||
|
new_termnodes.extend(node.children)
|
||||||
|
new_termnodes.append(addnodes.termsep())
|
||||||
|
for termnode in new_termnodes:
|
||||||
|
termnode.source, termnode.line = node.source, node.line
|
||||||
|
|
||||||
|
return new_id, termtext, new_termnodes
|
||||||
|
|
||||||
|
|
||||||
|
def make_term_from_paragraph_node(termnodes, ids):
|
||||||
|
# make a single "term" node with all the terms, separated by termsep
|
||||||
|
# nodes (remove the dangling trailing separator)
|
||||||
|
term = nodes.term('', '', *termnodes[:-1])
|
||||||
|
term.source, term.line = termnodes[0].source, termnodes[0].line
|
||||||
|
term.rawsource = term.astext()
|
||||||
|
term['ids'].extend(ids)
|
||||||
|
term['names'].extend(ids)
|
||||||
|
return term
|
||||||
|
|
||||||
|
|
||||||
class Glossary(Directive):
|
class Glossary(Directive):
|
||||||
"""
|
"""
|
||||||
Directive to create a glossary with cross-reference targets for :term:
|
Directive to create a glossary with cross-reference targets for :term:
|
||||||
@ -221,8 +256,6 @@ class Glossary(Directive):
|
|||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
env = self.state.document.settings.env
|
env = self.state.document.settings.env
|
||||||
objects = env.domaindata['std']['objects']
|
|
||||||
gloss_entries = env.temp_data.setdefault('gloss_entries', set())
|
|
||||||
node = addnodes.glossary()
|
node = addnodes.glossary()
|
||||||
node.document = self.state.document
|
node.document = self.state.document
|
||||||
|
|
||||||
@ -296,31 +329,15 @@ class Glossary(Directive):
|
|||||||
# get a text-only representation of the term and register it
|
# get a text-only representation of the term and register it
|
||||||
# as a cross-reference target
|
# as a cross-reference target
|
||||||
tmp = nodes.paragraph('', '', *res[0])
|
tmp = nodes.paragraph('', '', *res[0])
|
||||||
termtext = tmp.astext()
|
tmp.source = source
|
||||||
new_id = 'term-' + nodes.make_id(termtext)
|
tmp.line = lineno
|
||||||
if new_id in gloss_entries:
|
new_id, termtext, new_termnodes = \
|
||||||
new_id = 'term-' + str(len(gloss_entries))
|
make_termnodes_from_paragraph_node(env, tmp)
|
||||||
gloss_entries.add(new_id)
|
|
||||||
ids.append(new_id)
|
ids.append(new_id)
|
||||||
objects['term', termtext.lower()] = env.docname, new_id
|
|
||||||
termtexts.append(termtext)
|
termtexts.append(termtext)
|
||||||
# add an index entry too
|
|
||||||
indexnode = addnodes.index()
|
|
||||||
indexnode['entries'] = [('single', termtext, new_id, 'main')]
|
|
||||||
new_termnodes = []
|
|
||||||
new_termnodes.append(indexnode)
|
|
||||||
new_termnodes.extend(res[0])
|
|
||||||
new_termnodes.append(addnodes.termsep())
|
|
||||||
for termnode in new_termnodes:
|
|
||||||
termnode.source, termnode.line = source, lineno
|
|
||||||
termnodes.extend(new_termnodes)
|
termnodes.extend(new_termnodes)
|
||||||
# make a single "term" node with all the terms, separated by termsep
|
|
||||||
# nodes (remove the dangling trailing separator)
|
term = make_term_from_paragraph_node(termnodes, ids)
|
||||||
term = nodes.term('', '', *termnodes[:-1])
|
|
||||||
term.source, term.line = termnodes[0].source, termnodes[0].line
|
|
||||||
term.rawsource = term.astext()
|
|
||||||
term['ids'].extend(ids)
|
|
||||||
term['names'].extend(ids)
|
|
||||||
term += system_messages
|
term += system_messages
|
||||||
|
|
||||||
defnode = nodes.definition()
|
defnode = nodes.definition()
|
||||||
|
@ -24,6 +24,10 @@ from sphinx.util.nodes import traverse_translatable_index, extract_messages
|
|||||||
from sphinx.util.osutil import ustrftime, find_catalog
|
from sphinx.util.osutil import ustrftime, find_catalog
|
||||||
from sphinx.util.compat import docutils_version
|
from sphinx.util.compat import docutils_version
|
||||||
from sphinx.util.pycompat import all
|
from sphinx.util.pycompat import all
|
||||||
|
from sphinx.domains.std import (
|
||||||
|
make_term_from_paragraph_node,
|
||||||
|
make_termnodes_from_paragraph_node,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
default_substitutions = set([
|
default_substitutions = set([
|
||||||
@ -259,6 +263,16 @@ class Locale(Transform):
|
|||||||
if refname in refname_ids_map:
|
if refname in refname_ids_map:
|
||||||
new["ids"] = refname_ids_map[refname]
|
new["ids"] = refname_ids_map[refname]
|
||||||
|
|
||||||
|
# glossary terms update refid
|
||||||
|
if isinstance(node, nodes.term):
|
||||||
|
new_id, _, termnodes = \
|
||||||
|
make_termnodes_from_paragraph_node(env, patch)
|
||||||
|
term = make_term_from_paragraph_node(
|
||||||
|
termnodes, [new_id])
|
||||||
|
patch = term
|
||||||
|
node['ids'] = patch['ids']
|
||||||
|
node['names'] = patch['names']
|
||||||
|
|
||||||
# Original pending_xref['reftarget'] contain not-translated
|
# Original pending_xref['reftarget'] contain not-translated
|
||||||
# target name, new pending_xref must use original one.
|
# target name, new pending_xref must use original one.
|
||||||
# This code restricts to change ref-targets in the translation.
|
# This code restricts to change ref-targets in the translation.
|
||||||
@ -268,11 +282,24 @@ class Locale(Transform):
|
|||||||
if len(old_refs) != len(new_refs):
|
if len(old_refs) != len(new_refs):
|
||||||
env.warn_node('inconsistent term references in '
|
env.warn_node('inconsistent term references in '
|
||||||
'translated message', node)
|
'translated message', node)
|
||||||
|
def get_key(node):
|
||||||
|
key = node["refdomain"], node["reftype"]
|
||||||
|
if key == ('std', 'term'):
|
||||||
|
key = None
|
||||||
|
elif key == ('std', 'ref'):
|
||||||
|
key += (node['reftarget'],)
|
||||||
|
elif key == ('', 'doc'):
|
||||||
|
key += (node['reftarget'],)
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
return key
|
||||||
|
|
||||||
for old in old_refs:
|
for old in old_refs:
|
||||||
key = old["reftype"], old["refdomain"]
|
key = get_key(old)
|
||||||
xref_reftarget_map[key] = old["reftarget"]
|
if key:
|
||||||
|
xref_reftarget_map[key] = old["reftarget"]
|
||||||
for new in new_refs:
|
for new in new_refs:
|
||||||
key = new["reftype"], new["refdomain"]
|
key = get_key(new)
|
||||||
if key in xref_reftarget_map:
|
if key in xref_reftarget_map:
|
||||||
new['reftarget'] = xref_reftarget_map[key]
|
new['reftarget'] = xref_reftarget_map[key]
|
||||||
|
|
||||||
|
@ -21,3 +21,16 @@ msgstr "I18N ROCK'N ROLE XREF"
|
|||||||
|
|
||||||
msgid "link to :term:`Some term`, :ref:`i18n-role-xref`, :doc:`contents`."
|
msgid "link to :term:`Some term`, :ref:`i18n-role-xref`, :doc:`contents`."
|
||||||
msgstr "LINK TO :ref:`i18n-role-xref`, :doc:`contents`, :term:`SOME NEW TERM`."
|
msgstr "LINK TO :ref:`i18n-role-xref`, :doc:`contents`, :term:`SOME NEW TERM`."
|
||||||
|
|
||||||
|
msgid "same type links"
|
||||||
|
msgstr "SAME TYPE LINKS"
|
||||||
|
|
||||||
|
msgid "link to :term:`Some term` and :term:`Some other term`."
|
||||||
|
msgstr "LINK TO :term:`SOME OTHER NEW TERM` AND :term:`SOME NEW TERM`."
|
||||||
|
|
||||||
|
msgid "link to :ref:`i18n-role-xref` and :ref:`same-type-links`."
|
||||||
|
msgstr "LINK TO :ref:`same-type-links` AND :ref:`i18n-role-xref`."
|
||||||
|
|
||||||
|
msgid "link to :doc:`contents` and :doc:`glossary_terms`."
|
||||||
|
msgstr "LINK TO :doc:`glossary_terms` AND :doc:`contents`."
|
||||||
|
|
||||||
|
@ -7,3 +7,13 @@ i18n role xref
|
|||||||
|
|
||||||
link to :term:`Some term`, :ref:`i18n-role-xref`, :doc:`contents`.
|
link to :term:`Some term`, :ref:`i18n-role-xref`, :doc:`contents`.
|
||||||
|
|
||||||
|
.. _same-type-links:
|
||||||
|
|
||||||
|
same type links
|
||||||
|
=================
|
||||||
|
|
||||||
|
link to :term:`Some term` and :term:`Some other term`.
|
||||||
|
|
||||||
|
link to :ref:`i18n-role-xref` and :ref:`same-type-links`.
|
||||||
|
|
||||||
|
link to :doc:`contents` and :doc:`glossary_terms`.
|
||||||
|
@ -14,6 +14,7 @@ import os
|
|||||||
import re
|
import re
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
from sphinx.util.pycompat import relpath
|
from sphinx.util.pycompat import relpath
|
||||||
|
|
||||||
@ -292,25 +293,70 @@ def test_i18n_glossary_terms(app):
|
|||||||
assert 'term not in glossary' not in warnings
|
assert 'term not in glossary' not in warnings
|
||||||
|
|
||||||
|
|
||||||
@with_intl_app(buildername='text', warning=warnfile)
|
@with_intl_app(buildername='xml', warning=warnfile)
|
||||||
def test_i18n_role_xref(app):
|
def test_i18n_role_xref(app):
|
||||||
# regression test for #1090
|
# regression test for #1090
|
||||||
|
|
||||||
|
def gettexts(elem):
|
||||||
|
def itertext(self):
|
||||||
|
# this function copied from Python-2.7 'ElementTree.itertext'.
|
||||||
|
# for compatibility to Python-2.5, 2.6, 3.1
|
||||||
|
tag = self.tag
|
||||||
|
if not isinstance(tag, basestring) and tag is not None:
|
||||||
|
return
|
||||||
|
if self.text:
|
||||||
|
yield self.text
|
||||||
|
for e in self:
|
||||||
|
for s in itertext(e):
|
||||||
|
yield s
|
||||||
|
if e.tail:
|
||||||
|
yield e.tail
|
||||||
|
return filter(None, [s.strip() for s in itertext(elem)])
|
||||||
|
|
||||||
|
def getref(elem):
|
||||||
|
return elem.attrib.get('refid') or elem.attrib.get('refuri')
|
||||||
|
|
||||||
|
def assert_text_refs(elem, text, refs):
|
||||||
|
_text = gettexts(elem)
|
||||||
|
assert _text == text
|
||||||
|
_refs = map(getref, elem.findall('reference'))
|
||||||
|
assert _refs == refs
|
||||||
|
|
||||||
app.builddir.rmtree(True) #for warnings acceleration
|
app.builddir.rmtree(True) #for warnings acceleration
|
||||||
app.builder.build(['role_xref'])
|
app.builder.build(['role_xref'])
|
||||||
result = (app.outdir / 'role_xref.txt').text(encoding='utf-8')
|
et = ElementTree.parse(app.outdir / 'role_xref.xml')
|
||||||
expect = (
|
sec1, sec2 = et.findall('section')
|
||||||
u"\nI18N ROCK'N ROLE XREF"
|
|
||||||
u"\n*********************\n"
|
|
||||||
u"\nLINK TO *I18N ROCK'N ROLE XREF*, *CONTENTS*, *SOME NEW TERM*.\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
para1, = sec1.findall('paragraph')
|
||||||
|
assert_text_refs(
|
||||||
|
para1,
|
||||||
|
['LINK TO', "I18N ROCK'N ROLE XREF", ',', 'CONTENTS', ',',
|
||||||
|
'SOME NEW TERM', '.'],
|
||||||
|
['i18n-role-xref',
|
||||||
|
'contents',
|
||||||
|
'glossary_terms#term-some-new-term'])
|
||||||
|
|
||||||
|
para21, para22, para23 = sec2.findall('paragraph')
|
||||||
|
assert_text_refs(
|
||||||
|
para21,
|
||||||
|
['LINK TO', 'SOME OTHER NEW TERM', 'AND', 'SOME NEW TERM', '.'],
|
||||||
|
['glossary_terms#term-some-other-new-term',
|
||||||
|
'glossary_terms#term-some-new-term'])
|
||||||
|
assert_text_refs(
|
||||||
|
para22,
|
||||||
|
['LINK TO', 'SAME TYPE LINKS', 'AND', "I18N ROCK'N ROLE XREF", '.'],
|
||||||
|
['same-type-links', 'i18n-role-xref'])
|
||||||
|
assert_text_refs(
|
||||||
|
para23,
|
||||||
|
['LINK TO', 'I18N WITH GLOSSARY TERMS', 'AND', 'CONTENTS', '.'],
|
||||||
|
['glossary_terms', 'contents'])
|
||||||
|
|
||||||
|
#warnings
|
||||||
warnings = warnfile.getvalue().replace(os.sep, '/')
|
warnings = warnfile.getvalue().replace(os.sep, '/')
|
||||||
assert 'term not in glossary' not in warnings
|
assert 'term not in glossary' not in warnings
|
||||||
assert 'undefined label' not in warnings
|
assert 'undefined label' not in warnings
|
||||||
assert 'unknown document' not in warnings
|
assert 'unknown document' not in warnings
|
||||||
|
|
||||||
assert result == expect
|
|
||||||
|
|
||||||
|
|
||||||
@with_intl_app(buildername='text', warning=warnfile)
|
@with_intl_app(buildername='text', warning=warnfile)
|
||||||
def test_i18n_glossary_terms_inconsistency(app):
|
def test_i18n_glossary_terms_inconsistency(app):
|
||||||
|
Loading…
Reference in New Issue
Block a user