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:
2
CHANGES
2
CHANGES
@@ -23,6 +23,8 @@ Features added
|
|||||||
nav-item-0, 1, 2...).
|
nav-item-0, 1, 2...).
|
||||||
* New option `sphinx-quickstart --use-make-mode` for generating Makefile that
|
* New option `sphinx-quickstart --use-make-mode` for generating Makefile that
|
||||||
use sphinx-build make-mode.
|
use sphinx-build make-mode.
|
||||||
|
* #1235: i18n: 'literal-block' node can be translated if 'literal-block'
|
||||||
|
is set to `gettext_additional_targets`.
|
||||||
|
|
||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
|
|||||||
@@ -494,6 +494,7 @@ documentation on :ref:`intl` for details.
|
|||||||
i18n additionally. You can specify below names:
|
i18n additionally. You can specify below names:
|
||||||
|
|
||||||
:index: index terms
|
:index: index terms
|
||||||
|
:literal-block: literal blocks: ``::`` and ``code-block``.
|
||||||
|
|
||||||
The default is ``[]``.
|
The default is ``[]``.
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ from sphinx.locale import _
|
|||||||
from sphinx.versioning import add_uids, merge_doctrees
|
from sphinx.versioning import add_uids, merge_doctrees
|
||||||
from sphinx.transforms import DefaultSubstitutions, MoveModuleTargets, \
|
from sphinx.transforms import DefaultSubstitutions, MoveModuleTargets, \
|
||||||
HandleCodeBlocks, AutoNumbering, SortIds, CitationReferences, Locale, \
|
HandleCodeBlocks, AutoNumbering, SortIds, CitationReferences, Locale, \
|
||||||
RemoveTranslatableInline, SphinxContentsFilter
|
RemoveTranslatableInline, SphinxContentsFilter, ExtraTranslatableNodes
|
||||||
|
|
||||||
|
|
||||||
orig_role_function = roles.role
|
orig_role_function = roles.role
|
||||||
@@ -98,9 +98,9 @@ class SphinxStandaloneReader(standalone.Reader):
|
|||||||
"""
|
"""
|
||||||
Add our own transforms.
|
Add our own transforms.
|
||||||
"""
|
"""
|
||||||
transforms = [Locale, CitationReferences, DefaultSubstitutions,
|
transforms = [ExtraTranslatableNodes, Locale, CitationReferences,
|
||||||
MoveModuleTargets, HandleCodeBlocks, AutoNumbering, SortIds,
|
DefaultSubstitutions, MoveModuleTargets, HandleCodeBlocks,
|
||||||
RemoveTranslatableInline]
|
AutoNumbering, SortIds, RemoveTranslatableInline]
|
||||||
|
|
||||||
def get_transforms(self):
|
def get_transforms(self):
|
||||||
return standalone.Reader.get_transforms(self) + self.transforms
|
return standalone.Reader.get_transforms(self) + self.transforms
|
||||||
|
|||||||
@@ -20,8 +20,11 @@ from docutils.transforms.parts import ContentsFilter
|
|||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.locale import _, init as init_locale
|
from sphinx.locale import _, init as init_locale
|
||||||
from sphinx.util import split_index_msg
|
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.osutil import ustrftime, find_catalog
|
||||||
|
from sphinx.util.pycompat import indent
|
||||||
from sphinx.domains.std import (
|
from sphinx.domains.std import (
|
||||||
make_term_from_paragraph_node,
|
make_term_from_paragraph_node,
|
||||||
make_termnodes_from_paragraph_node,
|
make_termnodes_from_paragraph_node,
|
||||||
@@ -156,6 +159,28 @@ class CitationReferences(Transform):
|
|||||||
citnode.parent.replace(citnode, refnode)
|
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):
|
class CustomLocaleReporter(object):
|
||||||
"""
|
"""
|
||||||
Replacer for document.reporter.get_source_and_line method.
|
Replacer for document.reporter.get_source_and_line method.
|
||||||
@@ -177,7 +202,7 @@ class Locale(Transform):
|
|||||||
"""
|
"""
|
||||||
Replace translatable nodes with their translated doctree.
|
Replace translatable nodes with their translated doctree.
|
||||||
"""
|
"""
|
||||||
default_priority = 0
|
default_priority = 20
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
env = self.document.settings.env
|
env = self.document.settings.env
|
||||||
@@ -331,6 +356,11 @@ class Locale(Transform):
|
|||||||
msgstr += '\n\n dummy literal'
|
msgstr += '\n\n dummy literal'
|
||||||
# dummy literal node will discard by 'patch = patch[0]'
|
# 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)
|
patch = new_document(source, settings)
|
||||||
CustomLocaleReporter(node.source, node.line).set_reporter(patch)
|
CustomLocaleReporter(node.source, node.line).set_reporter(patch)
|
||||||
parser.parse(msgstr, patch)
|
parser.parse(msgstr, patch)
|
||||||
@@ -339,7 +369,7 @@ class Locale(Transform):
|
|||||||
except IndexError: # empty node
|
except IndexError: # empty node
|
||||||
pass
|
pass
|
||||||
# XXX doctest and other block markup
|
# 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
|
continue # skip for now
|
||||||
|
|
||||||
# auto-numbered foot note reference should use original 'ids'.
|
# auto-numbered foot note reference should use original 'ids'.
|
||||||
@@ -466,6 +496,11 @@ class Locale(Transform):
|
|||||||
for child in patch.children:
|
for child in patch.children:
|
||||||
child.parent = node
|
child.parent = node
|
||||||
node.children = patch.children
|
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
|
node['translated'] = True
|
||||||
|
|
||||||
if 'index' in env.config.gettext_additional_targets:
|
if 'index' in env.config.gettext_additional_targets:
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ IGNORED_NODES = (
|
|||||||
nodes.doctest_block,
|
nodes.doctest_block,
|
||||||
#XXX there are probably more
|
#XXX there are probably more
|
||||||
)
|
)
|
||||||
|
LITERAL_TYPE_NODES = (
|
||||||
|
nodes.literal_block,
|
||||||
|
)
|
||||||
def extract_messages(doctree):
|
def extract_messages(doctree):
|
||||||
"""Extract translatable messages from a document tree."""
|
"""Extract translatable messages from a document tree."""
|
||||||
for node in doctree.traverse(nodes.TextElement):
|
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':
|
if isinstance(node, nodes.field_name) and node.children[0] == 'orphan':
|
||||||
continue
|
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
|
# XXX nodes rendering empty are likely a bug in sphinx.addnodes
|
||||||
if msg:
|
if msg:
|
||||||
yield node, msg
|
yield node, msg
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ if PY3:
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.__unicode__()
|
return self.__unicode__()
|
||||||
|
|
||||||
|
from textwrap import indent
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Python 2
|
# Python 2
|
||||||
u = 'u'
|
u = 'u'
|
||||||
@@ -75,6 +77,17 @@ else:
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.__unicode__().encode('utf8')
|
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):
|
def execfile_(filepath, _globals):
|
||||||
from sphinx.util.osutil import fs_encoding
|
from sphinx.util.osutil import fs_encoding
|
||||||
|
|||||||
@@ -28,3 +28,40 @@ msgstr "MISSING LITERAL BLOCK::"
|
|||||||
msgid "That's all."
|
msgid "That's all."
|
||||||
msgstr "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::
|
Missing literal block::
|
||||||
|
|
||||||
That's all.
|
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'
|
root = tempdir / 'test-intl'
|
||||||
|
|
||||||
|
|
||||||
def gen_with_intl_app(*args, **kw):
|
def gen_with_intl_app(builder, confoverrides={}, *args, **kw):
|
||||||
default_kw = {
|
default_kw = {
|
||||||
'testroot': 'intl',
|
'testroot': 'intl',
|
||||||
'confoverrides': {
|
'confoverrides': {
|
||||||
@@ -36,7 +36,8 @@ def gen_with_intl_app(*args, **kw):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
default_kw.update(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():
|
def setup_module():
|
||||||
@@ -621,3 +622,56 @@ def test_xml_builder(app, status, warning):
|
|||||||
['label-bridged-target-section',
|
['label-bridged-target-section',
|
||||||
'section-and-label',
|
'section-and-label',
|
||||||
'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)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user