Skip to content

Commit 7418ec5

Browse files
vegardJonathan Corbet
authored andcommitted
docs: translations: add translations links when they exist
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
1 parent dcd39fa commit 7418ec5

File tree

4 files changed

+170
-1
lines changed

4 files changed

+170
-1
lines changed

Documentation/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def have_command(cmd):
5555
extensions = ['kerneldoc', 'rstFlatTable', 'kernel_include',
5656
'kfigure', 'sphinx.ext.ifconfig', 'automarkup',
5757
'maintainers_include', 'sphinx.ext.autosectionlabel',
58-
'kernel_abi', 'kernel_feat']
58+
'kernel_abi', 'kernel_feat', 'translations']
5959

6060
if major >= 3:
6161
if (major > 3) or (minor > 0 or patch >= 2):

Documentation/sphinx-static/custom.css

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,56 @@ input.kernel-toc-toggle { display: none; }
8383
h3.kernel-toc-contents { display: inline; }
8484
div.kerneltoc a { color: black; }
8585
}
86+
87+
/* Language selection menu */
88+
89+
div.admonition {
90+
/*
91+
* Make sure we don't overlap notes and warnings at the top of the
92+
* document.
93+
*/
94+
clear: both;
95+
}
96+
97+
div.language-selection {
98+
background: #eeeeee;
99+
border: 1px solid #cccccc;
100+
margin-bottom: 1em;
101+
padding: .5em;
102+
103+
position: relative;
104+
float: right;
105+
}
106+
107+
div.language-selection a {
108+
display: block;
109+
padding: 0.5em;
110+
color: #333333;
111+
text-decoration: none;
112+
}
113+
114+
div.language-selection ul {
115+
display: none;
116+
position: absolute;
117+
118+
/* Align with the parent div */
119+
top: 100%;
120+
right: 0;
121+
margin: 0;
122+
123+
list-style: none;
124+
125+
background: #fafafa;
126+
border: 1px solid #cccccc;
127+
128+
/* Never break menu item lines */
129+
white-space: nowrap;
130+
}
131+
132+
div.language-selection:hover ul {
133+
display: block;
134+
}
135+
136+
div.language-selection ul li:hover {
137+
background: #dddddd;
138+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!-- SPDX-License-Identifier: GPL-2.0 -->
2+
<!-- Copyright © 2023, Oracle and/or its affiliates. -->
3+
4+
{# Create a language menu for translations #}
5+
{% if languages|length > 0: %}
6+
<div class="language-selection">
7+
{{ current_language }}
8+
9+
<ul>
10+
{% for ref in languages: %}
11+
<li><a href="{{ ref.refuri }}">{{ ref.astext() }}</a></li>
12+
{% endfor %}
13+
</ul>
14+
</div>
15+
{% endif %}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
#
3+
# Copyright © 2023, Oracle and/or its affiliates.
4+
# Author: Vegard Nossum <vegard.nossum@oracle.com>
5+
#
6+
# Add translation links to the top of the document.
7+
#
8+
9+
import os
10+
11+
from docutils import nodes
12+
from docutils.transforms import Transform
13+
14+
import sphinx
15+
from sphinx import addnodes
16+
from sphinx.errors import NoUri
17+
18+
all_languages = {
19+
# English is always first
20+
None: 'English',
21+
22+
# Keep the rest sorted alphabetically
23+
'zh_CN': 'Chinese (Simplified)',
24+
'zh_TW': 'Chinese (Traditional)',
25+
'it_IT': 'Italian',
26+
'ja_JP': 'Japanese',
27+
'ko_KR': 'Korean',
28+
'sp_SP': 'Spanish',
29+
}
30+
31+
class LanguagesNode(nodes.Element):
32+
def __init__(self, current_language, *args, **kwargs):
33+
super().__init__(*args, **kwargs)
34+
35+
self.current_language = current_language
36+
37+
class TranslationsTransform(Transform):
38+
default_priority = 900
39+
40+
def apply(self):
41+
app = self.document.settings.env.app
42+
docname = self.document.settings.env.docname
43+
44+
this_lang_code = None
45+
components = docname.split(os.sep)
46+
if components[0] == 'translations' and len(components) > 2:
47+
this_lang_code = components[1]
48+
49+
# normalize docname to be the untranslated one
50+
docname = os.path.join(*components[2:])
51+
52+
new_nodes = LanguagesNode(all_languages[this_lang_code])
53+
54+
for lang_code, lang_name in all_languages.items():
55+
if lang_code == this_lang_code:
56+
continue
57+
58+
if lang_code is None:
59+
target_name = docname
60+
else:
61+
target_name = os.path.join('translations', lang_code, docname)
62+
63+
pxref = addnodes.pending_xref('', refdomain='std',
64+
reftype='doc', reftarget='/' + target_name, modname=None,
65+
classname=None, refexplicit=True)
66+
pxref += nodes.Text(lang_name)
67+
new_nodes += pxref
68+
69+
self.document.insert(0, new_nodes)
70+
71+
def process_languages(app, doctree, docname):
72+
for node in doctree.traverse(LanguagesNode):
73+
if app.builder.format not in ['html']:
74+
node.parent.remove(node)
75+
continue
76+
77+
languages = []
78+
79+
# Iterate over the child nodes; any resolved links will have
80+
# the type 'nodes.reference', while unresolved links will be
81+
# type 'nodes.Text'.
82+
languages = list(filter(lambda xref:
83+
isinstance(xref, nodes.reference), node.children))
84+
85+
html_content = app.builder.templates.render('translations.html',
86+
context={
87+
'current_language': node.current_language,
88+
'languages': languages,
89+
})
90+
91+
node.replace_self(nodes.raw('', html_content, format='html'))
92+
93+
def setup(app):
94+
app.add_node(LanguagesNode)
95+
app.add_transform(TranslationsTransform)
96+
app.connect('doctree-resolved', process_languages)
97+
98+
return {
99+
'parallel_read_safe': True,
100+
'parallel_write_safe': True,
101+
}

0 commit comments

Comments
 (0)