Merge branch 'stable'

This commit is contained in:
shimizukawa 2015-09-13 09:47:38 +09:00
commit ef890a45d3
25 changed files with 382 additions and 66 deletions

16
CHANGES
View File

@ -18,11 +18,13 @@ Features added
Bugs fixed Bugs fixed
---------- ----------
* #1976: Avoid "2.0" version of Babel because it doesn't work with Windows environment.
* Add a "default.css" stylesheet (which imports "classic.css") for compatibility.
* #1788: graphviz extension raises exception when caption option is present.
* #1789: ``:pyobject:`` option of ``literalinclude`` directive includes following * #1789: ``:pyobject:`` option of ``literalinclude`` directive includes following
lines after class definitions. lines after class definitions.
* #1790: ``literalinclude`` strips empty lines at the head and tail. * #1790: ``literalinclude`` strips empty lines at the head and tail.
* #1913: C++, fix assert bug for enumerators in next-to-global and global scope. * #1913: C++, fix assert bug for enumerators in next-to-global and global scope.
* #1790: ``literalinclude`` strips empty lines at the head and tail
* #1802: load plugin themes automatically when theme.conf use it as 'inherit'. Thanks to * #1802: load plugin themes automatically when theme.conf use it as 'inherit'. Thanks to
Takayuki Hirai. Takayuki Hirai.
* #1794: custom theme extended from alabaster or sphinx_rtd_theme can't find base theme. * #1794: custom theme extended from alabaster or sphinx_rtd_theme can't find base theme.
@ -47,15 +49,21 @@ Bugs fixed
* #1942: Fix a KeyError in websupport. * #1942: Fix a KeyError in websupport.
* #1903: Fix strange id generation for glossary terms. * #1903: Fix strange id generation for glossary terms.
* #1796, On py3, automated .mo building cause UnicodeDecodeError * #1796, On py3, automated .mo building cause UnicodeDecodeError
* Fix: ``make text`` will crush if a definition list item has more than 1 classifiers as:
* #1796: On py3, automated .mo building cause UnicodeDecodeError
* ``make text`` will crush if a definition list item has more than 1 classifiers as: * ``make text`` will crush if a definition list item has more than 1 classifiers as:
* Fixed #1855: make gettext generates broken po file for definition lists with classifier.
``term : classifier1 : classifier2``. ``term : classifier1 : classifier2``.
* #1855: make gettext generates broken po file for definition lists with classifier. * #1855: make gettext generates broken po file for definition lists with classifier.
* #1869: Fix problems when dealing with files containing non-ASCII characters. Thanks to * #1869: Fix problems when dealing with files containing non-ASCII characters. Thanks to
Marvin Schmidt. Marvin Schmidt.
* #1798: Fix building LaTeX with references in titles. * #1798: Fix building LaTeX with references in titles.
* #1725: On py2 environment, doctest with using non-ASCII characters causes
``'ascii' codec can't decode byte`` exception.
* #1540: Fix RuntimeError with circular referenced toctree
* #1983: i18n translation feature breaks references which uses section name.
* #1990: Use caption of toctree to title of \tableofcontents in LaTeX
* #1987: Fix ampersand is ignored in ``:menuselection:`` and ``:guilabel:`` on LaTeX builder
* #1994: More supporting non-standard parser (like recommonmark parser) for Translation and
WebSupport feature. Now node.rawsource is fall backed to node.astext() during docutils
transforming.
Documentation Documentation
------------- -------------

View File

@ -51,7 +51,7 @@ requires = [
'Pygments>=2.0', 'Pygments>=2.0',
'docutils>=0.11', 'docutils>=0.11',
'snowballstemmer>=1.1', 'snowballstemmer>=1.1',
'babel>=1.3', 'babel>=1.3,!=2.0',
'alabaster>=0.7,<0.8', 'alabaster>=0.7,<0.8',
'sphinx_rtd_theme>=0.1,<0.2', 'sphinx_rtd_theme>=0.1,<0.2',
] ]

View File

@ -160,10 +160,14 @@ class Builder(object):
def compile_catalogs(self, catalogs, message): def compile_catalogs(self, catalogs, message):
if not self.config.gettext_auto_build: if not self.config.gettext_auto_build:
return return
def cat2relpath(cat):
return path.relpath(cat.mo_path, self.env.srcdir).replace(path.sep, SEP)
self.info(bold('building [mo]: ') + message) self.info(bold('building [mo]: ') + message)
for catalog in self.app.status_iterator( for catalog in self.app.status_iterator(
catalogs, 'writing output... ', darkgreen, len(catalogs), catalogs, 'writing output... ', darkgreen, len(catalogs),
lambda c: c.mo_path): cat2relpath):
catalog.write_mo(self.config.language) catalog.write_mo(self.config.language)
def compile_all_catalogs(self): def compile_all_catalogs(self):

View File

@ -945,7 +945,7 @@ class SingleFileHTMLBuilder(StandaloneHTMLBuilder):
def assemble_doctree(self): def assemble_doctree(self):
master = self.config.master_doc master = self.config.master_doc
tree = self.env.get_doctree(master) tree = self.env.get_doctree(master)
tree = inline_all_toctrees(self, set(), master, tree, darkgreen) tree = inline_all_toctrees(self, set(), master, tree, darkgreen, [master])
tree['docname'] = master tree['docname'] = master
self.env.resolve_references(tree, master, self) self.env.resolve_references(tree, master, self)
self.fix_refuris(tree) self.fix_refuris(tree)

View File

@ -102,11 +102,22 @@ class LaTeXBuilder(Builder):
doctree.settings = docsettings doctree.settings = docsettings
doctree.settings.author = author doctree.settings.author = author
doctree.settings.title = title doctree.settings.title = title
doctree.settings.contentsname = self.get_contentsname(docname)
doctree.settings.docname = docname doctree.settings.docname = docname
doctree.settings.docclass = docclass doctree.settings.docclass = docclass
docwriter.write(doctree, destination) docwriter.write(doctree, destination)
self.info("done") self.info("done")
def get_contentsname(self, indexfile):
tree = self.env.get_doctree(indexfile)
contentsname = None
for toctree in tree.traverse(addnodes.toctree):
if toctree['caption']:
contentsname = toctree['caption']
break
return contentsname
def assemble_doctree(self, indexfile, toctree_only, appendices): def assemble_doctree(self, indexfile, toctree_only, appendices):
self.docnames = set([indexfile] + appendices) self.docnames = set([indexfile] + appendices)
self.info(darkgreen(indexfile) + " ", nonl=1) self.info(darkgreen(indexfile) + " ", nonl=1)
@ -124,7 +135,7 @@ class LaTeXBuilder(Builder):
new_sect += node new_sect += node
tree = new_tree tree = new_tree
largetree = inline_all_toctrees(self, self.docnames, indexfile, tree, largetree = inline_all_toctrees(self, self.docnames, indexfile, tree,
darkgreen) darkgreen, [indexfile])
largetree['docname'] = indexfile largetree['docname'] = indexfile
for docname in appendices: for docname in appendices:
appendix = self.env.get_doctree(docname) appendix = self.env.get_doctree(docname)

View File

@ -70,7 +70,7 @@ class ManualPageBuilder(Builder):
tree = self.env.get_doctree(docname) tree = self.env.get_doctree(docname)
docnames = set() docnames = set()
largetree = inline_all_toctrees(self, docnames, docname, tree, largetree = inline_all_toctrees(self, docnames, docname, tree,
darkgreen) darkgreen, [docname])
self.info('} ', nonl=True) self.info('} ', nonl=True)
self.env.resolve_references(largetree, docname, self) self.env.resolve_references(largetree, docname, self)
# remove pending_xref nodes # remove pending_xref nodes

View File

@ -180,7 +180,7 @@ class TexinfoBuilder(Builder):
new_sect += node new_sect += node
tree = new_tree tree = new_tree
largetree = inline_all_toctrees(self, self.docnames, indexfile, tree, largetree = inline_all_toctrees(self, self.docnames, indexfile, tree,
darkgreen) darkgreen, [indexfile])
largetree['docname'] = indexfile largetree['docname'] = indexfile
for docname in appendices: for docname in appendices:
appendix = self.env.get_doctree(docname) appendix = self.env.get_doctree(docname)

View File

@ -142,7 +142,7 @@ class Author(Directive):
env = self.state.document.settings.env env = self.state.document.settings.env
if not env.config.show_authors: if not env.config.show_authors:
return [] return []
para = nodes.paragraph() para = nodes.paragraph(translatable=False)
emph = nodes.emphasis() emph = nodes.emphasis()
para += emph para += emph
if self.name == 'sectionauthor': if self.name == 'sectionauthor':
@ -205,7 +205,7 @@ class VersionChange(Directive):
if len(self.arguments) == 2: if len(self.arguments) == 2:
inodes, messages = self.state.inline_text(self.arguments[1], inodes, messages = self.state.inline_text(self.arguments[1],
self.lineno+1) self.lineno+1)
para = nodes.paragraph(self.arguments[1], '', *inodes) para = nodes.paragraph(self.arguments[1], '', *inodes, translatable=False)
set_source_info(self, para) set_source_info(self, para)
node.append(para) node.append(para)
else: else:
@ -218,13 +218,14 @@ class VersionChange(Directive):
content.source = node[0].source content.source = node[0].source
content.line = node[0].line content.line = node[0].line
content += node[0].children content += node[0].children
node[0].replace_self(nodes.paragraph('', '', content)) node[0].replace_self(nodes.paragraph('', '', content, translatable=False))
node[0].insert(0, nodes.inline('', '%s: ' % text, node[0].insert(0, nodes.inline('', '%s: ' % text,
classes=['versionmodified'])) classes=['versionmodified']))
else: else:
para = nodes.paragraph('', '', para = nodes.paragraph('', '',
nodes.inline('', '%s.' % text, nodes.inline('', '%s.' % text,
classes=['versionmodified'])) classes=['versionmodified']),
translatable=False)
node.append(para) node.append(para)
env = self.state.document.settings.env env = self.state.document.settings.env
# XXX should record node.source as well # XXX should record node.source as well

View File

@ -49,9 +49,11 @@ from sphinx.util.websupport import is_commentable
from sphinx.errors import SphinxError, ExtensionError from sphinx.errors import SphinxError, ExtensionError
from sphinx.locale import _ from sphinx.locale import _
from sphinx.versioning import add_uids, merge_doctrees from sphinx.versioning import add_uids, merge_doctrees
from sphinx.transforms import DefaultSubstitutions, MoveModuleTargets, \ from sphinx.transforms import (
HandleCodeBlocks, AutoNumbering, SortIds, CitationReferences, Locale, \ DefaultSubstitutions, MoveModuleTargets, ApplySourceWorkaround,
RemoveTranslatableInline, SphinxContentsFilter, ExtraTranslatableNodes HandleCodeBlocks, AutoNumbering, SortIds, CitationReferences, Locale,
RemoveTranslatableInline, SphinxContentsFilter, ExtraTranslatableNodes,
)
orig_role_function = roles.role orig_role_function = roles.role
@ -99,7 +101,7 @@ class SphinxStandaloneReader(standalone.Reader):
""" """
Add our own transforms. Add our own transforms.
""" """
transforms = [ExtraTranslatableNodes, Locale, CitationReferences, transforms = [ApplySourceWorkaround, ExtraTranslatableNodes, Locale, CitationReferences,
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks,
AutoNumbering, SortIds, RemoveTranslatableInline] AutoNumbering, SortIds, RemoveTranslatableInline]

View File

@ -9,14 +9,16 @@
:copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from __future__ import absolute_import
import re import re
import sys import sys
import time import time
import codecs import codecs
from os import path from os import path
import doctest
from six import itervalues, StringIO, binary_type from six import itervalues, StringIO, binary_type, text_type, PY2
from docutils import nodes from docutils import nodes
from docutils.parsers.rst import directives from docutils.parsers.rst import directives
@ -26,13 +28,30 @@ from sphinx.util import force_decode
from sphinx.util.nodes import set_source_info from sphinx.util.nodes import set_source_info
from sphinx.util.compat import Directive from sphinx.util.compat import Directive
from sphinx.util.console import bold from sphinx.util.console import bold
from sphinx.util.osutil import fs_encoding
# circumvent relative import
doctest = __import__('doctest')
blankline_re = re.compile(r'^\s*<BLANKLINE>', re.MULTILINE) blankline_re = re.compile(r'^\s*<BLANKLINE>', re.MULTILINE)
doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE) doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE)
if PY2:
def doctest_encode(text, encoding):
if isinstance(text, text_type):
text = text.encode(encoding)
if text.startswith(codecs.BOM_UTF8):
text = text[len(codecs.BOM_UTF8):]
return text
else:
def doctest_encode(text, encoding):
return text
class _SpoofOutSphinx(doctest._SpoofOut):
# override: convert console encoding to unicode
if PY2:
def getvalue(self):
result = doctest._SpoofOut.getvalue(self)
return result.decode('string_escape')
# set up the necessary directives # set up the necessary directives
@ -165,6 +184,11 @@ class TestCode(object):
class SphinxDocTestRunner(doctest.DocTestRunner): class SphinxDocTestRunner(doctest.DocTestRunner):
def __init__(self, *args, **kw):
doctest.DocTestRunner.__init__(self, *args, **kw)
# Override a fake output target for capturing doctest output.
self._fakeout = _SpoofOutSphinx()
def summarize(self, out, verbose=None): def summarize(self, out, verbose=None):
string_io = StringIO() string_io = StringIO()
old_stdout = sys.stdout old_stdout = sys.stdout
@ -358,19 +382,25 @@ Doctest summary
return compile(code, name, self.type, flags, dont_inherit) return compile(code, name, self.type, flags, dont_inherit)
def test_group(self, group, filename): def test_group(self, group, filename):
if PY2:
filename_str = filename.encode(fs_encoding)
else:
filename_str = filename
ns = {} ns = {}
def run_setup_cleanup(runner, testcodes, what): def run_setup_cleanup(runner, testcodes, what):
examples = [] examples = []
for testcode in testcodes: for testcode in testcodes:
examples.append(doctest.Example(testcode.code, '', examples.append(doctest.Example(
lineno=testcode.lineno)) doctest_encode(testcode.code, self.env.config.source_encoding), '',
lineno=testcode.lineno))
if not examples: if not examples:
return True return True
# simulate a doctest with the code # simulate a doctest with the code
sim_doctest = doctest.DocTest(examples, {}, sim_doctest = doctest.DocTest(examples, {},
'%s (%s code)' % (group.name, what), '%s (%s code)' % (group.name, what),
filename, 0, None) filename_str, 0, None)
sim_doctest.globs = ns sim_doctest.globs = ns
old_f = runner.failures old_f = runner.failures
self.type = 'exec' # the snippet may contain multiple statements self.type = 'exec' # the snippet may contain multiple statements
@ -389,8 +419,9 @@ Doctest summary
if len(code) == 1: if len(code) == 1:
# ordinary doctests (code/output interleaved) # ordinary doctests (code/output interleaved)
try: try:
test = parser.get_doctest(code[0].code, {}, group.name, test = parser.get_doctest(
filename, code[0].lineno) doctest_encode(code[0].code, self.env.config.source_encoding), {},
group.name, filename_str, code[0].lineno)
except Exception: except Exception:
self.warn('ignoring invalid doctest code: %r' % self.warn('ignoring invalid doctest code: %r' %
code[0].code, code[0].code,
@ -416,12 +447,13 @@ Doctest summary
exc_msg = m.group('msg') exc_msg = m.group('msg')
else: else:
exc_msg = None exc_msg = None
example = doctest.Example(code[0].code, output, example = doctest.Example(
exc_msg=exc_msg, doctest_encode(code[0].code, self.env.config.source_encoding), output,
lineno=code[0].lineno, exc_msg=exc_msg,
options=options) lineno=code[0].lineno,
options=options)
test = doctest.DocTest([example], {}, group.name, test = doctest.DocTest([example], {}, group.name,
filename, code[0].lineno, None) filename_str, code[0].lineno, None)
self.type = 'exec' # multiple statements again self.type = 'exec' # multiple statements again
# DocTest.__init__ copies the globs namespace, which we don't want # DocTest.__init__ copies the globs namespace, which we don't want
test.globs = ns test.globs = ns

View File

@ -22,6 +22,7 @@ from sphinx.locale import _, init as init_locale
from sphinx.util import split_index_msg from sphinx.util import split_index_msg
from sphinx.util.nodes import ( from sphinx.util.nodes import (
traverse_translatable_index, extract_messages, LITERAL_TYPE_NODES, IMAGE_TYPE_NODES, traverse_translatable_index, extract_messages, LITERAL_TYPE_NODES, IMAGE_TYPE_NODES,
apply_source_workaround,
) )
from sphinx.util.osutil import ustrftime from sphinx.util.osutil import ustrftime
from sphinx.util.i18n import find_catalog from sphinx.util.i18n import find_catalog
@ -169,6 +170,18 @@ TRANSLATABLE_NODES = {
} }
class ApplySourceWorkaround(Transform):
"""
update source and rawsource attributes
"""
default_priority = 10
def apply(self):
for n in self.document.traverse():
if isinstance(n, nodes.TextElement):
apply_source_workaround(n)
class ExtraTranslatableNodes(Transform): class ExtraTranslatableNodes(Transform):
""" """
make nodes translatable make nodes translatable
@ -277,8 +290,10 @@ class Locale(Transform):
# document nameids mapping with new name. # document nameids mapping with new name.
names = section_node.setdefault('names', []) names = section_node.setdefault('names', [])
names.append(new_name) names.append(new_name)
if old_name in names: # Original section name (reference target name) should be kept to refer
names.remove(old_name) # from other nodes which is still not translated or uses explicit target
# name like "`text to display <explicit target name_>`_"..
# So, `old_name` is still exist in `names`.
_id = self.document.nameids.get(old_name, None) _id = self.document.nameids.get(old_name, None)
explicit = self.document.nametypes.get(old_name, None) explicit = self.document.nametypes.get(old_name, None)

View File

@ -50,6 +50,10 @@ def apply_source_workaround(node):
# overwrite: ``term : classifier1 : classifier2`` -> ``term text`` # overwrite: ``term : classifier1 : classifier2`` -> ``term text``
node.rawsource = node.astext() node.rawsource = node.astext()
# workaround: recommonmark-0.2.0 doesn't set rawsource attribute
if not node.rawsource:
node.rawsource = node.astext()
if node.source and node.rawsource: if node.source and node.rawsource:
return return
@ -74,18 +78,20 @@ IGNORED_NODES = (
nodes.Inline, nodes.Inline,
nodes.literal_block, nodes.literal_block,
nodes.doctest_block, nodes.doctest_block,
addnodes.versionmodified,
# XXX there are probably more # XXX there are probably more
) )
def is_translatable(node): def is_translatable(node):
if isinstance(node, nodes.TextElement): if isinstance(node, nodes.TextElement):
apply_source_workaround(node)
if not node.source: if not node.source:
return False # built-in message return False # built-in message
if isinstance(node, IGNORED_NODES) and 'translatable' not in node: if isinstance(node, IGNORED_NODES) and 'translatable' not in node:
return False return False
if not node.get('translatable', True):
# not(node['translatable'] == True or node['translatable'] is None)
return False
# <field_name>orphan</field_name> # <field_name>orphan</field_name>
# XXX ignore all metadata (== docinfo) # XXX ignore all metadata (== docinfo)
if isinstance(node, nodes.field_name) and node.children[0] == 'orphan': if isinstance(node, nodes.field_name) and node.children[0] == 'orphan':
@ -228,7 +234,7 @@ def process_index_entry(entry, targetid):
return indexentries return indexentries
def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc): def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc, traversed):
"""Inline all toctrees in the *tree*. """Inline all toctrees in the *tree*.
Record all docnames in *docnameset*, and output docnames with *colorfunc*. Record all docnames in *docnameset*, and output docnames with *colorfunc*.
@ -238,23 +244,25 @@ def inline_all_toctrees(builder, docnameset, docname, tree, colorfunc):
newnodes = [] newnodes = []
includefiles = map(text_type, toctreenode['includefiles']) includefiles = map(text_type, toctreenode['includefiles'])
for includefile in includefiles: for includefile in includefiles:
try: if includefile not in traversed:
builder.info(colorfunc(includefile) + " ", nonl=1) try:
subtree = inline_all_toctrees(builder, docnameset, includefile, traversed.append(includefile)
builder.env.get_doctree(includefile), builder.info(colorfunc(includefile) + " ", nonl=1)
colorfunc) subtree = inline_all_toctrees(builder, docnameset, includefile,
docnameset.add(includefile) builder.env.get_doctree(includefile),
except Exception: colorfunc, traversed)
builder.warn('toctree contains ref to nonexisting ' docnameset.add(includefile)
'file %r' % includefile, except Exception:
builder.env.doc2path(docname)) builder.warn('toctree contains ref to nonexisting '
else: 'file %r' % includefile,
sof = addnodes.start_of_file(docname=includefile) builder.env.doc2path(docname))
sof.children = subtree.children else:
for sectionnode in sof.traverse(nodes.section): sof = addnodes.start_of_file(docname=includefile)
if 'docname' not in sectionnode: sof.children = subtree.children
sectionnode['docname'] = includefile for sectionnode in sof.traverse(nodes.section):
newnodes.append(sof) if 'docname' not in sectionnode:
sectionnode['docname'] = includefile
newnodes.append(sof)
toctreenode.parent.replace(toctreenode, newnodes) toctreenode.parent.replace(toctreenode, newnodes)
return tree return tree

View File

@ -43,6 +43,7 @@ HEADER = r'''%% Generated by Sphinx.
\usepackage{sphinx} \usepackage{sphinx}
\usepackage{multirow} \usepackage{multirow}
%(usepackages)s %(usepackages)s
%(contentsname)s
%(numfig_format)s %(numfig_format)s
%(preamble)s %(preamble)s
@ -159,6 +160,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
'longtable': '\\usepackage{longtable}', 'longtable': '\\usepackage{longtable}',
'usepackages': '', 'usepackages': '',
'numfig_format': '', 'numfig_format': '',
'contentsname': '',
'preamble': '', 'preamble': '',
'title': '', 'title': '',
'date': '', 'date': '',
@ -248,6 +250,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
return '\\usepackage{%s}' % (packagename,) return '\\usepackage{%s}' % (packagename,)
usepackages = (declare_package(*p) for p in builder.usepackages) usepackages = (declare_package(*p) for p in builder.usepackages)
self.elements['usepackages'] += "\n".join(usepackages) self.elements['usepackages'] += "\n".join(usepackages)
if getattr(document.settings, 'contentsname', None):
self.elements['contentsname'] = \
self.babel_renewcommand(builder, '\\contentsname',
document.settings.contentsname)
self.elements['numfig_format'] = self.generate_numfig_format(builder) self.elements['numfig_format'] = self.generate_numfig_format(builder)
# allow the user to override them all # allow the user to override them all
self.elements.update(builder.config.latex_elements) self.elements.update(builder.config.latex_elements)
@ -329,8 +335,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
encode('ascii', 'backslashreplace').decode('ascii').\ encode('ascii', 'backslashreplace').decode('ascii').\
replace('\\', '_') replace('\\', '_')
def generate_numfig_format(self, builder): def babel_renewcommand(self, builder, command, definition):
ret = []
if builder.config.language == 'ja': if builder.config.language == 'ja':
babel_prefix = '' babel_prefix = ''
babel_suffix = '' babel_suffix = ''
@ -349,15 +354,18 @@ class LaTeXTranslator(nodes.NodeVisitor):
babel_prefix = '' babel_prefix = ''
babel_suffix = '' babel_suffix = ''
return ('%s\\renewcommand{%s}{%s}%s\n' %
(babel_prefix, command, definition, babel_suffix))
def generate_numfig_format(self, builder):
ret = []
figure = self.builder.config.numfig_format['figure'].split('%s', 1) figure = self.builder.config.numfig_format['figure'].split('%s', 1)
if len(figure) == 1: if len(figure) == 1:
ret.append('\\def\\fnum@figure{%s}\n' % ret.append('\\def\\fnum@figure{%s}\n' %
text_type(figure[0]).translate(tex_escape_map)) text_type(figure[0]).translate(tex_escape_map))
else: else:
ret.append('%s\\renewcommand{\\figurename}{%s}%s\n' % definition = text_type(figure[0]).translate(tex_escape_map)
(babel_prefix, ret.append(self.babel_renewcommand(builder, '\\figurename', definition))
text_type(figure[0]).translate(tex_escape_map),
babel_suffix))
if figure[1]: if figure[1]:
ret.append('\\makeatletter\n') ret.append('\\makeatletter\n')
ret.append('\\def\\fnum@figure{\\figurename\\thefigure%s}\n' % ret.append('\\def\\fnum@figure{\\figurename\\thefigure%s}\n' %
@ -369,10 +377,8 @@ class LaTeXTranslator(nodes.NodeVisitor):
ret.append('\\def\\fnum@table{%s}\n' % ret.append('\\def\\fnum@table{%s}\n' %
text_type(table[0]).translate(tex_escape_map)) text_type(table[0]).translate(tex_escape_map))
else: else:
ret.append('%s\\renewcommand{\\tablename}{%s}%s\n' % definition = text_type(table[0]).translate(tex_escape_map)
(babel_prefix, ret.append(self.babel_renewcommand(builder, '\\tablename', definition))
text_type(table[0]).translate(tex_escape_map),
babel_suffix))
if table[1]: if table[1]:
ret.append('\\makeatletter\n') ret.append('\\makeatletter\n')
ret.append('\\def\\fnum@table{\\tablename\\thetable%s}\n' % ret.append('\\def\\fnum@table{\\tablename\\thetable%s}\n' %
@ -1707,6 +1713,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
if classes in [['menuselection'], ['guilabel']]: if classes in [['menuselection'], ['guilabel']]:
self.body.append(r'\emph{') self.body.append(r'\emph{')
self.context.append('}') self.context.append('}')
elif classes in [['accelerator']]:
self.body.append(r'\underline{')
self.context.append('}')
elif classes and not self.in_title: elif classes and not self.in_title:
self.body.append(r'\DUspan{%s}{' % ','.join(classes)) self.body.append(r'\DUspan{%s}{' % ','.join(classes))
self.context.append('}') self.context.append('}')

View File

@ -0,0 +1,4 @@
===
Bar
===

View File

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
master_doc = 'index'
html_theme = 'classic'

View File

@ -0,0 +1,4 @@
===
Foo
===

View File

@ -0,0 +1,8 @@
test-tocdepth
=============
.. toctree::
:caption: Table of content
foo
bar

View File

@ -127,3 +127,12 @@ Special directives
import test_ext_doctest import test_ext_doctest
test_ext_doctest.cleanup_call() test_ext_doctest.cleanup_call()
non-ASCII result
----------------
>>> print('umlauts: äöü.')
umlauts: äöü.
>>> print('Japanese: 日本語')
Japanese: 日本語

View File

@ -22,3 +22,4 @@ CONTENTS
versionchange versionchange
docfields docfields
raw raw
refs

View File

@ -0,0 +1,85 @@
#
msgid ""
msgstr ""
"Project-Id-Version: 1191 1.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-08-08 15:31+0900\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "Translation Tips"
msgstr "X TIPS"
msgid "A-1. Here's how you can `download Sphinx`_."
msgstr "A-1. HERE'S HOW YOU CAN `download Sphinx`_."
msgid "A-2. Here's how you can `download Sphinx`_."
msgstr "A-2. HERE'S HOW YOU CAN `A1 DOWNLOAD SPHINX`_."
msgid "A-3. Here's how you can `download Sphinx`_."
msgstr ""
"A-3. HERE'S HOW YOU CAN `A3 DOWNLOAD SPHINX <download Sphinx_>`_ AND `A3 DOWNLOAD "
"SPHINX <download Sphinx_>`_."
msgid "B-1. `Docutils site`_ and `Sphinx site`_."
msgstr "B-1. `Docutils site`_ and `Sphinx site`_."
msgid "B-2. `Docutils site`_ and `Sphinx site`_."
msgstr "B-2. `B1 DOCUTILS SITE`_ AND `B1 SPHINX SITE`_."
msgid "B-3. `Docutils site`_ and `Sphinx site`_."
msgstr "B-3. `B2 SPHINX SITE`_ AND `B2 DOCUTILS SITE`_."
msgid "B-4. `Docutils site`_ and `Sphinx site`_."
msgstr ""
"B-4. `B4 SPHINX SITE <Sphinx site_>`_ AND `B4 DOCUTILS SITE <Docutils "
"site_>`_."
msgid "B-5. `Docutils site`_ and `Sphinx site`_."
msgstr ""
"B-5. `B5 SPHINX SITE <Sphinx site_>`_ AND `B5 DOCUTILS SITE <Docutils "
"site_>`_\" AND `B5 SPHINX SITE <Sphinx site_>`_."
msgid "C-1. Link to `Translation Tips`_ section."
msgstr "C-1. LINK TO `Translation Tips`_ SECTION."
msgid "C-2. Link to `Translation Tips`_ section."
msgstr "C-2. LINK TO `X TIPS`_ SECTION."
msgid "C-3. Link to `Translation Tips`_ section."
msgstr "C-3. LINK TO `X TIPS <Translation Tips_>`_ SECTION."
msgid "C-4. Link to `Translation Tips`_ section."
msgstr ""
"C-4. LINK TO `X TIPS <Translation Tips_>`_ x `X TIPS <Translation Tips_>`_ "
"SECTION."
msgid "C-5. Link to `Translation Tips`_ section."
msgstr ""
"C-5. LINK TO `TRANS <X TIPS_>`_ x `LATION <X TIPS_>`_ "
msgid "D-1. Link to `Translation Tips`_ and `Next Section`_ section."
msgstr "D-1. LINK TO `Translation Tips`_ and `Next Section`_ SECTION."
msgid "D-2. Link to `Translation Tips`_ and `Next Section`_ section."
msgstr "D-2. LINK TO `X TIPS`_ AND `N SECTION`_ SECTION."
msgid "D-3. Link to `Translation Tips`_ and `Next Section`_ section."
msgstr "D-3. LINK TO `N SECTION`_ AND `X TIPS`_ SECTION."
msgid "D-4. Link to `Translation Tips`_ and `Next Section`_ section."
msgstr ""
"D-4. LINK TO `N SECTION <Next Section_>`_ AND `X TIPS <Translation Tips_>`_ "
"SECTION."
msgid "D-5. Link to `Translation Tips`_ and `Next Section`_ section."
msgstr ""
"D-5. LINK TO `Next <N SECTION_>`_ AND `Tips <X TIPS_>`_ "
msgid "Next Section"
msgstr "N SECTION"

View File

@ -0,0 +1,47 @@
References
===========
Translation Tips
-----------------
.. _download Sphinx: https://pypi.python.org/pypi/sphinx
.. _Docutils site: http://docutils.sourceforge.net/
.. _Sphinx site: http://sphinx-doc.org/
A-1. Here's how you can `download Sphinx`_.
A-2. Here's how you can `download Sphinx`_.
A-3. Here's how you can `download Sphinx`_.
B-1. `Docutils site`_ and `Sphinx site`_.
B-2. `Docutils site`_ and `Sphinx site`_.
B-3. `Docutils site`_ and `Sphinx site`_.
B-4. `Docutils site`_ and `Sphinx site`_.
C-1. Link to `Translation Tips`_ section.
C-2. Link to `Translation Tips`_ section.
C-3. Link to `Translation Tips`_ section.
C-4. Link to `Translation Tips`_ section.
C-5. Link to `Translation Tips`_ section.
D-1. Link to `Translation Tips`_ and `Next Section`_ section.
D-2. Link to `Translation Tips`_ and `Next Section`_ section.
D-3. Link to `Translation Tips`_ and `Next Section`_ section.
D-4. Link to `Translation Tips`_ and `Next Section`_ section.
D-5. Link to `Translation Tips`_ and `Next Section`_ section.
Next Section
-------------

View File

@ -275,3 +275,25 @@ def test_latex_add_latex_package(app, status, warning):
result = (app.outdir / 'SphinxTests.tex').text(encoding='utf8') result = (app.outdir / 'SphinxTests.tex').text(encoding='utf8')
assert '\\usepackage{foo}' in result assert '\\usepackage{foo}' in result
assert '\\usepackage[baz]{bar}' in result assert '\\usepackage[baz]{bar}' in result
@with_app(buildername='latex', testroot='contentsname')
def test_contentsname(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'Python.tex').text(encoding='utf8')
print(result)
print(status.getvalue())
print(warning.getvalue())
assert ('\\addto\\captionsenglish{\\renewcommand{\\contentsname}{Table of content}}'
in result)
@with_app(buildername='latex', testroot='contentsname',
confoverrides={'language': 'ja'})
def test_contentsname_with_language_ja(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'Python.tex').text(encoding='utf8')
print(result)
print(status.getvalue())
print(warning.getvalue())
assert '\\renewcommand{\\contentsname}{Table of content}' in result

View File

@ -751,3 +751,11 @@ def test_additional_targets_should_be_translated(app, status, warning):
expected_expr = """<img alt="IMG -&gt; I18N" src="_images/i18n.png" />""" expected_expr = """<img alt="IMG -&gt; I18N" src="_images/i18n.png" />"""
yield assert_count(expected_expr, result, 1) yield assert_count(expected_expr, result, 1)
@gen_with_intl_app('text', freshenv=True)
def test_references(app, status, warning):
app.builder.build_specific([app.srcdir / 'refs.txt'])
warnings = warning.getvalue().replace(os.sep, '/')
warning_expr = u'refs.txt:\\d+: ERROR: Unknown target name:'
yield assert_count(warning_expr, warnings, 0)

View File

@ -109,7 +109,7 @@ def test_inline():
yield (verify, ':guilabel:`&Foo -&&- &Bar`', yield (verify, ':guilabel:`&Foo -&&- &Bar`',
u'<p><span class="guilabel"><span class="accelerator">F</span>oo ' u'<p><span class="guilabel"><span class="accelerator">F</span>oo '
'-&amp;- <span class="accelerator">B</span>ar</span></p>', '-&amp;- <span class="accelerator">B</span>ar</span></p>',
r'\emph{\DUspan{accelerator}{F}oo -\&- \DUspan{accelerator}{B}ar}') r'\emph{\underline{F}oo -\&- \underline{B}ar}')
# non-interpolation of dashes in option role # non-interpolation of dashes in option role
yield (verify_re, ':option:`--with-option`', yield (verify_re, ':option:`--with-option`',

View File

@ -16,13 +16,24 @@ from docutils.utils import new_document
from docutils import frontend from docutils import frontend
from sphinx.util.nodes import extract_messages from sphinx.util.nodes import extract_messages
from sphinx.transforms import ApplySourceWorkaround
def _get_doctree(text): def _transform(doctree):
ApplySourceWorkaround(doctree).apply()
def create_new_document():
settings = frontend.OptionParser( settings = frontend.OptionParser(
components=(rst.Parser,)).get_default_values() components=(rst.Parser,)).get_default_values()
document = new_document('dummy.txt', settings) document = new_document('dummy.txt', settings)
return document
def _get_doctree(text):
document = create_new_document()
rst.Parser().parse(text, document) rst.Parser().parse(text, document)
_transform(document)
return document return document
@ -119,3 +130,26 @@ def test_extract_messages():
extract_messages(_get_doctree(text)), extract_messages(_get_doctree(text)),
nodes.line, 2, nodes.line, 2,
) )
def test_extract_messages_without_rawsource():
"""
Check node.rawsource is fall-backed by using node.astext() value.
`extract_message` which is used from Sphinx i18n feature drop ``not node.rawsource`` nodes.
So, all nodes which want to translate must have ``rawsource`` value.
However, sometimes node.rawsource is not set.
For example: recommonmark-0.2.0 doesn't set rawsource to `paragraph` node.
refs #1994: Fall back to node's astext() during i18n message extraction.
"""
p = nodes.paragraph()
p.append(nodes.Text('test'))
p.append(nodes.Text('sentence'))
assert not p.rawsource # target node must not have rawsource value
document = create_new_document()
document.append(p)
_transform(document)
assert_node_count(extract_messages(document), nodes.TextElement, 1)
assert [m for n, m in extract_messages(document)][0], 'text sentence'