refactor versions and languages switchers logic + canonical urls + Makefile

This commit is contained in:
Antoine Vandevenne (anv) 2021-02-01 08:58:35 +01:00
parent 936fae4b40
commit c1f2fc9cd3
8 changed files with 138 additions and 116 deletions

View File

@ -2,7 +2,9 @@
SPHINX_BUILD = sphinx-build
CONFIG_DIR = .
SPHINXOPTS = -A google_analytics_key=$(GOOGLE_ANALYTICS_KEY)
SPHINXOPTS = -D project_root=$(ROOT) -D canonical_version=$(CANONICAL_VERSION) \
-D versions=$(VERSIONS) -D languages=$(LANGUAGES) -D language=$(CURRENT_LANG) \
-A google_analytics_key=$(GOOGLE_ANALYTICS_KEY)
SOURCE_DIR = content
BUILD_DIR = _build
@ -29,6 +31,10 @@ clean:
@echo "Cleaning finished."
edi: SPHINXOPTS += -A collapse_menu=True
edi: VERSIONS += 12.0,13.0,14.0
edi: CANONICAL_VERSION += 14.0
edi: LANGUAGES += en,fr,es
edi: CURRENT_LANG += fr
edi: clean html
html: extensions/odoo_theme/static/style.css

171
conf.py
View File

@ -1,4 +1,3 @@
import os
import sys
from pathlib import Path
@ -11,6 +10,10 @@ _logger = logging.getLogger(__name__)
#=== General configuration ===#
# General information about the project.
project = 'odoo'
copyright = 'Odoo S.A.'
# `version` if the version info for the project being documented, acts as replacement for |version|,
# also used in various other places throughout the built documents.
# `release` is the full version, including alpha/beta/rc tags. Acts as replacement for |release|.
@ -19,20 +22,16 @@ version = release = '12.0'
# The minimal Sphinx version required to build the documentation.
needs_sphinx = '3.0.0'
# The default language in which the documentation is written. It is set as `None` because Sphinx
# considers that no language means 'en'.
language = None
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'odoo'
copyright = 'Odoo S.A.'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# List of patterns, relative to source directory, that match files and directories to ignore when
# looking for source files.
exclude_patterns = [
@ -128,7 +127,7 @@ github_user = 'odoo'
github_project = 'documentation-user'
locale_dirs = ['locale/']
LANGUAGES = {
supported_languages = {
'de': 'German',
'en': 'English',
'es': 'Spanish',
@ -244,41 +243,90 @@ latex_documents = [
latex_logo = 'static/img/odoo_logo.png'
# If true, show URL addresses after external links.
latex_show_urls = "True"
latex_show_urls = 'True'
def setup(app):
app.connect('html-page-context', canonicalize)
# VFE TODO remove default before merge
app.add_config_value('canonical_root', os.path.dirname(os.path.realpath(__file__)), 'env')
app.add_config_value('canonical_branch', 'master', 'env')
# Generate all alternate URLs for each document
app.add_config_value('project_root', None, 'env')
app.add_config_value('canonical_version', None, 'env')
app.add_config_value('versions', None, 'env')
app.add_config_value('languages', None, 'env')
app.connect('html-page-context', _generate_alternate_urls)
app.connect('html-page-context', versionize)
# VFE TODO before merge, remove the default value put for testing
test_versions = ['12.0', '13.0', '14.0'] # TODO if not provided, use 'local'
app.add_config_value('versions', ",".join(test_versions), 'env')
app.connect('html-page-context', localize)
# VFE TODO before merge remove the default value put for testing
test_languages = ['fr', 'en', 'es']
app.add_config_value('languages', ",".join(test_languages), 'env')
app.connect('doctree-resolved', tag_toctrees) # TODO ANVFE not used + typo
app.connect('doctree-resolved', tag_toctrees) # TODO ANVFE review + typo
def versionize(app, pagename, templatename, context, doctree):
""" Adds a version switcher below the menu, requires ``canonical_root``
and ``versions`` (an ordered, space-separated lists of all possible
versions).
def _generate_alternate_urls(app, pagename, templatename, context, doctree):
""" Add keys of required alternate URLs for the current document in the rendering context.
Alternate URLS are required for:
- The canonical link tag
- The version switcher
- The language switcher and related link tags
"""
if not (app.config.canonical_root and app.config.versions):
return
context['versions'] = [
(vs, _build_url(app.config.canonical_root, vs, pagename))
for vs in app.config.versions.split(',')
if vs != app.config.version
]
def _canonicalize():
""" Add the canonical URL for the current document in the rendering context.
The canonical version is the last released version of the documentation.
For a given language, the canonical root of a page is in the same language so that web
searches in that language don't redirect users to the english version of that page.
E.g.:
- /documentation/sale.html -> canonical = /documentation/14.0/sale.html
- /documentation/11.0/fr/website.html -> canonical = /documentation/14.0/fr/website.html
"""
# If the canonical version is not set, assume that the project has a single version
_canonical_version = app.config.canonical_version or version
_canonical_lang = 'en' # Always 'en'. Don't take the value of the config option.
context['canonical'] = _build_url(_version=_canonical_version, _lang=_canonical_lang)
def _versionize():
""" Add the pairs of (version, url) for the current document in the rendering context.
The entry 'version' is added by Sphinx in the rendering context.
"""
# If the list of versions is not set, assume that the project has no alternate version
_alternate_versions = app.config.versions and app.config.versions.split(',') or []
context['alternate_versions'] = [
(_alternate_version, _build_url(_version=_alternate_version))
for _alternate_version in _alternate_versions if _alternate_version != version
]
def _localize():
"""
The entry 'language' is added by Sphinx in the rendering context.
"""
_current_lang = app.config.language or 'en'
# Replace the context value by its translated description ("Français" instead of "french")
context['language'] = supported_languages.get(_current_lang)
# If the list of languages is not set, assume that the project has no alternate language
_alternate_languages = app.config.languages and app.config.languages.split(',') or []
context['alternate_languages'] = [
(
supported_languages.get(_alternate_lang),
_alternate_lang.split('_')[0] if _alternate_lang != 'en' else 'x-default',
_build_url(_lang=_alternate_lang),
)
for _alternate_lang in _alternate_languages
if _alternate_lang in supported_languages and _alternate_lang != _current_lang
]
def _build_url(_version=None, _lang=None):
_root = app.config.project_root or str(Path(__file__).parent)
_version = _version or version
_lang = _lang or app.config.language or 'en'
_canonical_page = (pagename + '.html').replace('index.html', '').replace('index/', '')
return f'{_root}/{_version}{f"/{_lang}" if _lang != "en" else ""}/{_canonical_page}'
_canonicalize()
_versionize()
_localize()
def tag_toctrees(app, doctree, docname):
"""Add a 'is-toc-page' metadata entry to all documents containing only a toctree node"""
@ -287,7 +335,7 @@ def tag_toctrees(app, doctree, docname):
# title
# compound@toctree-wrapper
# ....
if not len(doctree.children) == 1:
if not len(doctree.children) <= 1:
return
section = doctree.children[0]
if len(section.children) < 2:
@ -297,50 +345,3 @@ def tag_toctrees(app, doctree, docname):
return
app.env.metadata[docname]['has_only_toc'] = True
def localize(app, pagename, templatename, context, doctree):
""" Adds a language switcher below the menu, requires ``canonical_root``
and ``languages`` (an ordered, space-separated lists of all possible
languages).
"""
if not (app.config.canonical_root and app.config.languages):
return
current_lang = app.config.language or 'en'
context['language'] = LANGUAGES.get(current_lang, current_lang.upper())
context['languages'] = [
(LANGUAGES.get(la, la.upper()), _build_url(
app.config.canonical_root, (la != 'en' and la or ''), pagename))
for la in app.config.languages.split(',')
if la != current_lang
]
context['language_codes'] = [
(la.split('_')[0] if la != 'en' else 'x-default',
_build_url(app.config.canonical_root, (la != 'en' and la or ''), pagename))
for la in app.config.languages.split(',')
]
def canonicalize(app, pagename, templatename, context, doctree):
""" Adds a 'canonical' URL for the current document in the rendering
context. Requires the ``canonical_root`` setting being set. The canonical
branch is ``master`` but can be overridden using ``canonical_branch``.
/documentation/user/12.0/sale.html -> /documentation/user/13.0/sale.html
/documentation/user/11.0/fr/website.html -> /documentation/user/13.0/fr/website.html
"""
if not app.config.canonical_root:
return
lang = app.config.language or 'en'
context['canonical'] = _build_url(
app.config.canonical_root, app.config.canonical_branch, pagename, lang)
def _build_url(root, branch, pagename, lang='en'):
return "{canonical_url}{canonical_branch}{lang}/{canonical_page}".format(
canonical_url=root,
canonical_branch=branch,
lang=lang != 'en' and lang or '',
canonical_page=(pagename + '.html').replace('index.html', '')
.replace('index/', ''),
)

View File

@ -22,16 +22,15 @@ def update_meta(app, pagename, templatename, context, doctree):
# TODO VFE detailed explanation of the patch logic and use.
class monkey(object):
class Monkey(object):
def __init__(self, obj):
self.obj = obj
def __call__(self, fn):
name = fn.__name__
old = getattr(self.obj, name)
setattr(self.obj, name, lambda self_, *args, **kwargs: \
fn(old, self_, *args, **kwargs))
setattr(self.obj, name, lambda self_, *args, **kwargs: fn(old, self_, *args, **kwargs))
@monkey(toctree.TocTree)
@Monkey(toctree.TocTree)
def resolve(old_resolve, tree, docname, *args, **kwargs):
resolved_toc = old_resolve(tree, docname, *args, **kwargs)
if resolved_toc:

View File

@ -38,8 +38,8 @@
{% endblock %}
{% block linktags %}
{% for code, url in language_codes %}
<link rel="alternate" hreflang="{{ code }}" href="{{ url }}" />
{% for alternate_language, language_code, url in alternate_languages %}
<link rel="alternate" hreflang="{{ language_code }}" href="{{ url }}" />
{%- endfor %}
<link rel="canonical" href="{{ canonical }}" />
{{ super() }}

View File

@ -8,7 +8,11 @@
</div>
{% include "layout_templates/searchbox.html" %}
<div class="d-none d-md-flex">
{% include "layout_templates/lang_switcher.html" %}
{% include "layout_templates/version_switcher.html" %}
{#{% if alternate_languages %}#}
{% include "layout_templates/language_switcher.html" %}
{#{% endif %}#}
{#{% if alternate_versions %}#}
{% include "layout_templates/version_switcher.html" %}
{#{% endif %}#}
<a class="btn btn-primary fw_semibold" href="https://odoo.com/trial">Try Odoo for FREE</a>
</div>

View File

@ -1,17 +0,0 @@
{% if languages %}
<div class="o_languages me-2">
<select class="languages form-select">
<!-- <a class="dropdown-toggle" href="#" data-toggle="dropdown">
English
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="{{ url }}">English</a></li>
<li><a href="{{ url }}">French</a></li>
</ul> -->
<option>{{ language }}</option> {# Current language #}
{% for name, url in languages %}
<option><a href="{{ url }}">{{ name }}</a></option>
{% endfor %}
</select>
</div>
{% endif %}

View File

@ -0,0 +1,22 @@
<div class="o_languages me-2">
{# <!-- TODO EDI <a> inside <option> is illegal
<select class="languages form-select">
<a class="dropdown-toggle" href="#" data-toggle="dropdown">
English
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="{{ url }}">English</a></li>
<li><a href="{{ url }}">French</a></li>
</ul>
<option>{{ language }}</option> {# The current language #}
{% for alternate_language, language_code, url in alternate_languages %}
<option><a href="{{ url }}">{{ alternate_language }}</a></option>
{% endfor %}
</select> --> #}
<div>
<a>{{ language }}</a> {# The current language #}
{% for alternate_language, language_code, url in alternate_languages %}
<a href="{{ url }}">{{ alternate_language }}</a>
{% endfor %}
</div>
</div>

View File

@ -3,11 +3,18 @@
<label class="fw_bold small mr-2">Version</label>
</div>
<div class="col-8">
{# <!-- TODO EDI <a> inside <option> is illegal
<select class="form-select">
<option>{{ version }}</option> {# Current version #}
{% for name, url in versions %}
<option><a href="{{ url }}">{{ name }}</a></option>
{% endfor %}
</select>
<option>{{ version }}</option> {# The current version #}
{% for alternate_version, url in versions %}
<option><a href="{{ url }}">{{ alternate_version }}</a></option>
{% endfor %}
</select> --> #}
<div>
<a>{{ version }}</a> {# The current version #}
{% for alternate_version, url in alternate_versions %}
<a href="{{ url }}">{{ alternate_version }}</a>
{% endfor %}
</div>
</div>
</div>