Fix #1734: Could not translate the caption of toctree directive

This commit is contained in:
Takeshi KOMIYA 2016-08-21 22:15:50 +09:00
parent c6e102999f
commit 9b00c31ee2
9 changed files with 118 additions and 7 deletions

View File

@ -127,6 +127,7 @@ Bugs fixed
* #2874: gettext builder could not extract all text under the ``only``
directives
* #2485: autosummary crashes with multiple source_suffix values
* #1734: Could not translate the caption of toctree directive
Documentation
-------------

View File

@ -14,9 +14,53 @@ import warnings
from docutils import nodes
class toctree(nodes.General, nodes.Element):
class translatable:
"""Node which supports translation.
The translation goes forward with following steps:
1. Preserve original translatable messages
2. Apply translated messages from message catalog
3. Extract preserved messages (for gettext builder)
The translatable nodes MUST preserve original messages.
And these messages should not be overridden at applying step.
Because they are used at final step; extraction.
"""
def preserve_original_messages(self):
"""Preserve original translatable messages."""
raise NotImplementedError
def apply_translated_message(self, original_message, translated_message):
"""Apply translated message."""
raise NotImplementedError
def extract_original_messages(self):
"""Extract translation messages.
:returns: list of extracted messages or messages generator
"""
raise NotImplementedError
class toctree(nodes.General, nodes.Element, translatable):
"""Node for inserting a "TOC tree"."""
def preserve_original_messages(self):
if 'caption' in self:
self['rawcaption'] = self['caption']
def apply_translated_message(self, original_message, translated_message):
if self.get('rawcaption') == original_message:
self['caption'] = translated_message
def extract_original_messages(self):
if 'rawcaption' in self:
return [self['rawcaption']]
else:
return []
# domain-specific object descriptions (class, function etc.)

View File

@ -1466,7 +1466,15 @@ class BuildEnvironment:
newnode = addnodes.compact_paragraph('', '')
caption = toctree.attributes.get('caption')
if caption:
newnode += nodes.caption(caption, '', *[nodes.Text(caption)])
caption_node = nodes.caption(caption, '', *[nodes.Text(caption)])
caption_node.line = toctree.line
caption_node.source = toctree.source
caption_node.rawsource = toctree['rawcaption']
if hasattr(toctree, 'uid'):
# move uid to caption_node to translate it
caption_node.uid = toctree.uid
del toctree.uid
newnode += caption_node
newnode.extend(tocentries)
newnode['toctree'] = True

View File

@ -13,9 +13,11 @@ from docutils.readers import standalone
from docutils.writers import UnfilteredWriter
from six import string_types, text_type
from sphinx.transforms import ApplySourceWorkaround, ExtraTranslatableNodes, Locale, \
CitationReferences, DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks, \
from sphinx.transforms import (
ApplySourceWorkaround, ExtraTranslatableNodes, PreserveTranslatableMessages, Locale,
CitationReferences, DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks,
AutoNumbering, AutoIndexUpgrader, SortIds, RemoveTranslatableInline
)
from sphinx.util import import_object, split_docinfo
@ -57,9 +59,10 @@ class SphinxStandaloneReader(SphinxBaseReader):
"""
Add our own transforms.
"""
transforms = [ApplySourceWorkaround, ExtraTranslatableNodes, Locale, CitationReferences,
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks,
AutoNumbering, AutoIndexUpgrader, SortIds, RemoveTranslatableInline]
transforms = [ApplySourceWorkaround, ExtraTranslatableNodes, PreserveTranslatableMessages,
Locale, CitationReferences, DefaultSubstitutions, MoveModuleTargets,
HandleCodeBlocks, AutoNumbering, AutoIndexUpgrader, SortIds,
RemoveTranslatableInline, PreserveTranslatableMessages]
class SphinxI18nReader(SphinxBaseReader):

View File

@ -238,6 +238,17 @@ def publish_msgstr(app, source, source_path, source_line, config, settings):
return doc
class PreserveTranslatableMessages(Transform):
"""
Preserve original translatable messages befor translation
"""
default_priority = 10 # this MUST be invoked before Locale transform
def apply(self):
for node in self.document.traverse(addnodes.translatable):
node.preserve_original_messages()
class Locale(Transform):
"""
Replace translatable nodes with their translated doctree.
@ -384,6 +395,11 @@ class Locale(Transform):
if not msgstr or msgstr == msg: # as-of-yet untranslated
continue
# update translatable nodes
if isinstance(node, addnodes.translatable):
node.apply_translated_message(msg, msgstr)
continue
# Avoid "Literal block expected; none found." warnings.
# If msgstr ends with '::' then it cause warning message at
# parser.parse() processing.

View File

@ -86,6 +86,9 @@ IGNORED_NODES = (
def is_translatable(node):
if isinstance(node, addnodes.translatable):
return True
if isinstance(node, nodes.TextElement):
if not node.source:
return False # built-in message
@ -119,6 +122,10 @@ IMAGE_TYPE_NODES = (
def extract_messages(doctree):
"""Extract translatable messages from a document tree."""
for node in doctree.traverse(is_translatable):
if isinstance(node, addnodes.translatable):
for msg in node.extract_original_messages():
yield node, msg
continue
if isinstance(node, LITERAL_TYPE_NODES):
msg = node.rawsource
if not msg:

View File

@ -0,0 +1,20 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2010, Georg Brandl & Team
# This file is distributed under the same license as the Sphinx <Tests> package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: Sphinx <Tests> 0.6\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-12-16 14:11+0000\n"
"PO-Revision-Date: 2012-12-18 06:14+0900\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 "Table of Contents"
msgstr "TABLE OF CONTENTS"

View File

@ -4,6 +4,7 @@ CONTENTS
.. toctree::
:maxdepth: 2
:numbered:
:caption: Table of Contents
subdir/contents
bom

View File

@ -118,6 +118,11 @@ def assert_count(expected_expr, result, count):
def test_text_builder(app, status, warning):
app.builder.build_all()
# --- toctree
result = (app.outdir / 'contents.txt').text(encoding='utf-8')
yield assert_startswith, result, u"CONTENTS\n********\n\nTABLE OF CONTENTS\n"
# --- warnings in translation
warnings = getwarning(warning)
@ -318,6 +323,12 @@ def test_text_builder(app, status, warning):
def test_gettext_builder(app, status, warning):
app.builder.build_all()
# --- toctree
expect = read_po(app.srcdir / 'contents.po')
actual = read_po(app.outdir / 'contents.pot')
for expect_msg in [m for m in expect if m.id]:
yield assert_in, expect_msg.id, [m.id for m in actual if m.id]
# --- definition terms: regression test for #2198, #2205
expect = read_po(app.srcdir / 'definition_terms.po')
actual = read_po(app.outdir / 'definition_terms.pot')