Skip to content

Commit

Permalink
docs: translations: add translations links when they exist
Browse files Browse the repository at this point in the history
Add a new Sphinx extension that knows about the translations of kernel
documentation and can insert links to the translations at the top of
the document.

It basically works like this:

1. Register a new node type, LanguagesNode.

2. Register a new transform, TranslationsTransform, that inserts a new
   LanguageNode at the top of every document. The LanguageNode contains
   "pending references" to translations of the document. The key here
   is that these are pending (i.e. unresolved) references that may or
   may not actually exist.

3. Register a 'doctree-resolved' event that iterates over all the
   LanguageNode nodes. Any unresolved references are filtered out; the
   list of resolved references is passed to the 'translations.html'
   template and rendered as an HTML node (if HTML output is selected).

Testing: make htmldocs, make latexdocs with Sphinx v4.3.2 and Firefox.

v2:
- changed bar into a drop-down menu
- fixed language labels
- fixed hysteresis reported by Akira Yokosawa

Cc: Federico Vaga <federico.vaga@vaga.pv.it>
Cc: Jani Nikula <jani.nikula@linux.intel.com>
Cc: Akira Yokosawa <akiyks@gmail.com>
Cc: Yanteng Si <siyanteng@loongson.cn>
Signed-off-by: Vegard Nossum <vegard.nossum@oracle.com>
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
Link: https://lore.kernel.org/r/20231215123701.2712807-1-vegard.nossum@oracle.com
  • Loading branch information
Vegard Nossum authored and Jonathan Corbet committed Dec 19, 2023
1 parent dcd39fa commit 7418ec5
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Documentation/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def have_command(cmd):
extensions = ['kerneldoc', 'rstFlatTable', 'kernel_include',
'kfigure', 'sphinx.ext.ifconfig', 'automarkup',
'maintainers_include', 'sphinx.ext.autosectionlabel',
'kernel_abi', 'kernel_feat']
'kernel_abi', 'kernel_feat', 'translations']

if major >= 3:
if (major > 3) or (minor > 0 or patch >= 2):
Expand Down
53 changes: 53 additions & 0 deletions Documentation/sphinx-static/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,56 @@ input.kernel-toc-toggle { display: none; }
h3.kernel-toc-contents { display: inline; }
div.kerneltoc a { color: black; }
}

/* Language selection menu */

div.admonition {
/*
* Make sure we don't overlap notes and warnings at the top of the
* document.
*/
clear: both;
}

div.language-selection {
background: #eeeeee;
border: 1px solid #cccccc;
margin-bottom: 1em;
padding: .5em;

position: relative;
float: right;
}

div.language-selection a {
display: block;
padding: 0.5em;
color: #333333;
text-decoration: none;
}

div.language-selection ul {
display: none;
position: absolute;

/* Align with the parent div */
top: 100%;
right: 0;
margin: 0;

list-style: none;

background: #fafafa;
border: 1px solid #cccccc;

/* Never break menu item lines */
white-space: nowrap;
}

div.language-selection:hover ul {
display: block;
}

div.language-selection ul li:hover {
background: #dddddd;
}
15 changes: 15 additions & 0 deletions Documentation/sphinx/templates/translations.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!-- SPDX-License-Identifier: GPL-2.0 -->
<!-- Copyright © 2023, Oracle and/or its affiliates. -->

{# Create a language menu for translations #}
{% if languages|length > 0: %}
<div class="language-selection">
{{ current_language }}

<ul>
{% for ref in languages: %}
<li><a href="{{ ref.refuri }}">{{ ref.astext() }}</a></li>
{% endfor %}
</ul>
</div>
{% endif %}
101 changes: 101 additions & 0 deletions Documentation/sphinx/translations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# SPDX-License-Identifier: GPL-2.0
#
# Copyright © 2023, Oracle and/or its affiliates.
# Author: Vegard Nossum <vegard.nossum@oracle.com>
#
# Add translation links to the top of the document.
#

import os

from docutils import nodes
from docutils.transforms import Transform

import sphinx
from sphinx import addnodes
from sphinx.errors import NoUri

all_languages = {
# English is always first
None: 'English',

# Keep the rest sorted alphabetically
'zh_CN': 'Chinese (Simplified)',
'zh_TW': 'Chinese (Traditional)',
'it_IT': 'Italian',
'ja_JP': 'Japanese',
'ko_KR': 'Korean',
'sp_SP': 'Spanish',
}

class LanguagesNode(nodes.Element):
def __init__(self, current_language, *args, **kwargs):
super().__init__(*args, **kwargs)

self.current_language = current_language

class TranslationsTransform(Transform):
default_priority = 900

def apply(self):
app = self.document.settings.env.app
docname = self.document.settings.env.docname

this_lang_code = None
components = docname.split(os.sep)
if components[0] == 'translations' and len(components) > 2:
this_lang_code = components[1]

# normalize docname to be the untranslated one
docname = os.path.join(*components[2:])

new_nodes = LanguagesNode(all_languages[this_lang_code])

for lang_code, lang_name in all_languages.items():
if lang_code == this_lang_code:
continue

if lang_code is None:
target_name = docname
else:
target_name = os.path.join('translations', lang_code, docname)

pxref = addnodes.pending_xref('', refdomain='std',
reftype='doc', reftarget='/' + target_name, modname=None,
classname=None, refexplicit=True)
pxref += nodes.Text(lang_name)
new_nodes += pxref

self.document.insert(0, new_nodes)

def process_languages(app, doctree, docname):
for node in doctree.traverse(LanguagesNode):
if app.builder.format not in ['html']:
node.parent.remove(node)
continue

languages = []

# Iterate over the child nodes; any resolved links will have
# the type 'nodes.reference', while unresolved links will be
# type 'nodes.Text'.
languages = list(filter(lambda xref:
isinstance(xref, nodes.reference), node.children))

html_content = app.builder.templates.render('translations.html',
context={
'current_language': node.current_language,
'languages': languages,
})

node.replace_self(nodes.raw('', html_content, format='html'))

def setup(app):
app.add_node(LanguagesNode)
app.add_transform(TranslationsTransform)
app.connect('doctree-resolved', process_languages)

return {
'parallel_read_safe': True,
'parallel_write_safe': True,
}

0 comments on commit 7418ec5

Please sign in to comment.