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