Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Language Selector :) #394

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,28 @@ To build live documentation that updates when you update local files, run the fo
$ nox -s docs-live
```

### Building for release

When building for release, the docs are built multiple times for each translation,
but translations are only included in the production version of the guide after some completion threshold.

The sphinx build environment is controlled by an environment variable `SPHINX_ENV`

- when `SPHINX_ENV=development` (default), sphinx assumes all languages are built,
and includes them in the language selector
- when `SPHINX_ENV=production`, only those languages in `release_languages` (set in `conf.py`)
are built and included in the language selector.

Most of the time you should not need to set `SPHINX_ENV`,
as it is forced by the primary nox sessions intended to be used for release or development:

`SPHINX_ENV=development`
- `docs-live` - autobuild english
- `docs-live-lang` - autobuild a single language
- `docs-live-langs` - autobuild all languages

`SPHINX_ENV=production`
- `build-test` - build all languages for production

## Contributing to this guide

Expand Down
13 changes: 13 additions & 0 deletions _static/language_select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
document.addEventListener("DOMContentLoaded", () => {
let selectors = document.querySelectorAll("#language-selector");
selectors.forEach((selector) => {
selector.addEventListener("change", (event) => {
let target = event.target.value;
if (target.startsWith("https")) {
window.location.href = target;
} else {
window.location.pathname = target;
}
});
});
});
8 changes: 8 additions & 0 deletions _static/pyos.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ body {
margin-right: auto !important;
}

.navbar-persistent--mobile {
margin-left: unset !important;
}

/* custom fonts */

html,
Expand Down Expand Up @@ -375,3 +379,7 @@ th {
border: 1px solid #ccc; /* Light gray border */
padding: 8px; /* Add some padding for better readability */
}

/* ----------------- */
/* Language Selector */
/* ----------------- */
30 changes: 30 additions & 0 deletions _templates/language-selector.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{%- macro langlink(lang, selected=False) -%} {%- if lang == "en" %}
<option
value="{{ baseurl }}{{ pagename }}{{ file_suffix }}"
{%
if
selected
%}selected{%
endif
%}
>
{{ lang }}
</option>
{%- else %}
<option
value="{{ baseurl }}{{ lang }}/{{ pagename }}{{ file_suffix }}"
{%
if
selected
%}selected{%
endif
%}
>
{{ lang }}
</option>
{%- endif -%} {%- endmacro -%}
<select class="dropdown" id="language-selector" aria-label="Choose a language">
{{ langlink(language, selected=True) }} {%- for a_language in languages -%}
{%- if a_language != language -%} {{ langlink(a_language) }} {%- endif -%} {%-
endfor %}
</select>
37 changes: 34 additions & 3 deletions conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,40 @@
# sys.path.insert(0, os.path.abspath('.'))
from datetime import datetime
import subprocess
import os

current_year = datetime.now().year
organization_name = "pyOpenSci"

# env vars
sphinx_env = os.environ.get("SPHINX_ENV", "development")
language_env = os.environ.get("SPHINX_LANG", "en")


# -- Project information -----------------------------------------------------

project = "pyOpenSci Python Package Guide"
copyright = f"{current_year}, {organization_name}"
author = "pyOpenSci Community"

# Language of the current build
# language can later be overridden (eg with the -D flag)
# but we need it set here so it can make it into the html_context
language = language_env
# all languages that have .po files generated for them
# (excluding english)
languages = ["es", "ja"]
# the languages that will be included in a production build
# (also excluding english)
release_languages = []

# languages that will be included in the language dropdown
# (ie. all that are being built in this nox build session)
if sphinx_env == "production":
build_languages = ["en"] + release_languages
else:
build_languages = ["en"] + languages

# Get the latest Git tag - there might be a prettier way to do this but...
try:
release_value = (
Expand Down Expand Up @@ -71,7 +94,11 @@
{"href": "https://www.pyopensci.org/images/favicon.ico"},
]

# Link to our repo for easy PR/ editing
html_baseurl = "https://www.pyopensci.org/python-package-guide/"
if not sphinx_env == "production":
# for links in language selector when developing locally
html_baseurl = "/"

html_theme_options = {
"announcement": "<p><a href='https://www.pyopensci.org/about-peer-review/index.html'>We run peer review of scientific Python software. Learn more.</a></p>",
# "navbar_center": ["nav"], this can be a way to override the default navigation structure
Expand Down Expand Up @@ -111,12 +138,16 @@
"github_url": "https://github.com/pyopensci/python-package-guide",
"footer_start": ["code_of_conduct", "copyright"],
"footer_end": [],
"navbar_persistent": ["language-selector", "search-button"]
}

html_context = {
"github_user": "pyopensci",
"github_repo": "python-package-guide",
"github_version": "main",
"language": language,
"languages": build_languages,
"baseurl": html_baseurl,
}

# Add any paths that contain templates here, relative to this directory.
Expand All @@ -140,7 +171,7 @@
]

# For sitemap generation
html_baseurl = "https://www.pyopensci.org/python-package-guide/"

sitemap_url_scheme = "{link}"

# -- Options for HTML output -------------------------------------------------
Expand All @@ -152,7 +183,7 @@
html_static_path = ["_static"]
html_css_files = ["pyos.css"]
html_title = "Python Packaging Guide"
html_js_files = ["matomo.js"]
html_js_files = ["matomo.js", "language_select.js"]


# Social cards
Expand Down
96 changes: 79 additions & 17 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
import pathlib
import shutil
import nox
import sys
import subprocess

# for some reason necessary to correctly import conf from cwd
sys.path.insert(0, str(pathlib.Path(__file__).parent.absolute()))
import conf

nox.options.reuse_existing_virtualenvs = True

Expand Down Expand Up @@ -43,19 +49,22 @@
## Localization options (translations)

# List of languages for which locales will be generated in (/locales/<lang>)
LANGUAGES = ["es", "ja"]
LANGUAGES = conf.languages

# List of languages that should be built when releasing the guide (docs or docs-test sessions)
RELEASE_LANGUAGES = []
RELEASE_LANGUAGES = conf.release_languages

# allowable values of `SPHINX_ENV`
SPHINX_ENVS = ('production', 'development')

@nox.session
def docs(session):
"""Build the packaging guide."""
session.install("-e", ".")
sphinx_env = _sphinx_env(session)
session.run(SPHINX_BUILD, *BUILD_PARAMETERS, SOURCE_DIR, OUTPUT_DIR, *session.posargs)
# When building the guide, also build the translations in RELEASE_LANGUAGES
session.notify("build-translations", ['release-build'])
session.notify("build-translations", [sphinx_env])


@nox.session(name="docs-test")
Expand All @@ -66,10 +75,17 @@ def docs_test(session):
Note: this is the session used in CI/CD to release the guide.
"""
session.install("-e", ".")
session.run(SPHINX_BUILD, *BUILD_PARAMETERS, *TEST_PARAMETERS, SOURCE_DIR, OUTPUT_DIR, *session.posargs)
session.run(SPHINX_BUILD, *BUILD_PARAMETERS, *TEST_PARAMETERS, SOURCE_DIR, OUTPUT_DIR, *session.posargs,
env={'SPHINX_ENV': 'production'})
# When building the guide with additional parameters, also build the translations in RELEASE_LANGUAGES
# with those same parameters.
session.notify("build-translations", ['release-build', *TEST_PARAMETERS])
session.notify("build-translations", ['production', *TEST_PARAMETERS])

def _autobuild_cmd(posargs: list[str], output_dir = OUTPUT_DIR) -> list[str]:
cmd = [SPHINX_AUTO_BUILD, *BUILD_PARAMETERS, str(SOURCE_DIR), str(output_dir), *posargs]
for folder in AUTOBUILD_IGNORE:
cmd.extend(["--ignore", f"*/{folder}/*"])
return cmd


@nox.session(name="docs-live")
Expand All @@ -88,13 +104,11 @@ def docs_live(session):
so they don't need to remember the specific sphinx-build parameters to build a different language.
"""
session.install("-e", ".")
cmd = [SPHINX_AUTO_BUILD, *BUILD_PARAMETERS, SOURCE_DIR, OUTPUT_DIR, *session.posargs]
for folder in AUTOBUILD_IGNORE:
cmd.extend(["--ignore", f"*/{folder}/*"])
cmd = _autobuild_cmd(session.posargs)
# This part was commented in the previous version of the nox file, keeping the same here
# for folder in AUTOBUILD_INCLUDE:
# cmd.extend(["--watch", folder])
session.run(*cmd)
session.run(*cmd, env={'SPHINX_ENV': "development"})


@nox.session(name="docs-live-lang")
Expand Down Expand Up @@ -128,6 +142,37 @@ def docs_live_lang(session):
f"where LANG is one of: {LANGUAGES}"
)

@nox.session(name="docs-live-langs")
def docs_live_langs(session):
"""
Like docs-live but build all languages simultaneously

Requires concurrently to run (npm install -g concurrently)
"""
try:
subprocess.check_call(['concurrently'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError:
# handle errors in the called executable
# (aka, was found)
pass
except OSError:
session.error('docs-live-langs requires concurrently (npm install -g concurrently)')

session.install("-e", ".")

cmds = ['"' + " ".join(["SPHINX_ENV=development"] + _autobuild_cmd(session.posargs) + ['--open-browser']) + '"']
for language in LANGUAGES:
cmds.append(
'"' + " ".join(
[f"SPHINX_LANG={language}", "SPHINX_ENV=development"] +
_autobuild_cmd(
session.posargs + ["-D", f"language={language}"],
output_dir=OUTPUT_DIR / language
) + ["--port=0"]
) + '"'
)
cmd = ['concurrently', '--kill-others', '-n', ','.join(["en"] + LANGUAGES), '-c', 'auto', *cmds]
session.run(*cmd)

@nox.session(name="docs-clean")
def clean_dir(session):
Expand Down Expand Up @@ -169,6 +214,9 @@ def build_languages(session):
"""
if not session.posargs:
session.error("Please provide the list of languages to build the translation for")

sphinx_env = _sphinx_env(session)

languages_to_build = session.posargs.pop(0)

session.install("-e", ".")
Expand All @@ -177,7 +225,12 @@ def build_languages(session):
session.warn(f"Language [{lang}] is not available for translation")
continue
session.log(f"Building [{lang}] guide")
session.run(SPHINX_BUILD, *BUILD_PARAMETERS, "-D", f"language={lang}", ".", OUTPUT_DIR / lang, *session.posargs)
if lang == 'en':
out_dir = OUTPUT_DIR
else:
out_dir = OUTPUT_DIR / lang
session.run(SPHINX_BUILD, *BUILD_PARAMETERS, "-D", f"language={lang}", ".", out_dir, *session.posargs,
env={"SPHINX_LANG": lang, "SPHINX_ENV": sphinx_env})


@nox.session(name="build-translations")
Expand All @@ -189,21 +242,19 @@ def build_translations(session):
It is also called by the docs and docs-test sessions with 'release-build' as the first positional
argument, to build only the translations defined in RELEASE_LANGUAGES.
"""
release_build = False
if session.posargs and session.posargs[0] == 'release-build':
session.posargs.pop(0)
release_build = True
sphinx_env = _sphinx_env(session)

# if running from the docs or docs-test sessions, build only release languages
BUILD_LANGUAGES = RELEASE_LANGUAGES if release_build else LANGUAGES
BUILD_LANGUAGES = RELEASE_LANGUAGES if sphinx_env == "production" else LANGUAGES
# only build languages that have a locale folder
BUILD_LANGUAGES = [lang for lang in BUILD_LANGUAGES if (TRANSLATION_LOCALES_DIR / lang).exists()]
session.log(f"Declared languages: {LANGUAGES}")
session.log(f"Release languages: {RELEASE_LANGUAGES}")
session.log(f"Building languages{' for release' if release_build else ''}: {BUILD_LANGUAGES}")
session.log(f"Building languages{' for release' if sphinx_env == 'production' else ''}: {BUILD_LANGUAGES}")
if not BUILD_LANGUAGES:
session.warn("No translations to build")
else:
session.notify("build-languages", [BUILD_LANGUAGES, *session.posargs])
session.notify("build-languages", [sphinx_env, BUILD_LANGUAGES, *session.posargs])


@nox.session(name="build-translations-test")
Expand All @@ -215,3 +266,14 @@ def build_translations_test(session):
in the same way docs-test does for the English version.
"""
session.notify("build-translations", [*TEST_PARAMETERS])


def _sphinx_env(session) -> str:
"""
Get the sphinx env, from the first positional argument if present or from the
``SPHINX_ENV`` environment variable, defaulting to "development"
"""
if session.posargs and session.posargs[0] in SPHINX_ENVS:
return session.posargs.pop(0)
else:
return os.environ.get('SPHINX_ENV', 'development')
Loading