mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Fix #1734: Could not translate the caption of toctree directive
This commit is contained in:
parent
c6e102999f
commit
9b00c31ee2
1
CHANGES
1
CHANGES
@ -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
|
||||
-------------
|
||||
|
@ -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.)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
13
sphinx/io.py
13
sphinx/io.py
@ -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):
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
20
tests/roots/test-intl/contents.po
Normal file
20
tests/roots/test-intl/contents.po
Normal 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"
|
@ -4,6 +4,7 @@ CONTENTS
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:numbered:
|
||||
:caption: Table of Contents
|
||||
|
||||
subdir/contents
|
||||
bom
|
||||
|
@ -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')
|
||||
|
Loading…
Reference in New Issue
Block a user