diff --git a/CHANGES b/CHANGES
index afb5c990c..d179eea3f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -141,6 +141,12 @@ Bugs fixed
----------
* #4924: html search: Upper characters problem in any other languages
+* #4932: apidoc: some subpackage is ignored if sibling subpackage contains a
+ module starting with underscore
+* #4938, #4939: i18n doesn't handle node.title correctly tat used for contents,
+ topic, admonition, table and section.
+* #4913: i18n: literal blocks in bullet list are not translated
+* #4962: cpp domain: raises TypeError on duplicate declaration
Testing
--------
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index 2998792ae..98eabb956 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -6054,7 +6054,7 @@ class CPPDomain(Domain):
msg = __("Duplicate declaration, also defined in '%s'.\n"
"Name of declaration is '%s'.")
msg = msg % (ourNames[name], name)
- logger.warning(msg, docname)
+ logger.warning(msg, location=docname)
else:
ourNames[name] = docname
diff --git a/sphinx/ext/apidoc.py b/sphinx/ext/apidoc.py
index ad024ea54..8a7c68402 100644
--- a/sphinx/ext/apidoc.py
+++ b/sphinx/ext/apidoc.py
@@ -202,8 +202,8 @@ def shall_skip(module, opts, excludes=[]):
# Yes, check if we have any non-excluded modules at all here
all_skipped = True
basemodule = path.dirname(module)
- for module in glob.glob(path.join(basemodule, '*.py')):
- if not is_excluded(path.join(basemodule, module), excludes):
+ for submodule in glob.glob(path.join(basemodule, '*.py')):
+ if not is_excluded(path.join(basemodule, submodule), excludes):
# There's a non-excluded module here, we won't skip
all_skipped = False
if all_skipped:
diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py
index be7f9af5b..397470317 100644
--- a/sphinx/transforms/i18n.py
+++ b/sphinx/transforms/i18n.py
@@ -248,13 +248,32 @@ class Locale(SphinxTransform):
if isinstance(node, LITERAL_TYPE_NODES):
msgstr = '::\n\n' + indent(msgstr, ' ' * 3)
+ # Structural Subelements phase1
+ # There is a possibility that only the title node is created.
+ # see: http://docutils.sourceforge.net/docs/ref/doctree.html#structural-subelements
+ if isinstance(node, nodes.title):
+ # This generates:
+ msgstr = msgstr + '\n' + '-' * len(msgstr) * 2
+
patch = publish_msgstr(self.app, msgstr, source,
node.line, self.config, settings)
- # XXX doctest and other block markup
- if not isinstance(
- patch,
- (nodes.paragraph,) + LITERAL_TYPE_NODES + IMAGE_TYPE_NODES):
- continue # skip for now
+
+ # Structural Subelements phase2
+ if isinstance(node, nodes.title):
+ # get
node that placed as a first child
+ patch = patch.next_node()
+
+ # ignore unexpected markups in translation message
+ if not isinstance(patch, (
+ (nodes.paragraph, # expected form of translation
+ nodes.title, # generated by above "Subelements phase2"
+ ) +
+ # following types are expected if
+ # config.gettext_additional_targets is configured
+ LITERAL_TYPE_NODES +
+ IMAGE_TYPE_NODES
+ )):
+ continue # skip
# auto-numbered foot note reference should use original 'ids'.
def is_autonumber_footnote_ref(node):
diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py
index 3ca8155dd..d3441565b 100644
--- a/sphinx/util/nodes.py
+++ b/sphinx/util/nodes.py
@@ -81,6 +81,11 @@ def apply_source_workaround(node):
logger.debug('[i18n] PATCH: %r to have source, line: %s',
get_full_module_name(node), repr_domxml(node))
node.source, node.line = node.parent.source, node.parent.line
+ if isinstance(node, nodes.title) and node.source is None:
+ # Uncomment these lines after merging into master(1.8)
+ # logger.debug('[i18n] PATCH: %r to have source: %s',
+ # get_full_module_name(node), repr_domxml(node))
+ node.source, node.line = node.parent.source, node.parent.line
if isinstance(node, nodes.term):
logger.debug('[i18n] PATCH: %r to have rawsource: %s',
get_full_module_name(node), repr_domxml(node))
@@ -89,6 +94,10 @@ def apply_source_workaround(node):
node.rawsource = re.sub(r'\s*:\s*%s' % re.escape(classifier.astext()),
'', node.rawsource)
+ # workaround: literal_block under bullet list (#4913)
+ if isinstance(node, nodes.literal_block) and node.source is None:
+ node.source = find_source_node(node)
+
# workaround: recommonmark-0.2.0 doesn't set rawsource attribute
if not node.rawsource:
node.rawsource = node.astext()
diff --git a/tests/roots/test-intl/admonitions.po b/tests/roots/test-intl/admonitions.po
index bc722e58b..f114e33e7 100644
--- a/tests/roots/test-intl/admonitions.po
+++ b/tests/roots/test-intl/admonitions.po
@@ -79,3 +79,6 @@ msgstr "ADMONITION TITLE"
msgid "admonition body"
msgstr "ADMONITION BODY"
+msgid "1. admonition title"
+msgstr "1. ADMONITION TITLE"
+
diff --git a/tests/roots/test-intl/admonitions.txt b/tests/roots/test-intl/admonitions.txt
index 170b8ed57..a539461c1 100644
--- a/tests/roots/test-intl/admonitions.txt
+++ b/tests/roots/test-intl/admonitions.txt
@@ -44,3 +44,7 @@ Admonitions
admonition body
+.. admonition:: 1. admonition title
+
+ admonition body
+
diff --git a/tests/roots/test-intl/contents.txt b/tests/roots/test-intl/contents.txt
index e2336856c..b818e99c7 100644
--- a/tests/roots/test-intl/contents.txt
+++ b/tests/roots/test-intl/contents.txt
@@ -28,3 +28,5 @@ CONTENTS
docfields
raw
refs
+ section
+ topic
diff --git a/tests/roots/test-intl/literalblock.po b/tests/roots/test-intl/literalblock.po
index 0f2ddc613..040295359 100644
--- a/tests/roots/test-intl/literalblock.po
+++ b/tests/roots/test-intl/literalblock.po
@@ -72,6 +72,11 @@ msgstr ""
" return 0;\n"
"}"
+msgid "literal-block\n"
+"in list"
+msgstr "LITERAL-BLOCK\n"
+"IN LIST"
+
msgid "doctest blocks"
msgstr "DOCTEST-BLOCKS"
diff --git a/tests/roots/test-intl/literalblock.txt b/tests/roots/test-intl/literalblock.txt
index 57041fe41..2b9eb8eb1 100644
--- a/tests/roots/test-intl/literalblock.txt
+++ b/tests/roots/test-intl/literalblock.txt
@@ -44,6 +44,11 @@ code blocks
}
+* ::
+
+ literal-block
+ in list
+
doctest blocks
==============
diff --git a/tests/roots/test-intl/section.po b/tests/roots/test-intl/section.po
new file mode 100644
index 000000000..4af349c91
--- /dev/null
+++ b/tests/roots/test-intl/section.po
@@ -0,0 +1,28 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2018, dev
+# This file is distributed under the same license as the sphinx package.
+# FIRST AUTHOR , 2018.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: sphinx 1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-05-06 16:44+0900\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.4.0\n"
+
+msgid "1. Section"
+msgstr "1. SECTION"
+
+msgid "2. Sub Section"
+msgstr "2. SUB SECTION"
+
+msgid "3. Contents Title"
+msgstr "3. CONTENTS TITLE"
+
diff --git a/tests/roots/test-intl/section.txt b/tests/roots/test-intl/section.txt
new file mode 100644
index 000000000..ae0604cac
--- /dev/null
+++ b/tests/roots/test-intl/section.txt
@@ -0,0 +1,8 @@
+1. Section
+==========
+
+.. contents:: 3. Contents Title
+ :local:
+
+2. Sub Section
+--------------
diff --git a/tests/roots/test-intl/table.po b/tests/roots/test-intl/table.po
index 9f8d687c3..d8ffd3571 100644
--- a/tests/roots/test-intl/table.po
+++ b/tests/roots/test-intl/table.po
@@ -48,3 +48,7 @@ msgstr "TEXT5"
msgid "text6"
msgstr "TEXT6"
+
+msgid "1. table caption"
+msgstr "1. TABLE CAPTION"
+
diff --git a/tests/roots/test-intl/table.txt b/tests/roots/test-intl/table.txt
index 2eab86248..cf824380d 100644
--- a/tests/roots/test-intl/table.txt
+++ b/tests/roots/test-intl/table.txt
@@ -12,3 +12,9 @@ i18n with table
text3 text4
text5 text6
======= =======
+
+.. table:: 1. table caption
+
+ +-----+
+ |text1|
+ +-----+
diff --git a/tests/roots/test-intl/topic.po b/tests/roots/test-intl/topic.po
new file mode 100644
index 000000000..53ecb0da2
--- /dev/null
+++ b/tests/roots/test-intl/topic.po
@@ -0,0 +1,31 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2018, dev
+# This file is distributed under the same license as the sphinx package.
+# FIRST AUTHOR , 2018.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: sphinx 1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2018-05-06 16:44+0900\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.4.0\n"
+
+msgid "i18n with topic"
+msgstr "I18N WITH TOPIC"
+
+msgid "Topic Title"
+msgstr "TOPIC TITLE"
+
+msgid "Topic Content"
+msgstr "TOPIC CONTENT"
+
+msgid "1. Topic Title"
+msgstr "1. TOPIC TITLE"
+
diff --git a/tests/roots/test-intl/topic.txt b/tests/roots/test-intl/topic.txt
new file mode 100644
index 000000000..255a33464
--- /dev/null
+++ b/tests/roots/test-intl/topic.txt
@@ -0,0 +1,13 @@
+:tocdepth: 2
+
+i18n with topic
+================
+
+.. topic:: Topic Title
+
+ Topic Content
+
+.. topic:: 1. Topic Title
+
+ Topic Content
+
diff --git a/tests/test_intl.py b/tests/test_intl.py
index b94fe85df..11e14ef84 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -306,6 +306,30 @@ def test_text_glossary_term_inconsistencies(app, warning):
assert_re_search(expected_warning_expr, warnings)
+@sphinx_intl
+@pytest.mark.sphinx('gettext')
+@pytest.mark.test_params(shared_result='test_intl_gettext')
+def test_gettext_section(app):
+ app.build()
+ # --- section
+ expect = read_po(app.srcdir / 'section.po')
+ actual = read_po(app.outdir / 'section.pot')
+ for expect_msg in [m for m in expect if m.id]:
+ assert expect_msg.id in [m.id for m in actual if m.id]
+
+
+@sphinx_intl
+@pytest.mark.sphinx('text')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_text_section(app):
+ app.build()
+ # --- section
+ result = (app.outdir / 'section.txt').text(encoding='utf-8')
+ expect = read_po(app.srcdir / 'section.po')
+ for expect_msg in [m for m in expect if m.id]:
+ assert expect_msg.string in result
+
+
@sphinx_intl
@pytest.mark.sphinx('text')
@pytest.mark.test_params(shared_result='test_intl_basic')
@@ -428,6 +452,9 @@ def test_text_admonitions(app):
assert d.upper() + " TITLE" in result
assert d.upper() + " BODY" in result
+ # for #4938 `1. ` prefixed admonition title
+ assert "1. ADMONITION TITLE" in result
+
@sphinx_intl
@pytest.mark.sphinx('gettext')
@@ -453,6 +480,42 @@ def test_gettext_table(app):
assert expect_msg.id in [m.id for m in actual if m.id]
+@sphinx_intl
+@pytest.mark.sphinx('text')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_text_table(app):
+ app.build()
+ # --- toctree
+ result = (app.outdir / 'table.txt').text(encoding='utf-8')
+ expect = read_po(app.srcdir / 'table.po')
+ for expect_msg in [m for m in expect if m.id]:
+ assert expect_msg.string in result
+
+
+@sphinx_intl
+@pytest.mark.sphinx('gettext')
+@pytest.mark.test_params(shared_result='test_intl_gettext')
+def test_gettext_topic(app):
+ app.build()
+ # --- topic
+ expect = read_po(app.srcdir / 'topic.po')
+ actual = read_po(app.outdir / 'topic.pot')
+ for expect_msg in [m for m in expect if m.id]:
+ assert expect_msg.id in [m.id for m in actual if m.id]
+
+
+@sphinx_intl
+@pytest.mark.sphinx('text')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_text_topic(app):
+ app.build()
+ # --- topic
+ result = (app.outdir / 'topic.txt').text(encoding='utf-8')
+ expect = read_po(app.srcdir / 'topic.po')
+ for expect_msg in [m for m in expect if m.id]:
+ assert expect_msg.string in result
+
+
@sphinx_intl
@pytest.mark.sphinx('gettext')
@pytest.mark.test_params(shared_result='test_intl_gettext')
@@ -1003,6 +1066,14 @@ def test_additional_targets_should_not_be_translated(app):
"""<stdio.h>""")
assert_count(expected_expr, result, 1)
+ # literal block in list item should not be translated
+ expected_expr = ("""literal"""
+ """-"""
+ """block\n"""
+ """in """
+ """list""")
+ assert_count(expected_expr, result, 1)
+
# doctest block should not be translated but be highlighted
expected_expr = (
""">>> """
@@ -1069,6 +1140,14 @@ def test_additional_targets_should_be_translated(app):
"""<STDIO.H>""")
assert_count(expected_expr, result, 1)
+ # literal block in list item should be translated
+ expected_expr = ("""LITERAL"""
+ """-"""
+ """BLOCK\n"""
+ """IN """
+ """LIST""")
+ assert_count(expected_expr, result, 1)
+
# doctest block should not be translated but be highlighted
expected_expr = (
""">>> """
diff --git a/utils/release-checklist b/utils/release-checklist
index 2ffbc6a81..84cbb3829 100644
--- a/utils/release-checklist
+++ b/utils/release-checklist
@@ -11,7 +11,8 @@ for stable releases
* Edit CHANGES if empty section exists
* ``git commit -am 'Bump to X.Y.Z final'``
* ``make clean``
-* ``python setup.py release bdist_wheel sdist upload --identity=[your key]``
+* ``python setup.py release bdist_wheel sdist``
+* ``twine upload dist/ --sign --identity [your GPG key]``
* open https://pypi.org/project/Sphinx/ and check there are no obvious errors
* ``git tag vX.Y.Z``
* ``python utils/bump_version.py --in-develop X.Y.Zb0`` (ex. 1.5.3b0)
@@ -37,7 +38,8 @@ for first beta releases
* Edit CHANGES if empty section exists
* ``git commit -am 'Bump to X.Y.0 beta1'``
* ``make clean``
-* ``python setup.py release bdist_wheel sdist upload --identity=[your key]``
+* ``python setup.py release bdist_wheel sdist``
+* ``twine upload dist/ --sign --identity [your GPG key]``
* open https://pypi.org/project/Sphinx/ and check there are no obvious errors
* ``git tag vX.Y.0b1``
* ``python utils/bump_version.py --in-develop X.Y.0b2`` (ex. 1.6.0b2)
@@ -66,7 +68,8 @@ for other beta releases
* Edit CHANGES if empty section exists
* ``git commit -am 'Bump to X.Y.0 betaN'``
* ``make clean``
-* ``python setup.py release bdist_wheel sdist upload --identity=[your key]``
+* ``python setup.py release bdist_wheel sdist``
+* ``twine upload dist/ --sign --identity [your GPG key]``
* open https://pypi.org/project/Sphinx/ and check there are no obvious errors
* ``git tag vX.Y.0bN``
* ``python utils/bump_version.py --in-develop X.Y.0bM`` (ex. 1.6.0b3)
@@ -94,7 +97,8 @@ for major releases
* Edit CHANGES if empty section exists
* ``git commit -am 'Bump to X.Y.0 final'``
* ``make clean``
-* ``python setup.py release bdist_wheel sdist upload --identity=[your key]``
+* ``python setup.py release bdist_wheel sdist``
+* ``twine upload dist/ --sign --identity [your GPG key]``
* open https://pypi.org/project/Sphinx/ and check there are no obvious errors
* ``git tag vX.Y.0``
* ``python utils/bump_version.py --in-develop X.Y.1b0`` (ex. 1.6.1b0)