diff --git a/sphinx/environment.py b/sphinx/environment.py
index 32e8eb5bd..e7e030240 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -45,7 +45,8 @@ from sphinx.errors import SphinxError, ExtensionError
from sphinx.locale import _
from sphinx.versioning import add_uids, merge_doctrees
from sphinx.transforms import DefaultSubstitutions, MoveModuleTargets, \
- HandleCodeBlocks, SortIds, CitationReferences, Locale, SphinxContentsFilter
+ HandleCodeBlocks, SortIds, CitationReferences, Locale, \
+ RemoveTranslatableInline, SphinxContentsFilter
orig_role_function = roles.role
@@ -90,7 +91,8 @@ class SphinxStandaloneReader(standalone.Reader):
Add our own transforms.
"""
transforms = [Locale, CitationReferences, DefaultSubstitutions,
- MoveModuleTargets, HandleCodeBlocks, SortIds]
+ MoveModuleTargets, HandleCodeBlocks, SortIds,
+ RemoveTranslatableInline]
def get_transforms(self):
return standalone.Reader.get_transforms(self) + self.transforms
diff --git a/sphinx/transforms.py b/sphinx/transforms.py
index 5b96300de..97721eb5e 100644
--- a/sphinx/transforms.py
+++ b/sphinx/transforms.py
@@ -305,6 +305,24 @@ class Locale(Transform):
node['entries'] = new_entries
+class RemoveTranslatableInline(Transform):
+ """
+ Remove inline nodes used for translation as placeholders.
+ """
+ default_priority = 999
+
+ def apply(self):
+ from sphinx.builders.gettext import MessageCatalogBuilder
+ env = self.document.settings.env
+ builder = env.app.builder
+ if isinstance(builder, MessageCatalogBuilder):
+ return
+ for inline in self.document.traverse(nodes.inline):
+ if 'translatable' in inline:
+ inline.parent.remove(inline)
+ inline.parent += inline.children
+
+
class SphinxContentsFilter(ContentsFilter):
"""
Used with BuildEnvironment.add_toc_from() to discard cross-file links
diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py
index 961b09a78..b59d3f317 100644
--- a/sphinx/util/docfields.py
+++ b/sphinx/util/docfields.py
@@ -67,7 +67,7 @@ class Field(object):
fieldname += nodes.Text(' ')
fieldname += self.make_xref(self.rolename, domain,
fieldarg, nodes.Text)
- fieldbody = nodes.field_body('', nodes.paragraph('', '', *content))
+ fieldbody = nodes.field_body('', nodes.paragraph('', '', content))
return nodes.field('', fieldname, fieldbody)
@@ -255,6 +255,12 @@ class DocFieldTransformer(object):
[nodes.Text(argtype)]
fieldarg = argname
+ translatable_content = nodes.inline(fieldbody.rawsource,
+ translatable=True)
+ translatable_content.source = fieldbody.parent.source
+ translatable_content.line = fieldbody.parent.line
+ translatable_content += content
+
# grouped entries need to be collected in one entry, while others
# get one entry per field
if typedesc.is_grouped:
@@ -264,10 +270,11 @@ class DocFieldTransformer(object):
groupindices[typename] = len(entries)
group = [typedesc, []]
entries.append(group)
- group[1].append(typedesc.make_entry(fieldarg, content))
+ entry = typedesc.make_entry(fieldarg, translatable_content)
+ group[1].append(entry)
else:
- entries.append([typedesc,
- typedesc.make_entry(fieldarg, content)])
+ entry = typedesc.make_entry(fieldarg, translatable_content)
+ entries.append([typedesc, entry])
# step 2: all entries are collected, construct the new field list
new_list = nodes.field_list()
diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py
index 1d4d3ba53..5a7749bef 100644
--- a/sphinx/util/nodes.py
+++ b/sphinx/util/nodes.py
@@ -61,7 +61,7 @@ def extract_messages(doctree):
if not node.source:
continue # built-in message
- if isinstance(node, IGNORED_NODES):
+ if isinstance(node, IGNORED_NODES) and 'translatable' not in node:
continue
# orphan
# XXX ignore all metadata (== docinfo)
diff --git a/tests/roots/test-intl/contents.txt b/tests/roots/test-intl/contents.txt
index 298beb6ce..a3c0e3549 100644
--- a/tests/roots/test-intl/contents.txt
+++ b/tests/roots/test-intl/contents.txt
@@ -19,3 +19,4 @@ CONTENTS
role_xref
glossary_terms
glossary_terms_inconsistency
+ docfields
diff --git a/tests/roots/test-intl/docfields.po b/tests/roots/test-intl/docfields.po
new file mode 100644
index 000000000..f906ca19e
--- /dev/null
+++ b/tests/roots/test-intl/docfields.po
@@ -0,0 +1,39 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2010, Georg Brandl & Team
+# This file is distributed under the same license as the Sphinx package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: Sphinx 0.6\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2012-12-16 14:11\n"
+"PO-Revision-Date: 2012-12-18 06:14+0900\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"
+
+msgid "i18n with docfields"
+msgstr "I18N WITH DOCFIELDS"
+
+msgid "description of parameter param"
+msgstr "DESCRIPTION OF PARAMETER param"
+
+msgid "description of parameter foo"
+msgstr "DESCRIPTION OF PARAMETER foo"
+
+msgid "description of parameter bar"
+msgstr "DESCRIPTION OF PARAMETER bar"
+
+msgid "if the values are not valid"
+msgstr "IF THE VALUES ARE NOT VALID"
+
+msgid "if the values are out of range"
+msgstr "IF THE VALUES ARE OUT OF RANGE"
+
+msgid "a new :class:`Cls3` instance"
+msgstr "A NEW :class:`Cls3` INSTANCE"
+
diff --git a/tests/roots/test-intl/docfields.txt b/tests/roots/test-intl/docfields.txt
new file mode 100644
index 000000000..e4dab8e55
--- /dev/null
+++ b/tests/roots/test-intl/docfields.txt
@@ -0,0 +1,46 @@
+:tocdepth: 2
+
+i18n with docfields
+===================
+
+.. single TypedField
+
+.. class:: Cls1
+ :noindex:
+
+ :param param: description of parameter param
+
+.. grouped TypedFields
+
+.. class:: Cls2
+ :noindex:
+
+ :param foo: description of parameter foo
+ :param bar: description of parameter bar
+
+
+.. single GroupedField
+
+.. class:: Cls3(values)
+ :noindex:
+
+ :raises ValueError: if the values are out of range
+
+.. grouped GroupedFields
+
+.. class:: Cls4(values)
+ :noindex:
+
+ :raises TypeError: if the values are not valid
+ :raises ValueError: if the values are out of range
+
+
+.. single Field
+
+.. class:: Cls5
+ :noindex:
+
+ :returns: a new :class:`Cls3` instance
+
+.. Field is never grouped
+
diff --git a/tests/test_intl.py b/tests/test_intl.py
index 4dec84646..77b5a5616 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -387,3 +387,36 @@ def test_i18n_index_entries(app):
]
for expr in expected_exprs:
assert re.search(expr, result, re.M)
+
+
+@with_intl_app(buildername='text', cleanenv=True)
+def test_i18n_docfields(app):
+ app.builder.build(['docfields'])
+ result = (app.outdir / 'docfields.txt').text(encoding='utf-8')
+ expect = (u"\nI18N WITH DOCFIELDS"
+ u"\n*******************\n"
+ u"\nclass class Cls1\n"
+ u"\n Parameters:"
+ u"\n **param** -- DESCRIPTION OF PARAMETER param\n"
+ u"\nclass class Cls2\n"
+ u"\n Parameters:"
+ u"\n * **foo** -- DESCRIPTION OF PARAMETER foo\n"
+ u"\n * **bar** -- DESCRIPTION OF PARAMETER bar\n"
+ u"\nclass class Cls3(values)\n"
+ u"\n Raises ValueError:"
+ u"\n IF THE VALUES ARE OUT OF RANGE\n"
+ u"\nclass class Cls4(values)\n"
+ u"\n Raises:"
+ u"\n * **TypeError** -- IF THE VALUES ARE NOT VALID\n"
+ u"\n * **ValueError** -- IF THE VALUES ARE OUT OF RANGE\n"
+ u"\nclass class Cls5\n"
+ u"\n Returns:"
+ u'\n A NEW "Cls3" INSTANCE\n')
+ assert result == expect
+
+
+@with_intl_app(buildername='html', cleanenv=True)
+def test_i18n_docfields_html(app):
+ app.builder.build(['docfields'])
+ result = (app.outdir / 'docfields.html').text(encoding='utf-8')
+ # expect no error by build