Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- 🐛 FIX: Table cells inside a MyST ``tab-item`` are now extracted for gettext
and translated, rather than being omitted from the ``.pot`` because they
lacked ``source`` after nested parse ({issue}`234`)
- ♻️ IMPROVE: Replace the Sass/Node build with a dependency-free Python CSS
generator (`tools/generate_css.py` driven by `style/design.toml` and
hand-authored `style/*.css`); `package.json` is gone. The compiled
Expand Down
22 changes: 22 additions & 0 deletions sphinx_design/tabs.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def run_with_defaults(self) -> list[nodes.Node]:
classes=["sd-tab-content", *self.options.get("class-content", [])],
)
self.state.nested_parse(self.content, self.content_offset, tab_content)
_propagate_table_source(tab_content)
tab_item += tab_content

return [tab_item]
Expand Down Expand Up @@ -197,6 +198,27 @@ def run_with_defaults(self) -> list[nodes.Node]:
return [tab_set]


def _propagate_table_source(root: nodes.Node) -> None:
"""Copy source/line from a table onto descendants that lack them.

MyST's table renderer sets source on the ``table`` node but not on cell
paragraphs. At document level those cells inherit
``document.current_source``; during ``nested_parse`` that is ``None``,
so Sphinx's gettext extractor skips them (``is_translatable`` requires
``node.source``). See https://github.com/executablebooks/sphinx-design/issues/234
"""
for table in root.findall(nodes.table):
source = table.source
if not source:
continue
line = table.line
for child in table.findall(nodes.Element):
if child.source is None:
child.source = source
if child.line is None:
child.line = line


class sd_tab_input(nodes.Element, nodes.General): # noqa: N801
pass

Expand Down
72 changes: 72 additions & 0 deletions tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,78 @@ def test_button_i18n_translated(sphinx_builder):
assert badges[0].astext() == "stable"


TAB_TABLE_MYST = """\
# Heading

::::{tab-set}

:::{tab-item} Label1
Content1

| UniqueHeaderA | UniqueHeaderB |
| --- | --- |
| UniqueCellC | UniqueCellD |
:::

::::
"""


@pytest.mark.skipif(not MYST_INSTALLED, reason="myst-parser not installed")
def test_tab_table_i18n_gettext(sphinx_builder):
"""Table cells inside a MyST tab-item must be extracted for gettext.

See https://github.com/executablebooks/sphinx-design/issues/234
"""
builder = sphinx_builder("gettext")
builder.src_path.joinpath("index.md").write_text(TAB_TABLE_MYST, encoding="utf8")
builder.build()
pot = (builder.out_path / "index.pot").read_text(encoding="utf8")
for msgid in (
"Heading",
"Label1",
"Content1",
"UniqueHeaderA",
"UniqueHeaderB",
"UniqueCellC",
"UniqueCellD",
):
assert f'msgid "{msgid}"' in pot, pot


@pytest.mark.skipif(not MYST_INSTALLED, reason="myst-parser not installed")
def test_tab_table_i18n_translated(sphinx_builder):
"""Translated table cells inside a tab-item must appear in the HTML output.

See https://github.com/executablebooks/sphinx-design/issues/234
"""
builder = sphinx_builder(
conf_kwargs={
"extensions": ["myst_parser", "sphinx_design"],
"myst_enable_extensions": ["colon_fence"],
"language": "de",
"locale_dirs": ["locales"],
}
)
builder.src_path.joinpath("index.md").write_text(TAB_TABLE_MYST, encoding="utf8")
catalog = Catalog(locale="de", domain="index")
catalog.add("UniqueHeaderA", "KopfA")
catalog.add("UniqueHeaderB", "KopfB")
catalog.add("UniqueCellC", "ZelleC")
catalog.add("UniqueCellD", "ZelleD")
mo_dir = builder.src_path / "locales" / "de" / "LC_MESSAGES"
mo_dir.mkdir(parents=True)
with (mo_dir / "index.mo").open("wb") as handle:
write_mo(handle, catalog)

builder.build()
html = (builder.out_path / "index.html").read_text(encoding="utf8")
for translated in ("KopfA", "KopfB", "ZelleC", "ZelleD"):
assert translated in html, html
for original in ("UniqueHeaderA", "UniqueHeaderB", "UniqueCellC", "UniqueCellD"):
assert original not in html, html


INVALID_CONFIG_VALUES = {
"custom_directives": (["not", "a", "dict"], "must be a dictionary"),
"fontawesome_source": ("invalid", "must be one of"),
Expand Down