mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
refs #1235: i18n: 'literal-block' node can be translated if 'literal-block' is set to gettext_additional_targets
.
This commit is contained in:
parent
d5dca56ff8
commit
c53fa4b369
2
CHANGES
2
CHANGES
@ -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
|
||||
----------
|
||||
|
@ -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 ``[]``.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
"}"
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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">'result'</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 <stdlib.h></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 <stdio.h></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">'RESULT'</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 <STDLIB.H></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 <STDIO.H></span>"""
|
||||
yield assert_equal, len(re.findall(expected_expr, result)), 1, (expected_expr, result)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user