xref: /linux/Documentation/sphinx/translations.py (revision e7d759f31ca295d589f7420719c311870bb3166f)
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
9import os
10
11from docutils import nodes
12from docutils.transforms import Transform
13
14import sphinx
15from sphinx import addnodes
16from sphinx.errors import NoUri
17
18all_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
31class LanguagesNode(nodes.Element):
32    def __init__(self, current_language, *args, **kwargs):
33        super().__init__(*args, **kwargs)
34
35        self.current_language = current_language
36
37class 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
71def 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
93def 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    }
102