Add dropdown for selecting languages

Change-Id: If40c46320cf3db24b4b94fb9982f5fa589a0011d
This commit is contained in:
Ziwei Wang 2023-08-04 17:30:14 -04:00
parent 471f040dce
commit 8ce181bfbf
3 changed files with 370 additions and 1 deletions

View File

@ -0,0 +1,66 @@
<select id="language-selector" onchange="handleChangeLanguage();" style="width: 100%; position: absolute;">
{% for lang, lang_url in languages %}
<option value="{{ lang_url }}">
{{ lang }}
</option>
{% endfor %}
</select>
<script>
// Should be consistent with the list `languages` in conf.py
let available_lang_code = ['/ar/', '/bg/', '/bn/', '/ca/', '/da/', '/de/', '/el/', '/en_US/', '/eo/', '/es/', '/et/', '/eu/', '/fa/', '/fi/', '/fr/', '/he/', '/hi/', '/hi_IN/', '/hr/', '/hu/', '/id/', '/it/', '/ja/', '/ko/', '/lt/', '/ne/', '/nl/', '/pl/', '/pt/', '/pt_BR/', '/pt_PT/', '/ro/', '/ru/', '/si/', '/sk/', '/sl/', '/sr/', '/sv/', '/ta/', '/te/', '/tr/', '/vi/', '/zh_CN/', '/zh_TW/'];
let selector = document.getElementById('language-selector');
let site_domain = /docs\.jami\.net/;
function handleChangeLanguage(e) {
let target_lang_code = selector.value;
let current_url = window.location.href;
let found_site_domain = current_url.match(site_domain);
// Replace lang code
window.location.href = current_url.slice(0, found_site_domain.index + found_site_domain[0].length) + '/' + target_lang_code + '/' + current_url.slice(found_site_domain.index + found_site_domain[0].length + getCurrentLangCode().length + 2);
}
//Get current lang code from the URL
function getCurrentLangCode() {
let current_url = window.location.href;
let current_lang_code = '';
for (let i = 0; i < available_lang_code.length; ++i) {
if (current_url.indexOf(available_lang_code[i]) !== -1) {
// exclude the leading and trailing '/''
current_lang_code = available_lang_code[i].slice(1, -1);
}
}
return current_lang_code;
}
// Use system language if avaiable lang codes not found in the URL
document.addEventListener('DOMContentLoaded', function () {
let current_lang_code = getCurrentLangCode();
//Most modern browsers use hypen (IETF language tags) as a separator. Transifex uses underscore. Underscores are preferred for the convenience of working with Transifex.
let browserLang = navigator.language.replace('-', '_') || 'en_US';
let current_url = window.location.href;
let found_site_domain = current_url.match(site_domain);
if(current_lang_code === ''){
let lang = browserLang;
// if browser language is not supported, use en_US
if(available_lang_code.indexOf(`/${browserLang}/`) === -1){
lang = 'en_US';
}
window.location.href = current_url.slice(0, found_site_domain.index + found_site_domain[0].length) + '/' + lang + current_url.slice(found_site_domain.index + found_site_domain[0].length);
}
})
//Display current language in language selector on page load
document.addEventListener('DOMContentLoaded', function () {
let current_lang_code = getCurrentLangCode();
document.querySelectorAll(`option[value='${current_lang_code}']`).forEach(option => {
option.setAttribute('selected', '');
})
})
</script>

252
_templates/layout.html Normal file
View File

@ -0,0 +1,252 @@
<!-- This file is identical to layout.html in sphinx_rtd_theme. The only difference is the language selector added at the bottom of the nav with class 'wy-nav-side' -->
{# TEMPLATE VAR SETTINGS #}
{%- set url_root = pathto('', 1) %}
{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
{%- if not embedded and docstitle %}
{%- set titlesuffix = " &mdash; "|safe + docstitle|e %}
{%- else %}
{%- set titlesuffix = "" %}
{%- endif %}
{%- set lang_attr = 'en' if language == None else (language | replace('_', '-')) %}
{%- set sphinx_writer = 'writer-html5' if html5_doctype else 'writer-html4' -%}
{# Build sphinx_version_info tuple from sphinx_version string in pure Jinja #}
{%- set (_ver_major, _ver_minor) = (sphinx_version.split('.') | list)[:2] | map('int') -%}
{%- set sphinx_version_info = (_ver_major, _ver_minor, -1) -%}
<!DOCTYPE html>
<html class="{{ sphinx_writer }}" lang="{{ lang_attr }}" >
<head>
<meta charset="utf-8" />
{{- metatags }}
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{%- block htmltitle %}
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
{%- endblock -%}
{#- CSS #}
{%- if sphinx_version_info < (4, 0) -%}
<link rel="stylesheet" href="{{ pathto('_static/' + style, 1) }}" type="text/css" />
<link rel="stylesheet" href="{{ pathto('_static/pygments.css', 1) }}" type="text/css" />
{%- endif %}
{%- for css in css_files %}
{%- if css|attr("rel") %}
<link rel="{{ css.rel }}" href="{{ pathto(css.filename, 1) }}" type="text/css"{% if css.title is not none %} title="{{ css.title }}"{% endif %} />
{%- else %}
<link rel="stylesheet" href="{{ pathto(css, 1) }}" type="text/css" />
{%- endif %}
{%- endfor %}
{%- for cssfile in extra_css_files %}
<link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css" />
{%- endfor -%}
{#- FAVICON
favicon_url is the only context var necessary since Sphinx 4.
In Sphinx<4, we use favicon but need to prepend path info.
#}
{%- set _favicon_url = favicon_url | default(pathto('_static/' + (favicon or ""), 1)) %}
{%- if favicon_url or favicon %}
<link rel="shortcut icon" href="{{ _favicon_url }}"/>
{%- endif %}
{#- CANONICAL URL (deprecated) #}
{%- if theme_canonical_url and not pageurl %}
<link rel="canonical" href="{{ theme_canonical_url }}{{ pagename }}.html"/>
{%- endif -%}
{#- CANONICAL URL #}
{%- if pageurl %}
<link rel="canonical" href="{{ pageurl|e }}" />
{%- endif -%}
{#- JAVASCRIPTS #}
{%- block scripts %}
<!--[if lt IE 9]>
<script src="{{ pathto('_static/js/html5shiv.min.js', 1) }}"></script>
<![endif]-->
{%- if not embedded %}
{# XXX Sphinx 1.8.0 made this an external js-file, quick fix until we refactor the template to inherert more blocks directly from sphinx #}
{%- if sphinx_version_info >= (1, 8) -%}
{%- if sphinx_version_info < (4, 0) -%}
<script id="documentation_options" data-url_root="{{ url_root }}" src="{{ pathto('_static/documentation_options.js', 1) }}"></script>
{%- endif -%}
{%- for scriptfile in script_files %}
{{ js_tag(scriptfile) }}
{%- endfor %}
{%- else %}
<script>
var DOCUMENTATION_OPTIONS = {
URL_ROOT:'{{ url_root }}',
VERSION:'{{ release|e }}',
LANGUAGE:'{{ language }}',
COLLAPSE_INDEX:false,
FILE_SUFFIX:'{{ '' if no_search_suffix else file_suffix }}',
HAS_SOURCE: {{ has_source|lower }},
SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}'
};
</script>
{%- for scriptfile in script_files %}
<script src="{{ pathto(scriptfile, 1) }}"></script>
{%- endfor %}
{%- endif %}
<script src="{{ pathto('_static/js/theme.js', 1) }}"></script>
{#- OPENSEARCH #}
{%- if use_opensearch %}
<link rel="search" type="application/opensearchdescription+xml"
title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}"
href="{{ pathto('_static/opensearch.xml', 1) }}"/>
{%- endif %}
{%- endif %}
{%- endblock %}
{%- block linktags %}
{%- if hasdoc('about') %}
<link rel="author" title="{{ _('About these documents') }}" href="{{ pathto('about') }}" />
{%- endif %}
{%- if hasdoc('genindex') %}
<link rel="index" title="{{ _('Index') }}" href="{{ pathto('genindex') }}" />
{%- endif %}
{%- if hasdoc('search') %}
<link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}" />
{%- endif %}
{%- if hasdoc('copyright') %}
<link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}" />
{%- endif %}
{%- if next %}
<link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}" />
{%- endif %}
{%- if prev %}
<link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}" />
{%- endif %}
{%- endblock %}
{%- block extrahead %} {% endblock %}
</head>
<body class="wy-body-for-nav">
{%- block extrabody %} {% endblock %}
<div class="wy-grid-for-nav">
{#- SIDE NAV, TOGGLES ON MOBILE #}
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" {% if theme_style_nav_header_background %} style="background: {{theme_style_nav_header_background}}" {% endif %}>
{%- block sidebartitle %}
{# the logo helper function was removed in Sphinx 6 and deprecated since Sphinx 4 #}
{# the master_doc variable was renamed to root_doc in Sphinx 4 (master_doc still exists in later Sphinx versions) #}
{%- set _logo_url = logo_url|default(pathto('_static/' + (logo or ""), 1)) %}
{%- set _root_doc = root_doc|default(master_doc) %}
<a href="{{ pathto(_root_doc) }}"{% if not theme_logo_only %} class="icon icon-home"{% endif %}>
{% if not theme_logo_only %}{{ project }}{% endif %}
{%- if logo or logo_url %}
<img src="{{ _logo_url }}" class="logo" alt="{{ _('Logo') }}"/>
{%- endif %}
</a>
{%- if theme_display_version %}
{%- set nav_version = version %}
{%- if READTHEDOCS and current_version %}
{%- set nav_version = current_version %}
{%- endif %}
{%- if nav_version %}
<div class="version">
{{ nav_version }}
</div>
{%- endif %}
{%- endif %}
{%- include "searchbox.html" %}
{%- endblock %}
</div>
{%- block navigation %}
{#- Translators: This is an ARIA section label for the main navigation menu -#}
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="{{ _('Navigation menu') }}">
{%- block menu %}
{%- set toctree = toctree(maxdepth=theme_navigation_depth|int,
collapse=theme_collapse_navigation|tobool,
includehidden=theme_includehidden|tobool,
titles_only=theme_titles_only|tobool) %}
{%- if toctree %}
{{ toctree }}
{%- else %}
<!-- Local TOC -->
<div class="local-toc">{{ toc }}</div>
{%- endif %}
{%- endblock %}
</div>
{%- endblock %}
</div>
{% include "language-selector.html" %}
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
{#- MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #}
{#- Translators: This is an ARIA section label for the navigation menu that is visible when viewing the page on mobile devices -#}
<nav class="wy-nav-top" aria-label="{{ _('Mobile navigation menu') }}" {% if theme_style_nav_header_background %} style="background: {{theme_style_nav_header_background}}" {% endif %}>
{%- block mobile_nav %}
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="{{ pathto(master_doc) }}">{{ project }}</a>
{%- endblock %}
</nav>
<div class="wy-nav-content">
{%- block content %}
{%- if theme_style_external_links|tobool %}
<div class="rst-content style-external-links">
{%- else %}
<div class="rst-content">
{%- endif %}
{% include "breadcrumbs.html" %}
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
{%- block document %}
<div itemprop="articleBody">
{% block body %}{% endblock %}
</div>
{%- if self.comments()|trim %}
<div class="articleComments">
{%- block comments %}{% endblock %}
</div>
{%- endif%}
</div>
{%- endblock %}
{% include "footer.html" %}
</div>
{%- endblock %}
</div>
</section>
</div>
{% include "versions.html" -%}
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable({{ 'true' if theme_sticky_navigation|tobool else 'false' }});
});
</script>
{#- Do not conflict with RTD insertion of analytics script #}
{%- if not READTHEDOCS %}
{%- if theme_analytics_id %}
<!-- Theme Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id={{ theme_analytics_id }}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{{ theme_analytics_id }}', {
'anonymize_ip': {{ 'true' if theme_analytics_anonymize_ip|tobool else 'false' }},
});
</script>
{%- endif %}
{%- endif %}
{%- block footer %} {% endblock %}
</body>
</html>

53
conf.py
View File

@ -75,7 +75,7 @@ autosectionlabel_maxdepth = 4
templates_path = ['_templates']
exclude_patterns = ['_build']
exclude_patterns = ['_build', '*env*', 'README.md']
html_theme = 'sphinx_rtd_theme'
@ -93,3 +93,54 @@ html_static_path = ['_static']
# internationalization / translations
locale_dirs = ['locales']
gettext_compact = True
languages = {
'العربية': 'ar',
'Български': 'bg',
'বাংলা': 'bn',
'Català': 'ca',
'Dansk': 'da',
'Deutsch': 'de',
'Ελληνικά': 'el',
'English (US)': 'en_US',
'Esperanto': 'eo',
'Español': 'es',
'Eesti': 'et',
'Euskara': 'eu',
'فارسی': 'fa',
'Suomi': 'fi',
'Français': 'fr',
'עברית': 'he',
'हिन्दी': 'hi',
'हिन्दी (भारत)': 'hi_IN',
'Hrvatski': 'hr',
'magyar': 'hu',
'Indonesia': 'id',
'Italiano': 'it',
'日本語': 'ja',
'한국어': 'ko',
'Lietuvių': 'lt',
'नेपाली': 'ne',
'Nederlands': 'nl',
'Polski': 'pl',
'Português': 'pt',
'Português (Brasil)': 'pt_BR',
'Português (Portugal)': 'pt_PT',
'Română': 'ro',
'Русский': 'ru',
#'සිංහල': 'si', # TODO: Enable this language when translations are provided
'Slovenčina': 'sk',
'Slovenščina': 'sl',
'Српски': 'sr',
'Svenska': 'sv',
'தமிழ்': 'ta',
'తెలుగు': 'te',
'Türkçe': 'tr',
'Tiếng Việt': 'vi',
'中文简体': 'zh_CN',
'中文繁體': 'zh_TW',
}
html_context = {
'languages' : list(languages.items()),
}