refs #1235: i18n: 'literal-block' node can be translated if 'literal-block' is set to gettext_additional_targets.

This commit is contained in:
shimizukawa 2015-02-22 13:51:02 +09:00
parent d5dca56ff8
commit c53fa4b369
9 changed files with 187 additions and 10 deletions

View File

@ -23,6 +23,8 @@ Features added
nav-item-0, 1, 2...).
* New option `sphinx-quickstart --use-make-mode` for generating Makefile that
use sphinx-build make-mode.
* #1235: i18n: 'literal-block' node can be translated if 'literal-block'
is set to `gettext_additional_targets`.
Bugs fixed
----------

View File

@ -494,6 +494,7 @@ documentation on :ref:`intl` for details.
i18n additionally. You can specify below names:
:index: index terms
:literal-block: literal blocks: ``::`` and ``code-block``.
The default is ``[]``.

View File

@ -50,7 +50,7 @@ from sphinx.locale import _
from sphinx.versioning import add_uids, merge_doctrees
from sphinx.transforms import DefaultSubstitutions, MoveModuleTargets, \
HandleCodeBlocks, AutoNumbering, SortIds, CitationReferences, Locale, \
RemoveTranslatableInline, SphinxContentsFilter
RemoveTranslatableInline, SphinxContentsFilter, ExtraTranslatableNodes
orig_role_function = roles.role
@ -98,9 +98,9 @@ class SphinxStandaloneReader(standalone.Reader):
"""
Add our own transforms.
"""
transforms = [Locale, CitationReferences, DefaultSubstitutions,
MoveModuleTargets, HandleCodeBlocks, AutoNumbering, SortIds,
RemoveTranslatableInline]
transforms = [ExtraTranslatableNodes, Locale, CitationReferences,
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks,
AutoNumbering, SortIds, RemoveTranslatableInline]
def get_transforms(self):
return standalone.Reader.get_transforms(self) + self.transforms

View File

@ -20,8 +20,11 @@ from docutils.transforms.parts import ContentsFilter
from sphinx import addnodes
from sphinx.locale import _, init as init_locale
from sphinx.util import split_index_msg
from sphinx.util.nodes import traverse_translatable_index, extract_messages
from sphinx.util.nodes import (
traverse_translatable_index, extract_messages, LITERAL_TYPE_NODES,
)
from sphinx.util.osutil import ustrftime, find_catalog
from sphinx.util.pycompat import indent
from sphinx.domains.std import (
make_term_from_paragraph_node,
make_termnodes_from_paragraph_node,
@ -156,6 +159,28 @@ class CitationReferences(Transform):
citnode.parent.replace(citnode, refnode)
TRANSLATABLE_NODES = {
'literal-block': nodes.literal_block,
}
class ExtraTranslatableNodes(Transform):
"""
make nodes translatable
"""
default_priority = 10
def apply(self):
targets = self.document.settings.env.config.gettext_additional_targets
target_nodes = [v for k, v in TRANSLATABLE_NODES.items() if k in targets]
if not target_nodes:
return
def is_translatable_node(node):
return isinstance(node, tuple(target_nodes))
for node in self.document.traverse(is_translatable_node):
node['translatable'] = True
class CustomLocaleReporter(object):
"""
Replacer for document.reporter.get_source_and_line method.
@ -177,7 +202,7 @@ class Locale(Transform):
"""
Replace translatable nodes with their translated doctree.
"""
default_priority = 0
default_priority = 20
def apply(self):
env = self.document.settings.env
@ -331,6 +356,11 @@ class Locale(Transform):
msgstr += '\n\n dummy literal'
# dummy literal node will discard by 'patch = patch[0]'
# literalblock need literal block notation to avoid it become
# paragraph.
if isinstance(node, LITERAL_TYPE_NODES):
msgstr = '::\n\n' + indent(msgstr, ' '*3)
patch = new_document(source, settings)
CustomLocaleReporter(node.source, node.line).set_reporter(patch)
parser.parse(msgstr, patch)
@ -339,7 +369,7 @@ class Locale(Transform):
except IndexError: # empty node
pass
# XXX doctest and other block markup
if not isinstance(patch, nodes.paragraph):
if not isinstance(patch, (nodes.paragraph,) + LITERAL_TYPE_NODES):
continue # skip for now
# auto-numbered foot note reference should use original 'ids'.
@ -466,6 +496,11 @@ class Locale(Transform):
for child in patch.children:
child.parent = node
node.children = patch.children
# for highlighting that expects .rawsource and .astext() are same.
if isinstance(node, LITERAL_TYPE_NODES):
node.rawsource = node.astext()
node['translated'] = True
if 'index' in env.config.gettext_additional_targets:

View File

@ -73,6 +73,9 @@ IGNORED_NODES = (
nodes.doctest_block,
#XXX there are probably more
)
LITERAL_TYPE_NODES = (
nodes.literal_block,
)
def extract_messages(doctree):
"""Extract translatable messages from a document tree."""
for node in doctree.traverse(nodes.TextElement):
@ -87,7 +90,11 @@ def extract_messages(doctree):
if isinstance(node, nodes.field_name) and node.children[0] == 'orphan':
continue
msg = node.rawsource.replace('\n', ' ').strip()
if isinstance(node, LITERAL_TYPE_NODES):
msg = node.rawsource
else:
msg = node.rawsource.replace('\n', ' ').strip()
# XXX nodes rendering empty are likely a bug in sphinx.addnodes
if msg:
yield node, msg

View File

@ -52,6 +52,8 @@ if PY3:
def __str__(self):
return self.__unicode__()
from textwrap import indent
else:
# Python 2
u = 'u'
@ -75,6 +77,17 @@ else:
def __str__(self):
return self.__unicode__().encode('utf8')
# backport from python3
def indent(text, prefix, predicate=None):
if predicate is None:
def predicate(line):
return line.strip()
def prefixed_lines():
for line in text.splitlines(True):
yield (prefix + line if predicate(line) else line)
return ''.join(prefixed_lines())
def execfile_(filepath, _globals):
from sphinx.util.osutil import fs_encoding

View File

@ -28,3 +28,40 @@ msgstr "MISSING LITERAL BLOCK::"
msgid "That's all."
msgstr "THAT'S ALL."
msgid "code blocks"
msgstr "CODE-BLOCKS"
msgid ""
"def main\n"
" 'result'\n"
"end"
msgstr ""
"def main\n"
" 'RESULT'\n"
"end"
msgid ""
"#include <stdlib.h>\n"
"int main(int argc, char** argv)\n"
"{\n"
" return 0;\n"
"}"
msgstr ""
"#include <STDLIB.H>\n"
"int main(int ARGC, char** ARGV)\n"
"{\n"
" return 0;\n"
"}"
msgid ""
"#include <stdio.h>\n"
"int main(int argc, char** argv)\n"
"{\n"
" return 0;\n"
"}"
msgstr ""
"#include <STDIO.H>\n"
"int main(int ARGC, char** ARGV)\n"
"{\n"
" return 0;\n"
"}"

View File

@ -11,3 +11,31 @@ Correct literal block::
Missing literal block::
That's all.
code blocks
==============
.. highlight:: ruby
::
def main
'result'
end
::
#include <stdlib.h>
int main(int argc, char** argv)
{
return 0;
}
.. code-block:: c
#include <stdio.h>
int main(int argc, char** argv)
{
return 0;
}

View File

@ -27,7 +27,7 @@ from util import tempdir, rootdir, path, gen_with_app, SkipTest, \
root = tempdir / 'test-intl'
def gen_with_intl_app(*args, **kw):
def gen_with_intl_app(builder, confoverrides={}, *args, **kw):
default_kw = {
'testroot': 'intl',
'confoverrides': {
@ -36,7 +36,8 @@ def gen_with_intl_app(*args, **kw):
},
}
default_kw.update(kw)
return gen_with_app(*args, **default_kw)
default_kw['confoverrides'].update(confoverrides)
return gen_with_app(builder, *args, **default_kw)
def setup_module():
@ -621,3 +622,56 @@ def test_xml_builder(app, status, warning):
['label-bridged-target-section',
'section-and-label',
'section-and-label'])
@gen_with_intl_app('html', freshenv=True)
def test_additional_targets_should_not_be_translated(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'literalblock.html').text(encoding='utf-8')
# title should be translated
expected_expr = 'CODE-BLOCKS'
yield assert_equal, len(re.findall(expected_expr, result)), 2, (expected_expr, result)
# ruby code block should not be translated but be highlighted
expected_expr = """<span class="s1">&#39;result&#39;</span>"""
yield assert_equal, len(re.findall(expected_expr, result)), 1, (expected_expr, result)
# C code block without lang should not be translated and *ruby* highlighted
expected_expr = """<span class="c1">#include &lt;stdlib.h&gt;</span>"""
yield assert_equal, len(re.findall(expected_expr, result)), 1, (expected_expr, result)
# C code block with lang should not be translated but be *C* highlighted
expected_expr = """<span class="cp">#include &lt;stdio.h&gt;</span>"""
yield assert_equal, len(re.findall(expected_expr, result)), 1, (expected_expr, result)
@gen_with_intl_app('html', freshenv=True,
confoverrides={
'gettext_additional_targets': [
'index',
'literal-block',
],
})
def test_additional_targets_should_be_translated(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'literalblock.html').text(encoding='utf-8')
# title should be translated
expected_expr = 'CODE-BLOCKS'
yield assert_equal, len(re.findall(expected_expr, result)), 2, (expected_expr, result)
# ruby code block should be translated and be highlighted
expected_expr = """<span class="s1">&#39;RESULT&#39;</span>"""
yield assert_equal, len(re.findall(expected_expr, result)), 1, (expected_expr, result)
# C code block without lang should be translated and *ruby* highlighted
expected_expr = """<span class="c1">#include &lt;STDLIB.H&gt;</span>"""
yield assert_equal, len(re.findall(expected_expr, result)), 1, (expected_expr, result)
# C code block with lang should be translated and be *C* highlighted
expected_expr = """<span class="cp">#include &lt;STDIO.H&gt;</span>"""
yield assert_equal, len(re.findall(expected_expr, result)), 1, (expected_expr, result)