Message ID | 20231028162931.261843-1-vegard.nossum@oracle.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:d641:0:b0:403:3b70:6f57 with SMTP id cy1csp1303458vqb; Sat, 28 Oct 2023 09:30:21 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFHv0Np243iL77+OVXIC0usA5UG+Z0zvGtxrHlr2Pcy4J/5YxAstRBW69rt9fY/BrzeFpr8 X-Received: by 2002:a17:903:27d0:b0:1c7:443d:7412 with SMTP id km16-20020a17090327d000b001c7443d7412mr4660281plb.26.1698510621371; Sat, 28 Oct 2023 09:30:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1698510621; cv=none; d=google.com; s=arc-20160816; b=uejY4/ky4TxR5AszBIlNSU4wifwGo7yr9iu7tQEIEH92BnaPOf3Oe/B+T1BZvLI+d7 e6N35+g1QoENxXazF1IYnDfQznaY63GaF1yA2YTrfeyLC8UEsERVb5DHQE9+afXFx4SY dL3izeZpp6ZLLB8iPbU6uI3gBiptGOE9whMkDX2dfPx7ecb8nhzGCvc6v5K40/Ih3OhM ePPdfZRsODIIC6WqXq2iUQZ7HRbn/dbgQ96NVINQZ4w1DtEG7Wwfk/y3vMQ0RdD527NE uJD4wgLuQFr5gmzqr6Gm1Qr/yAVipAoC51qt9x3U9298alPmYS46wGL/CtPpDRVHZW+F b7Bg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=b32Ho9TlxRqA+lGs0AbT5dgfNMW2JGlPfC8cer5+j5o=; fh=c0OImX6OKuxrBEpRTv2Vnf1krZjTNxKyGx+FMTFukK0=; b=nTCprKa1KB6GZpVfYh/GB/M28dX2nY4X7JEw0luKfK7XPDFITR/4y8Tnks7Qniur5S ClmEXf6EEfBHj3pMsQXU1qxaXii8XNouYN/TozSwNeUcvsOBoj7iCESVNU21NdrFq4IF GVICo5Z8QLMePCM3zmoOYvI/ADJfun7reDZbyJc54EIkpVLiajTjRatfef1s8dy2Klsd nDc4PDXgV/kpkV4CfXO2tVVjVkx4XPQZzxfzKVaOlci+zUu4bN43GAHkWmK0h9iI2kmw ygGdY48lfUgpVycK0ogXAtiP0+5WJCVWKk0XrN+iS7epsDIowBxb+1IS+65uEqpsvp0d ZlDQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@oracle.com header.s=corp-2023-03-30 header.b=tC7EwQC1; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:7 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=oracle.com Received: from snail.vger.email (snail.vger.email. [2620:137:e000::3:7]) by mx.google.com with ESMTPS id l15-20020a170902f68f00b001c9ad8ca211si2751312plg.18.2023.10.28.09.30.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Oct 2023 09:30:21 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:7 as permitted sender) client-ip=2620:137:e000::3:7; Authentication-Results: mx.google.com; dkim=pass header.i=@oracle.com header.s=corp-2023-03-30 header.b=tC7EwQC1; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:7 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=oracle.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by snail.vger.email (Postfix) with ESMTP id 7234D804C534; Sat, 28 Oct 2023 09:30:20 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at snail.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229546AbjJ1QaP (ORCPT <rfc822;peter110.wang@gmail.com> + 28 others); Sat, 28 Oct 2023 12:30:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53118 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229565AbjJ1QaO (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Sat, 28 Oct 2023 12:30:14 -0400 Received: from mx0b-00069f02.pphosted.com (mx0b-00069f02.pphosted.com [205.220.177.32]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2C5F1EB; Sat, 28 Oct 2023 09:30:11 -0700 (PDT) Received: from pps.filterd (m0246630.ppops.net [127.0.0.1]) by mx0b-00069f02.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 39S5GTDH027570; Sat, 28 Oct 2023 16:29:41 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : mime-version : content-type : content-transfer-encoding; s=corp-2023-03-30; bh=b32Ho9TlxRqA+lGs0AbT5dgfNMW2JGlPfC8cer5+j5o=; b=tC7EwQC1r5Up9d3wZkfhsBAdj/eOXHGQOEjbg8GWZyWowKU6LndhJ3Ha3zIEa098BbAP NkqFBKepTmAnCF5sSAw94xX7PoksKL5rYT4Kl70M0HQmi9JUZZztHaDCcE+4KGTVelg9 ZqlT8btXrSMb/dalrrG5wYIrEPrj+A8mOcXVVCDbLCN6v07gZvo58JIFDD0cEQhXUHxA vkO/8XlRgVhsPEo/a0w8DzCwaO+iWI/OWODscK4hMUtsPAOB+UUzJ4VpPeHCTlJv40Rq oJ1UvnVa/Y//9TPpC7ghcB2dk17oIUZEXi70f7CqJ09o3BIqN+R5CyEAbNsmf6z49irt Xg== Received: from iadpaimrmta03.imrmtpd1.prodappiadaev1.oraclevcn.com (iadpaimrmta03.appoci.oracle.com [130.35.103.27]) by mx0b-00069f02.pphosted.com (PPS) with ESMTPS id 3u0rqdrmnp-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Sat, 28 Oct 2023 16:29:41 +0000 Received: from pps.filterd (iadpaimrmta03.imrmtpd1.prodappiadaev1.oraclevcn.com [127.0.0.1]) by iadpaimrmta03.imrmtpd1.prodappiadaev1.oraclevcn.com (8.17.1.19/8.17.1.19) with ESMTP id 39SBCVIV030752; Sat, 28 Oct 2023 16:29:40 GMT Received: from pps.reinject (localhost [127.0.0.1]) by iadpaimrmta03.imrmtpd1.prodappiadaev1.oraclevcn.com (PPS) with ESMTPS id 3u0rr8y2h8-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Sat, 28 Oct 2023 16:29:40 +0000 Received: from iadpaimrmta03.imrmtpd1.prodappiadaev1.oraclevcn.com (iadpaimrmta03.imrmtpd1.prodappiadaev1.oraclevcn.com [127.0.0.1]) by pps.reinject (8.17.1.5/8.17.1.5) with ESMTP id 39SGTdgL026344; Sat, 28 Oct 2023 16:29:39 GMT Received: from t460-2.nl.oracle.com (dhcp-10-175-33-123.vpn.oracle.com [10.175.33.123]) by iadpaimrmta03.imrmtpd1.prodappiadaev1.oraclevcn.com (PPS) with ESMTP id 3u0rr8y2gm-1; Sat, 28 Oct 2023 16:29:39 +0000 From: Vegard Nossum <vegard.nossum@oracle.com> To: Jonathan Corbet <corbet@lwn.net> Cc: linux-doc@vger.kernel.org, Federico Vaga <federico.vaga@vaga.pv.it>, Akira Yokosawa <akiyks@gmail.com>, Carlos Bilbao <carlos.bilbao@amd.com>, Alex Shi <alexs@kernel.org>, Yanteng Si <siyanteng@loongson.cn>, Hu Haowen <src.res.211@gmail.com>, linux-kernel@vger.kernel.org, Vegard Nossum <vegard.nossum@oracle.com> Subject: [PATCH] docs: translations: add translations links when they exist Date: Sat, 28 Oct 2023 18:29:31 +0200 Message-Id: <20231028162931.261843-1-vegard.nossum@oracle.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.987,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2023-10-28_15,2023-10-27_01,2023-05-22_02 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 bulkscore=0 suspectscore=0 spamscore=0 phishscore=0 mlxlogscore=999 adultscore=0 mlxscore=0 malwarescore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2310240000 definitions=main-2310280132 X-Proofpoint-ORIG-GUID: _Ev5Xq1gNTxfDJ48GX9LoBhpgE8hsTf0 X-Proofpoint-GUID: _Ev5Xq1gNTxfDJ48GX9LoBhpgE8hsTf0 X-Spam-Status: No, score=-2.8 required=5.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (snail.vger.email [0.0.0.0]); Sat, 28 Oct 2023 09:30:20 -0700 (PDT) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1781017473654187565 X-GMAIL-MSGID: 1781017473654187565 |
Series |
docs: translations: add translations links when they exist
|
|
Commit Message
Vegard Nossum
Oct. 28, 2023, 4:29 p.m. UTC
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 with v7.3.0.
Signed-off-by: Vegard Nossum <vegard.nossum@oracle.com>
---
Documentation/conf.py | 2 +-
Documentation/sphinx-static/custom.css | 8 ++
.../sphinx/templates/translations.html | 12 +++
Documentation/sphinx/translations.py | 96 +++++++++++++++++++
4 files changed, 117 insertions(+), 1 deletion(-)
create mode 100644 Documentation/sphinx/templates/translations.html
create mode 100644 Documentation/sphinx/translations.py
Comments
This went a bit fast... corrections below. On 28/10/2023 18:29, Vegard Nossum wrote: > 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. [...] > Testing: make htmldocs with v7.3.0. *Sphinx v7.3.0. > +all_languages = { > + # English is always first > + None: 'English', > + > + # Keep the rest sorted alphabetically > + 'zh_CN': 'Chinese', > + 'it_IT': 'Italian', > + 'ja_JP': 'Japanese', > + 'ko_KR': 'Korean', > + 'sp_SP': 'Spanish', > + 'zh_TW': 'Taiwanese', > +} I went with my naive understanding of the language codes without double checking but I think these might be better names: 'zh_CN': 'Chinese (simplified)' 'zh_TW': 'Chinese (traditional)', Thoughts? Vegard
I find it a nice initiative :) On Sat, Oct 28, 2023 at 06:29:31PM +0200, Vegard Nossum wrote: >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 with v7.3.0. > >Signed-off-by: Vegard Nossum <vegard.nossum@oracle.com> >--- > Documentation/conf.py | 2 +- > Documentation/sphinx-static/custom.css | 8 ++ > .../sphinx/templates/translations.html | 12 +++ > Documentation/sphinx/translations.py | 96 +++++++++++++++++++ > 4 files changed, 117 insertions(+), 1 deletion(-) > create mode 100644 Documentation/sphinx/templates/translations.html > create mode 100644 Documentation/sphinx/translations.py > >diff --git a/Documentation/conf.py b/Documentation/conf.py >index d4fdf6a3875a..64eab500b2cd 100644 >--- a/Documentation/conf.py >+++ b/Documentation/conf.py >@@ -55,7 +55,7 @@ needs_sphinx = '1.7' > 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): >diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css >index 084a884f6fb7..33adee4a35d9 100644 >--- a/Documentation/sphinx-static/custom.css >+++ b/Documentation/sphinx-static/custom.css >@@ -73,3 +73,11 @@ input.kernel-toc-toggle { display: none; } > h3.kernel-toc-contents { display: inline; } > div.kerneltoc a { color: black; } > } >+ >+/* Language selection bar */ >+div.language-selection { >+ background: #eeeeee; >+ border: 1px solid #cccccc; >+ margin-bottom: 1em; >+ padding: .5em; >+} >diff --git a/Documentation/sphinx/templates/translations.html b/Documentation/sphinx/templates/translations.html >new file mode 100644 >index 000000000000..08afb595c203 >--- /dev/null >+++ b/Documentation/sphinx/templates/translations.html >@@ -0,0 +1,12 @@ >+<!-- SPDX-License-Identifier: GPL-2.0 --> >+<!-- Copyright © 2023, Oracle and/or its affiliates. --> >+ >+{# Create a language bar for translations #} >+{% if languages|length > 0: %} >+<div class="language-selection"> >+Languages: Should also "Languages" subject to translation? Or perhaps, nothing. Just the list of languages. >+{% for ref in languages: %} >+<a href="{{ ref.refuri }}">{{ ref.astext() }}</a>{% if not loop.last %}, {% endif %} >+{% endfor %} >+</div> >+{% endif %} >diff --git a/Documentation/sphinx/translations.py b/Documentation/sphinx/translations.py >new file mode 100644 >index 000000000000..e1da811bdaf0 >--- /dev/null >+++ b/Documentation/sphinx/translations.py >@@ -0,0 +1,96 @@ >+# 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', >+ 'it_IT': 'Italian', >+ 'ja_JP': 'Japanese', >+ 'ko_KR': 'Korean', >+ 'sp_SP': 'Spanish', >+ 'zh_TW': 'Taiwanese', Minor comment. Should we use the language name in its original language? Example: [... snip ...] 'it_IT': Italiano, [... snip ...] 'sp_SP': Español, [... snip ...] >+} >+ >+class LanguagesNode(nodes.Element): >+ pass >+ >+class TranslationsTransform(Transform): >+ default_priority = 900 >+ >+ def apply(self): >+ app = self.document.settings.env.app >+ if app.builder.format not in ['html']: >+ return >+ >+ 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() >+ >+ for lang_code, lang_name in all_languages.items(): >+ if lang_code == this_lang_code: >+ continue We could show all languages (remove the above two lines) to have a consistent output among pages. >+ 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): >+ 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={ >+ '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, >+ } >-- >2.34.1 >
On Sat, 28 Oct 2023, Vegard Nossum <vegard.nossum@oracle.com> wrote: > 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 with v7.3.0. I'm just observing the kernel documentation has a system of its own for translations. Maybe it's fine this way, but it's certainly different from what the Sphinx developers had in mind. (But I'm not an expert on this field, don't ask me how this should be done!) Regardless, the implication is that this builds on the translation solution chosen for kernel documentation, for better and for worse, and raises the bar for switching later. It's something to be aware of. > > Signed-off-by: Vegard Nossum <vegard.nossum@oracle.com> > --- > Documentation/conf.py | 2 +- > Documentation/sphinx-static/custom.css | 8 ++ > .../sphinx/templates/translations.html | 12 +++ > Documentation/sphinx/translations.py | 96 +++++++++++++++++++ > 4 files changed, 117 insertions(+), 1 deletion(-) > create mode 100644 Documentation/sphinx/templates/translations.html > create mode 100644 Documentation/sphinx/translations.py > > diff --git a/Documentation/conf.py b/Documentation/conf.py > index d4fdf6a3875a..64eab500b2cd 100644 > --- a/Documentation/conf.py > +++ b/Documentation/conf.py > @@ -55,7 +55,7 @@ needs_sphinx = '1.7' > 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): > diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css > index 084a884f6fb7..33adee4a35d9 100644 > --- a/Documentation/sphinx-static/custom.css > +++ b/Documentation/sphinx-static/custom.css > @@ -73,3 +73,11 @@ input.kernel-toc-toggle { display: none; } > h3.kernel-toc-contents { display: inline; } > div.kerneltoc a { color: black; } > } > + > +/* Language selection bar */ > +div.language-selection { > + background: #eeeeee; > + border: 1px solid #cccccc; > + margin-bottom: 1em; > + padding: .5em; > +} > diff --git a/Documentation/sphinx/templates/translations.html b/Documentation/sphinx/templates/translations.html > new file mode 100644 > index 000000000000..08afb595c203 > --- /dev/null > +++ b/Documentation/sphinx/templates/translations.html > @@ -0,0 +1,12 @@ > +<!-- SPDX-License-Identifier: GPL-2.0 --> > +<!-- Copyright © 2023, Oracle and/or its affiliates. --> > + > +{# Create a language bar for translations #} > +{% if languages|length > 0: %} > +<div class="language-selection"> > +Languages: > +{% for ref in languages: %} > +<a href="{{ ref.refuri }}">{{ ref.astext() }}</a>{% if not loop.last %}, {% endif %} > +{% endfor %} > +</div> > +{% endif %} This could also be part of the menu on the left, I think it's fairly easy to extend in alabaster. But then again it's already pretty crowded, and it's all a matter of taste. > diff --git a/Documentation/sphinx/translations.py b/Documentation/sphinx/translations.py > new file mode 100644 > index 000000000000..e1da811bdaf0 > --- /dev/null > +++ b/Documentation/sphinx/translations.py > @@ -0,0 +1,96 @@ > +# 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', > + 'it_IT': 'Italian', > + 'ja_JP': 'Japanese', > + 'ko_KR': 'Korean', > + 'sp_SP': 'Spanish', > + 'zh_TW': 'Taiwanese', > +} Maybe the path to translations should be a configuration option for the extension, and it could read the translations from the directory instead of hard coding them here. > + > +class LanguagesNode(nodes.Element): > + pass > + > +class TranslationsTransform(Transform): > + default_priority = 900 > + > + def apply(self): > + app = self.document.settings.env.app > + if app.builder.format not in ['html']: > + return > + > + 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:]) Need to rename these files, and keep the translated document names up-to-date: for f in $(find Documentation/translations/ -name "*.rst"); do if ! test -f Documentation/${f##Documentation/translations/??_??/}; then echo $f; fi; done Maybe the extension should warn about documents under translations that do not have a corresponding original. BR, Jani. > + > + new_nodes = LanguagesNode() > + > + 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): > + 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={ > + '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, > + }
Hi, On Sat, 28 Oct 2023 18:29:31 +0200, Vegard Nossum wrote: > 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 with v7.3.0. So, I've started playing with this. It looks like this introduces hysteresis in successive runs of "make htmldocs" and "make latexdocs". Steps to reproduce 1. Run "make cleandocs" 2. Run "make htmldocs" 3. Run "make latexdocs" This aborts with the message (under Sphinx 7.2.6): Extension error (translations): Handler <function process_languages at 0x7f122f343420> for event 'doctree-resolved' threw an exception (exception: 'LaTeXBuilder' object has no attribute 'templates') make[2]: *** [Documentation/Makefile:128: latexdocs] Error 2 make[1]: *** [/linux/Makefile:1695: latexdocs] Error 2 make: *** [Makefile:234: __sub-make] Error 2 Command exited with non-zero status 2 If I run "make latexdocs" in step 2 and "make htmldocs" in step 3, both runs complete successfully, but html pages don't have the expected links to other translations. All I can do is to report the symptoms. Vegard, can you look into them? > > Signed-off-by: Vegard Nossum <vegard.nossum@oracle.com> > --- > Documentation/conf.py | 2 +- > Documentation/sphinx-static/custom.css | 8 ++ > .../sphinx/templates/translations.html | 12 +++ > Documentation/sphinx/translations.py | 96 +++++++++++++++++++ > 4 files changed, 117 insertions(+), 1 deletion(-) > create mode 100644 Documentation/sphinx/templates/translations.html > create mode 100644 Documentation/sphinx/translations.py > > diff --git a/Documentation/conf.py b/Documentation/conf.py > index d4fdf6a3875a..64eab500b2cd 100644 > --- a/Documentation/conf.py > +++ b/Documentation/conf.py > @@ -55,7 +55,7 @@ needs_sphinx = '1.7' > 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): > diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css > index 084a884f6fb7..33adee4a35d9 100644 > --- a/Documentation/sphinx-static/custom.css > +++ b/Documentation/sphinx-static/custom.css > @@ -73,3 +73,11 @@ input.kernel-toc-toggle { display: none; } > h3.kernel-toc-contents { display: inline; } > div.kerneltoc a { color: black; } > } > + > +/* Language selection bar */ > +div.language-selection { > + background: #eeeeee; > + border: 1px solid #cccccc; > + margin-bottom: 1em; > + padding: .5em; > +} > diff --git a/Documentation/sphinx/templates/translations.html b/Documentation/sphinx/templates/translations.html > new file mode 100644 > index 000000000000..08afb595c203 > --- /dev/null > +++ b/Documentation/sphinx/templates/translations.html > @@ -0,0 +1,12 @@ > +<!-- SPDX-License-Identifier: GPL-2.0 --> > +<!-- Copyright © 2023, Oracle and/or its affiliates. --> > + > +{# Create a language bar for translations #} > +{% if languages|length > 0: %} > +<div class="language-selection"> > +Languages: > +{% for ref in languages: %} > +<a href="{{ ref.refuri }}">{{ ref.astext() }}</a>{% if not loop.last %}, {% endif %} > +{% endfor %} > +</div> > +{% endif %} > diff --git a/Documentation/sphinx/translations.py b/Documentation/sphinx/translations.py > new file mode 100644 > index 000000000000..e1da811bdaf0 > --- /dev/null > +++ b/Documentation/sphinx/translations.py > @@ -0,0 +1,96 @@ > +# 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 NoUri is not in sphinx.errors prior to Sphinx 2. I think it is a good chance to finally get rid of Sphinx 1.7.x support. Thanks, Akira > + > +all_languages = { > + # English is always first > + None: 'English', > + > + # Keep the rest sorted alphabetically > + 'zh_CN': 'Chinese', > + 'it_IT': 'Italian', > + 'ja_JP': 'Japanese', > + 'ko_KR': 'Korean', > + 'sp_SP': 'Spanish', > + 'zh_TW': 'Taiwanese', > +} > + > +class LanguagesNode(nodes.Element): > + pass > + > +class TranslationsTransform(Transform): > + default_priority = 900 > + > + def apply(self): > + app = self.document.settings.env.app > + if app.builder.format not in ['html']: > + return > + > + 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() > + > + 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): > + 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={ > + '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, > + } > -- > 2.34.1
On 01/11/2023 15:56, Akira Yokosawa wrote: > It looks like this introduces hysteresis in successive runs of > "make htmldocs" and "make latexdocs". > > Steps to reproduce > > 1. Run "make cleandocs" > > 2. Run "make htmldocs" > > 3. Run "make latexdocs" > > This aborts with the message (under Sphinx 7.2.6): > > Extension error (translations): > Handler <function process_languages at 0x7f122f343420> for event 'doctree-resolved' threw an exception (exception: 'LaTeXBuilder' object has no attribute 'templates') > make[2]: *** [Documentation/Makefile:128: latexdocs] Error 2 > make[1]: *** [/linux/Makefile:1695: latexdocs] Error 2 > make: *** [Makefile:234: __sub-make] Error 2 > Command exited with non-zero status 2 > > If I run "make latexdocs" in step 2 and "make htmldocs" in step 3, > both runs complete successfully, but html pages don't have the > expected links to other translations. > > All I can do is to report the symptoms. > Vegard, can you look into them? Thanks for testing this out and reporting! I think we can fix this by moving the "is this html output?" check from the TranslationsTransform into the 'doctree-resolved' handler (which, as far as I can tell, runs after the doctree has been serialized to disk but before output is generated). I've attached an incremental patch, does that seem to work for you? I test both (clean/html/latex + clean/latex/html) and it seemed to work here. I had a look at using a custom "visit" callback that would just render the HTML in place instead of manipulating the doctree, but it also doesn't feel right as then you need to specify callbacks for every output writer; there doesn't seem to be a way to ignore the node by default. Maybe I should ask on the Sphinx/docutils mailing lists what the "proper" way to do this would be. Thanks again! Vegard
On 2023/11/02 20:07, Vegard Nossum wrote: > On 01/11/2023 15:56, Akira Yokosawa wrote: >> It looks like this introduces hysteresis in successive runs of >> "make htmldocs" and "make latexdocs". >> >> Steps to reproduce >> >> 1. Run "make cleandocs" >> >> 2. Run "make htmldocs" >> >> 3. Run "make latexdocs" >> >> This aborts with the message (under Sphinx 7.2.6): >> >> Extension error (translations): >> Handler <function process_languages at 0x7f122f343420> for event 'doctree-resolved' threw an exception (exception: 'LaTeXBuilder' object has no attribute 'templates') >> make[2]: *** [Documentation/Makefile:128: latexdocs] Error 2 >> make[1]: *** [/linux/Makefile:1695: latexdocs] Error 2 >> make: *** [Makefile:234: __sub-make] Error 2 >> Command exited with non-zero status 2 >> >> If I run "make latexdocs" in step 2 and "make htmldocs" in step 3, >> both runs complete successfully, but html pages don't have the >> expected links to other translations. >> >> All I can do is to report the symptoms. >> Vegard, can you look into them? > > Thanks for testing this out and reporting! > > I think we can fix this by moving the "is this html output?" check from > the TranslationsTransform into the 'doctree-resolved' handler (which, as > far as I can tell, runs after the doctree has been serialized to disk > but before output is generated). > > I've attached an incremental patch, does that seem to work for you? I> test both (clean/html/latex + clean/latex/html) and it seemed to work here. Yes, it works here as well. Thanks, Akira > > I had a look at using a custom "visit" callback that would just render > the HTML in place instead of manipulating the doctree, but it also > doesn't feel right as then you need to specify callbacks for every > output writer; there doesn't seem to be a way to ignore the node by > default. Maybe I should ask on the Sphinx/docutils mailing lists what > the "proper" way to do this would be. > > Thanks again! > > > Vegard
在 2023/10/29 02:51, Vegard Nossum 写道: > > This went a bit fast... corrections below. > > On 28/10/2023 18:29, Vegard Nossum wrote: >> 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. > > [...] > >> Testing: make htmldocs with v7.3.0. > > *Sphinx v7.3.0. > >> +all_languages = { >> + # English is always first >> + None: 'English', >> + >> + # Keep the rest sorted alphabetically >> + 'zh_CN': 'Chinese', >> + 'it_IT': 'Italian', >> + 'ja_JP': 'Japanese', >> + 'ko_KR': 'Korean', >> + 'sp_SP': 'Spanish', >> + 'zh_TW': 'Taiwanese', >> +} > > I went with my naive understanding of the language codes without double > checking but I think these might be better names: > > 'zh_CN': 'Chinese (simplified)' > 'zh_TW': 'Chinese (traditional)', Yes, but we need to capitalize the first letter, just like: 'zh_CN': 'Chinese (Simplified)' 'zh_TW': 'Chinese (Traditional)', see <https://translations.launchpad.net/ubuntu> Thanks, Yanteng > > Thoughts? > > > Vegard
Vegard Nossum <vegard.nossum@oracle.com> writes: > 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 with v7.3.0. > > Signed-off-by: Vegard Nossum <vegard.nossum@oracle.com> > --- > Documentation/conf.py | 2 +- > Documentation/sphinx-static/custom.css | 8 ++ > .../sphinx/templates/translations.html | 12 +++ > Documentation/sphinx/translations.py | 96 +++++++++++++++++++ > 4 files changed, 117 insertions(+), 1 deletion(-) > create mode 100644 Documentation/sphinx/templates/translations.html > create mode 100644 Documentation/sphinx/translations.py OK, this does seem to work. The naming of the translations definitely needs to change; if we put out something with "Taiwanese" in it, experience tells me, there will be objections - and that's not what the translation was called when it was added. I'm unsure about putting the languages in the top bar like that; it will already become pretty wide with the relabeled translations, and may not look great on a small-screen device. Perhaps a pulldown would be better? The build problem reported by Akira definitely needs to be fixed as well. Thanks, jon
diff --git a/Documentation/conf.py b/Documentation/conf.py index d4fdf6a3875a..64eab500b2cd 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -55,7 +55,7 @@ needs_sphinx = '1.7' 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): diff --git a/Documentation/sphinx-static/custom.css b/Documentation/sphinx-static/custom.css index 084a884f6fb7..33adee4a35d9 100644 --- a/Documentation/sphinx-static/custom.css +++ b/Documentation/sphinx-static/custom.css @@ -73,3 +73,11 @@ input.kernel-toc-toggle { display: none; } h3.kernel-toc-contents { display: inline; } div.kerneltoc a { color: black; } } + +/* Language selection bar */ +div.language-selection { + background: #eeeeee; + border: 1px solid #cccccc; + margin-bottom: 1em; + padding: .5em; +} diff --git a/Documentation/sphinx/templates/translations.html b/Documentation/sphinx/templates/translations.html new file mode 100644 index 000000000000..08afb595c203 --- /dev/null +++ b/Documentation/sphinx/templates/translations.html @@ -0,0 +1,12 @@ +<!-- SPDX-License-Identifier: GPL-2.0 --> +<!-- Copyright © 2023, Oracle and/or its affiliates. --> + +{# Create a language bar for translations #} +{% if languages|length > 0: %} +<div class="language-selection"> +Languages: +{% for ref in languages: %} +<a href="{{ ref.refuri }}">{{ ref.astext() }}</a>{% if not loop.last %}, {% endif %} +{% endfor %} +</div> +{% endif %} diff --git a/Documentation/sphinx/translations.py b/Documentation/sphinx/translations.py new file mode 100644 index 000000000000..e1da811bdaf0 --- /dev/null +++ b/Documentation/sphinx/translations.py @@ -0,0 +1,96 @@ +# 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', + 'it_IT': 'Italian', + 'ja_JP': 'Japanese', + 'ko_KR': 'Korean', + 'sp_SP': 'Spanish', + 'zh_TW': 'Taiwanese', +} + +class LanguagesNode(nodes.Element): + pass + +class TranslationsTransform(Transform): + default_priority = 900 + + def apply(self): + app = self.document.settings.env.app + if app.builder.format not in ['html']: + return + + 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() + + 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): + 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={ + '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, + }