Skip to content

Commit

Permalink
Merge branch 'new-inline-parse-i18n-logic' into club1
Browse files Browse the repository at this point in the history
  • Loading branch information
n-peugnet committed Apr 30, 2024
2 parents 81c0182 + 17455d6 commit 437dc73
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 71 deletions.
6 changes: 6 additions & 0 deletions sphinx/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ def setup(self, app: Sphinx) -> None:
if transform in self.transforms:
self.transforms.remove(transform)

def parse(self) -> None:
"""Override the BaseReader parse method to call self.parser.parse_inline()."""
self.document = document = self.new_document()
self.parser.parse_inline(self.input, document)
document.current_source = document.current_line = None


class SphinxDummyWriter(UnfilteredWriter):
"""Dummy writer module used for generating doctree."""
Expand Down
24 changes: 24 additions & 0 deletions sphinx/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ def set_application(self, app: Sphinx) -> None:
self.config = app.config
self.env = app.env

def parse_inline(self, inputstring: str, document: nodes.document) -> None:
"""Parse the inline elements of a text block and generate a document tree."""
msg = 'Parser subclasses must implement parse_inline'
raise NotImplementedError(msg)


class RSTParser(docutils.parsers.rst.Parser, Parser):
"""A reST parser for Sphinx."""
Expand All @@ -60,6 +65,25 @@ def get_transforms(self) -> list[type[Transform]]:
transforms.remove(SmartQuotes)
return transforms

def parse_inline(self, inputstring: str, document: nodes.document) -> None:
"""Parse inline syntax from text and generate a document tree."""
# Avoid "Literal block expected; none found." warnings.
if inputstring.endswith('::'):
inputstring = inputstring[:-1]

self.setup_parse(inputstring, document)
self.statemachine = states.RSTStateMachine(
state_classes=self.state_classes,
initial_state='Text',
debug=document.reporter.debug_flag,
)

inputlines = StringList([inputstring], document.current_source)

self.decorate(inputlines)
self.statemachine.run(inputlines, document, inliner=self.inliner)
self.finish_parse()

def parse(self, inputstring: str | StringList, document: nodes.document) -> None:
"""Parse text and generate a document tree."""
self.setup_parse(inputstring, document) # type: ignore[arg-type]
Expand Down
74 changes: 15 additions & 59 deletions sphinx/transforms/i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import contextlib
from os import path
from re import DOTALL, match
from textwrap import indent
from typing import TYPE_CHECKING, Any, TypeVar

from docutils import nodes
Expand All @@ -21,7 +20,6 @@
from sphinx.util.i18n import docname_to_domain
from sphinx.util.index_entries import split_index_msg
from sphinx.util.nodes import (
IMAGE_TYPE_NODES,
LITERAL_TYPE_NODES,
NodeMatcher,
extract_messages,
Expand Down Expand Up @@ -380,25 +378,12 @@ def apply(self, **kwargs: Any) -> None:
node['translated'] = True
continue

# Avoid "Literal block expected; none found." warnings.
# If msgstr ends with '::' then it cause warning message at
# parser.parse() processing.
# literal-block-warning is only appear in avobe case.
if msgstr.strip().endswith('::'):
msgstr += '\n\n dummy literal'
# dummy literal node will discard by 'patch = patch[0]'

# literalblock need literal block notation to avoid it become
# paragraph.
# literalblock can not contain references or terms
if isinstance(node, LITERAL_TYPE_NODES):
msgstr = '::\n\n' + indent(msgstr, ' ' * 3)
continue

patch = publish_msgstr(self.app, msgstr, source,
node.line, self.config, settings) # type: ignore[arg-type]
# FIXME: no warnings about inconsistent references in this part
# XXX doctest and other block markup
if not isinstance(patch, nodes.paragraph):
continue # skip for now

updater = _NodeUpdater(node, patch, self.document, noqa=False)
processed = updater.update_title_mapping()
Expand Down Expand Up @@ -453,45 +438,25 @@ def apply(self, **kwargs: Any) -> None:
node['alt'] = msgstr
continue

# Avoid "Literal block expected; none found." warnings.
# If msgstr ends with '::' then it cause warning message at
# parser.parse() processing.
# literal-block-warning is only appear in avobe case.
if msgstr.strip().endswith('::'):
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)
if isinstance(node, nodes.image) and node.get('uri') == msg:
node['uri'] = msgstr
continue

# Structural Subelements phase1
# There is a possibility that only the title node is created.
# see: https://docutils.sourceforge.io/docs/ref/doctree.html#structural-subelements
if isinstance(node, nodes.title):
# This generates: <section ...><title>msgstr</title></section>
msgstr = msgstr + '\n' + '=' * len(msgstr) * 2
# literalblock do not need to be parsed as they do not contain inline syntax,
# except for parsed-literals, but they use the same node type, so we differentiate
# them based on their number of children.
if isinstance(node, LITERAL_TYPE_NODES) and len(node.children) <= 1:
node.children = [nodes.Text(msgstr)]
# for highlighting that expects .rawsource and .astext() are same.
node.rawsource = node.astext()
node['translated'] = True
continue

patch = publish_msgstr(self.app, msgstr, source,
node.line, self.config, settings) # type: ignore[arg-type]
# Structural Subelements phase2
if isinstance(node, nodes.title):
# get <title> node that placed as a first child
patch = patch.next_node() # type: ignore[assignment]

# ignore unexpected markups in translation message
unexpected: tuple[type[nodes.Element], ...] = (
nodes.paragraph, # expected form of translation
nodes.title, # generated by above "Subelements phase2"
)

# following types are expected if
# config.gettext_additional_targets is configured
unexpected += LITERAL_TYPE_NODES
unexpected += IMAGE_TYPE_NODES

if not isinstance(patch, unexpected):
if not isinstance(patch, nodes.paragraph):
continue # skip

updater = _NodeUpdater(node, patch, self.document, noqa)
Expand All @@ -502,15 +467,6 @@ def apply(self, **kwargs: Any) -> None:
updater.update_pending_xrefs()
updater.update_leaves()

# for highlighting that expects .rawsource and .astext() are same.
if isinstance(node, LITERAL_TYPE_NODES):
node.rawsource = node.astext()

if isinstance(node, nodes.image) and node.get('alt') != msg:
node['uri'] = patch['uri']
node['translated'] = False
continue # do not mark translated

node['translated'] = True # to avoid double translation

if 'index' in self.config.gettext_additional_targets:
Expand Down
3 changes: 1 addition & 2 deletions sphinx/util/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,7 @@ def extract_messages(doctree: Element) -> Iterable[tuple[Element, str]]:
if node.get('alt'):
yield node, node['alt']
if node.get('translatable'):
image_uri = node.get('original_uri', node['uri'])
msg = f'.. image:: {image_uri}'
msg = node.get('original_uri', node['uri'])
else:
msg = ''
elif isinstance(node, nodes.meta):
Expand Down
8 changes: 4 additions & 4 deletions tests/roots/test-intl/xx/LC_MESSAGES/figure.po
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ msgstr "IMAGE URL AND ALT"
msgid "img"
msgstr "IMG -> I18N"

msgid ".. image:: img.png"
msgstr ".. image:: i18n.png"
msgid "img.png"
msgstr "i18n.png"

msgid "i18n"
msgstr "I18N -> IMG"

msgid ".. image:: i18n.png"
msgstr ".. image:: img.png"
msgid "i18n.png"
msgstr "img.png"

msgid "image on substitution"
msgstr "IMAGE ON SUBSTITUTION"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ msgstr "SUBSTITUTED IMAGE |subst_epilog_2| HERE."
msgid "subst_prolog_2"
msgstr "SUBST_PROLOG_2 TRANSLATED"

msgid ".. image:: /img.png"
msgstr ".. image:: /i18n.png"
msgid "/img.png"
msgstr "/i18n.png"

msgid "subst_epilog_2"
msgstr "SUBST_EPILOG_2 TRANSLATED"

msgid ".. image:: /i18n.png"
msgstr ".. image:: /img.png"
msgid "/i18n.png"
msgstr "/img.png"
4 changes: 2 additions & 2 deletions tests/test_builders/test_build_gettext.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,11 @@ def test_gettext_prolog_epilog_substitution(app):
"This is content that contains |subst_prolog_1|.",
"Substituted image |subst_prolog_2| here.",
"subst_prolog_2",
".. image:: /img.png",
"/img.png",
"This is content that contains |subst_epilog_1|.",
"Substituted image |subst_epilog_2| here.",
"subst_epilog_2",
".. image:: /i18n.png",
"/i18n.png",
]


Expand Down

0 comments on commit 437dc73

Please sign in to comment.