🌐 feat(i18n): overhaul translation system & add languages (#145)

Revamp the existing translation system, simplifying
management and adding several new languages. The new system reads from
TOML files in the `/i18n` directory and improves template structures.
It also enhances customisation options and robustness by providing
fallbacks and modularity.

- Implement a new, streamlined translation macro.
- Load translations from `/i18n` TOML files.
- Remove redundant configuration requirements.
- Refactor templates to align with new i18n system.
- Add support for Hindi, Japanese, Russian, Portuguese, Chinese,
  Italian, German, Ukranian, Korean, and French languages.
- Credit Thomas Weitzel (@thomasweitzel) for inspiration.
This commit is contained in:
Óscar
2023-09-12 18:58:58 +02:00
committed by GitHub
parent 327545f2d5
commit 32a2d5094b
46 changed files with 1534 additions and 483 deletions

View File

@@ -1,19 +1,34 @@
{% extends "page.html" %}
{% macro display_404_message(language_name) %}
{%- set language_strings = load_data(path="i18n/" ~ language_name ~ '.toml', required=false) -%}
{%- if not language_strings -%}
{%- set language_strings = load_data(path="themes/tabi/i18n/" ~ language_name ~ '.toml', required=false) -%}
{%- endif -%}
<p>{{ macros_translate::translate(key="page_missing", default="The page you've requested seems to be missing", force_lang=language_name, language_strings=language_strings) }}
{%- if config.languages | length > 0 -%}
&nbsp;{{ macros_translate::translate(key="translation_missing", default="or hasn't been translated into your language yet", force_lang=language_name, language_strings=language_strings) }}{{ macros_translate::translate(key="full_stop", default=".", force_lang=language_name, language_strings=language_strings) }}
{%- else %}.
{%- endif %}
{{ macros_translate::translate(key="check_url", default="Check the URL for errors or", force_lang=language_name, language_strings=language_strings) }}
<a href="{{ config.base_url }}{% if language_name != config.default_language %}/{{ language_name }}{% endif %}/">
{{ macros_translate::translate(key="go_home", default="go back to the homepage", force_lang=language_name, language_strings=language_strings) }}</a>{{ macros_translate::translate(key="full_stop", default=".", force_lang=language_name, language_strings=language_strings) }}</p>
{% endmacro %}
{% block main_content %}
<main class="centered-text">
{{ macros_page_header::page_header(title="404")}}
<div class="subheader">not found</div>
<div class="subheader">{{ macros_translate::translate(key="not_found") }}</div>
{# Default English message #}
<p>The page you requested doesn't seem to exist{%- if config.languages | length > 0 %} or has not been translated to your language yet{%- endif -%}. Check the URL for errors, or <a href="{{ config.base_url }}">return to the homepage</a>.</p>
{# 404 message for base language #}
{{ self::display_404_message(language_name=config.default_language, is_multilingual=is_multilingual) }}
{# Iterate through each language and display the localised 404 message along with a "Go Home" link #}
{#- Iterate through each extra language, to display the localised 404 message -#}
{%- for language_name, language in config.languages -%}
{%- if language_name != config.default_language -%}
<p>{{ macros_translate::translate(key="404_error", force_lang=language_name) }} <a href="{{ config.base_url }}/{{ language_name }}/">{{ macros_translate::translate(key="go_home", force_lang=language_name) }}</a>.</p>
{%- if language_name == config.default_language -%}
{%- continue -%} {#- We've already displayed the 404 message for the base language -#}
{%- endif -%}
{{ self::display_404_message(language_name=language_name, is_multilingual=is_multilingual) }}
{%- endfor -%}
</main>
{% endblock main_content %}

View File

@@ -5,7 +5,7 @@
{{ macros_page_header::page_header(title=section.title) }}
{# Set locale for date #}
{% set date_locale = macros_translate::translate(key="date_locale", default="en_GB") %}
{% set date_locale = macros_translate::translate(key="date_locale", default="en_GB", language_strings=language_strings) %}
<div class="archive">
<ul class="list-with-title">

View File

@@ -1,40 +1,25 @@
{% import "macros/translate.html" as macros_translate %}
{%- import "macros/translate.html" as macros_translate -%}
{#- Load the internationalisation data -#}
{%- set language_strings = load_data(path="i18n/" ~ lang ~ '.toml', required=false) -%}
{%- if not language_strings -%}
{%- set language_strings = load_data(path="themes/tabi/i18n/" ~ lang ~ ".toml", required=false) -%}
{%- endif -%}
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="{{ get_url(path='/feed_style.xsl', trailing_slash=false) | safe }}" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:base="http://purl.org/atompub/base/1.0/" xml:lang="{{ lang }}" xml:base="{{ config.base_url }}">
{# This section stores the strings/translations for the stylised feed. If the translation is not found, the default English text will be used. #}
<str:translations xmlns:str="https://github.com/welpo/tabi">
<str:this_is_a_web_feed>
{{- macros_translate::translate(key="this_is_a_web_feed", default="This is a web feed") -}}
</str:this_is_a_web_feed>
<str:also_known_as_an_Atom_feed>
{{- macros_translate::translate(key="also_known_as_an_Atom_feed", default="also known as an Atom feed") -}}
</str:also_known_as_an_Atom_feed>
<str:subscribe>
{{- macros_translate::translate(key="subscribe", default="Subscribe") -}}
</str:subscribe>
<str:by_copying_the_URL_from_the_address_bar_into_your_newsreader>
{{- macros_translate::translate(key="by_copying_the_URL_from_the_address_bar_into_your_newsreader", default="by copying the URL from the address bar into your newsreader") -}}
</str:by_copying_the_URL_from_the_address_bar_into_your_newsreader>
<str:visit>
{{- macros_translate::translate(key="visit", default="Visit") -}}
</str:visit>
<str:to_learn_more_and_get_started>
{{- macros_translate::translate(key="to_learn_more_and_get_started", default="to learn more and get started") -}}
</str:to_learn_more_and_get_started>
<str:it_s_free>
{{- macros_translate::translate(key="it_s_free", default="It's free") -}}
</str:it_s_free>
<str:website>
{{- macros_translate::translate(key="website", default="website") -}}
</str:website>
<str:about_feeds>
{{- macros_translate::translate(key="about_feeds", default="This is a web feed, also known as an Atom feed. Subscribe by copying the URL from the address bar into your newsreader", language_strings=language_strings) -}}
</str:about_feeds>
<str:visit_the_site>
{{- macros_translate::translate(key="visit_the_site", default="Visita la web", language_strings=language_strings) -}}
</str:visit_the_site>
<str:recent_posts>
{{- macros_translate::translate(key="recent_posts", default="Recent posts") -}}
{{- macros_translate::translate(key="recent_posts", default="Publicaciones recientes", language_strings=language_strings) -}}
</str:recent_posts>
</str:translations>
{# Load extra CSS (skin) if set in config.toml #}
{#- Load extra CSS (skin) if set in config.toml -#}
{%- if config.extra.skin and config.extra.skin != "teal" -%}
<link rel="extra-stylesheet" href="{{ get_url(path='skins/' ~ config.extra.skin ~ '.css', cachebust=true) | safe }}" />
{%- endif -%}

View File

@@ -1,6 +1,5 @@
{% import "macros/add_comments.html" as macros_add_comments %}
{% import "macros/cards_pages.html" as macros_cards_pages %}
{% import "macros/content.html" as macros_content %}
{% import "macros/create_history_url.html" as macros_create_history_url %}
{% import "macros/format_date.html" as macros_format_date %}
{% import "macros/list_posts.html" as macros_list_posts %}
@@ -13,6 +12,15 @@
{% import "macros/table_of_contents.html" as macros_toc %}
{% import "macros/translate.html" as macros_translate %}
{# Load the internationalisation data for the current language from
the .toml files in the user's '/i18n' folder, falling back to the theme's.
This variable will hold all the text strings for the language #}
{%- set language_strings = load_data(path="i18n/" ~ lang ~ '.toml', required=false) -%}
{%- if not language_strings -%}
{%- set language_strings = load_data(path="themes/tabi/i18n/" ~ lang ~ ".toml", required=false) -%}
{%- endif -%}
<!DOCTYPE html>
<html lang="{{ lang }}" {% if config.extra.default_theme -%}
data-theme="{{config.extra.default_theme}}"

View File

@@ -18,6 +18,6 @@
</main>
{% if paginator %}
{{ macros_paginate::paginate() }}
{{ macros_paginate::paginate(language_strings=language_strings) }}
{% endif %}
{% endblock main_content %}

View File

@@ -86,7 +86,7 @@
{% if automatic_loading %}
<script src="{{ get_url(path='js/' ~ comment_system ~ '.min.js', trailing_slash=false) | safe }}" async></script>
{% else %}
<button id="load-comments" class="load-comments-button" data-script-src="{{ get_url(path='js/' ~ comment_system ~ '.min.js', trailing_slash=false) | safe }}">{{ macros_translate::translate(key="load_comments", default="Load comments") }}</button>
<button id="load-comments" class="load-comments-button" data-script-src="{{ get_url(path='js/' ~ comment_system ~ '.min.js', trailing_slash=false) | safe }}">{{ macros_translate::translate(key="load_comments", default="Load comments", language_strings=language_strings) }}</button>
<script src="{{ get_url(path='js/loadComments.min.js', trailing_slash=false) | safe }}" async></script>
{% endif %}

View File

@@ -1,197 +0,0 @@
{% macro content(page) %}
{%- set separator = config.extra.separator | default(value="•") -%}
{%- set rel_attributes = macros_rel_attributes::rel_attributes() | trim -%}
{%- if config.markdown.external_links_target_blank -%}
{%- set blank_target = "target=_blank" -%}
{%- else -%}
{%- set blank_target = "" -%}
{%- endif -%}
{# Debugging #}
{# {% set last_ancestor = page.ancestors | slice(start=-1) %}
{% set current_section = get_section(path=last_ancestor.0) %}
{% set settings_to_test = [
"footnote_backlinks",
"katex",
"quick_navigation_buttons",
"show_reading_time",
"show_remote_changes",
"toc",
] %}
<table>
<thead>
<tr>
<th>setting</th>
<th>page</th>
<th>section</th>
<th>config</th>
<th>macro output</th>
</tr>
</thead>
<tbody>
{% for setting in settings_to_test %}
<tr>
<td><code>{{ setting }}</code></td>
<td>{{ page.extra[setting] | default(value="⬛") }}</td>
<td>{{ current_section.extra[setting] | default(value="⬛") }}</td>
<td>{{ config.extra[setting] | default(value="⬛") }}</td>
<td>{{ macros_settings::evaluate_setting_priority(setting=setting, page=page) }}</td>
</tr>
{% endfor %}
</tbody>
</table> #}
{# End debugging #}
<main>
<article>
<h1 class="article-title">
{{ page.title }}
</h1>
<ul class="meta">
{% if page.draft %}
<li class="draft-label">{{ macros_translate::translate(key="draft", default="DRAFT") }}</li>
{% endif %}
{% if page.date %}
<li>{{ macros_format_date::format_date(date=page.date, short=true) }}</li>
{% endif %}
{# page settings override config settings #}
{% if macros_settings::evaluate_setting_priority(setting="show_reading_time", page=page, default_global_value=true) == "true" %}
{{ separator }} <li title="{{ page.word_count }} {{ macros_translate::translate(key="words", default="words") }}">{{ page.reading_time }}&nbsp;{{ macros_translate::translate(key="min_read", default="min read") }}</li>
{% endif %}
{%- if page.taxonomies and page.taxonomies.tags -%}
{{ separator }}&nbsp;<li>{{- macros_translate::translate(key="tags", default="tags") | capitalize -}}:&nbsp;</li>
{%- for tag in page.taxonomies.tags -%}
<li><a href={{ get_taxonomy_url(kind='tags', name=tag, lang=lang) | safe }}>{{ tag }}</a>
{%- if not loop.last -%}
,&nbsp;
{%- endif -%}
</li>
{%- endfor -%}
{%- endif -%}
{% if page.updated %}
</ul><ul class="meta last-updated"><li>{{ macros_translate::translate(key="last_updated_on", default="Last updated on") }} {{ macros_format_date::format_date(date=page.updated, short=true) }}</li>
{# Show link to remote changes if enabled #}
{% if config.extra.remote_repository_url and macros_settings::evaluate_setting_priority(setting="show_remote_changes", page=page, default_global_value=true) == "true" %}
{{ separator }}
<li><a href="{{ macros_create_history_url::create_history_url(relative_path=page.relative_path) }}" {{ blank_target }} rel="{{ rel_attributes }}">{{ macros_translate::translate(key="see_changes", default="See changes") }}<small></small></a></li>
{% endif %}
{% endif %}
</ul>
{% if page.extra.tldr %}
<div class="tldr">
<h3>TL;DR:</h3>
<p>{{ page.extra.tldr }}</p>
</div>
{% endif %}
{# Optional table of contents below the header #}
{% if page.toc and macros_settings::evaluate_setting_priority(setting="toc", page=page, default_global_value=false) == "true" %}
{{ macros_toc::toc(page=page, header=true) }}
{% endif %}
<section class="body">
{# The replace pattern is used to enable arbitrary locations for the Table of Contents #}
{# This is Philipp Oppermann's workaround: https://github.com/getzola/zola/issues/584#issuecomment-474329637 #}
{{ page.content | replace(from="<!-- toc -->", to=macros_toc::toc(page=page, header=false)) | safe }}
</section>
{# Check if comments are enabled #}
{% set giscus_enabled = config.extra.giscus.enabled_for_all_posts or page.extra.giscus %}
{% set utterances_enabled = config.extra.utterances.enabled_for_all_posts or page.extra.utterances %}
{% set hyvortalk_enabled = config.extra.hyvortalk.enabled_for_all_posts or page.extra.hyvortalk %}
{% set isso_enabled = config.extra.isso.enabled_for_all_posts or page.extra.isso %}
{# Ensure only one comment system is enabled #}
{# Counter for enabled comment systems #}
{% set enabled_systems = 0 %}
{# Check and count the enabled comment systems #}
{% if giscus_enabled %}
{% set comment_system = "giscus" %}
{% set enabled_systems = enabled_systems + 1 %}
{% endif %}
{% if utterances_enabled %}
{% set comment_system = "utterances" %}
{% set enabled_systems = enabled_systems + 1 %}
{% endif %}
{% if hyvortalk_enabled %}
{% set comment_system = "hyvortalk" %}
{% set enabled_systems = enabled_systems + 1 %}
{% endif %}
{% if isso_enabled %}
{% set comment_system = "isso" %}
{% set enabled_systems = enabled_systems + 1 %}
{% endif %}
{# Ensure only one comment system is enabled #}
{% if enabled_systems > 1 %}
{{ throw(message="ERROR: Multiple comment systems have been enabled for the same page. Check your config.toml and individual page settings to ensure only one comment system is activated at a time.") }}
{% endif %}
{% if comment_system %}
{% set automatic_loading = config.extra[comment_system].automatic_loading %}
{{ macros_add_comments::add_comments(comment_system=comment_system, automatic_loading=automatic_loading) }}
{% endif %}
</article>
</main>
{# Quick navigation buttons #}
{% if macros_settings::evaluate_setting_priority(setting="quick_navigation_buttons", page=page, default_global_value=false) == "true" %}
<div id="button-container">
{# Button to go show a floating Table of Contents #}
{% if page.toc %}
<div id="toc-floating-container">
<input type="checkbox" id="toc-toggle" class="toggle"/>
<label for="toc-toggle" class="overlay"></label>
<label for="toc-toggle" id="toc-button" class="button" title="Toggle Table of Contents" aria-label="toggle Table of Contents">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M414.82-193.094q-18.044 0-30.497-12.32-12.453-12.319-12.453-30.036t12.453-30.086q12.453-12.37 30.497-12.37h392.767q17.237 0 29.927 12.487 12.69 12.486 12.69 30.203 0 17.716-12.69 29.919t-29.927 12.203H414.82Zm0-244.833q-18.044 0-30.497-12.487Q371.87-462.9 371.87-480.45t12.453-29.92q12.453-12.369 30.497-12.369h392.767q17.237 0 29.927 12.511 12.69 12.512 12.69 29.845 0 17.716-12.69 30.086-12.69 12.37-29.927 12.37H414.82Zm0-245.167q-18.044 0-30.497-12.32t-12.453-30.037q0-17.716 12.453-30.086 12.453-12.369 30.497-12.369h392.767q17.237 0 29.927 12.486 12.69 12.487 12.69 30.203 0 17.717-12.69 29.92-12.69 12.203-29.927 12.203H414.82ZM189.379-156.681q-32.652 0-55.878-22.829t-23.226-55.731q0-32.549 23.15-55.647 23.151-23.097 55.95-23.097 32.799 0 55.313 23.484 22.515 23.484 22.515 56.246 0 32.212-22.861 54.893-22.861 22.681-54.963 22.681Zm0-245.167q-32.652 0-55.878-23.134-23.226-23.135-23.226-55.623 0-32.487 23.467-55.517t56.12-23.03q32.102 0 54.721 23.288 22.62 23.288 22.62 55.775 0 32.488-22.861 55.364-22.861 22.877-54.963 22.877Zm-.82-244.833q-32.224 0-55.254-23.288-23.03-23.289-23.03-55.623 0-32.333 23.271-55.364 23.272-23.03 55.495-23.03 32.224 0 55.193 23.288 22.969 23.289 22.969 55.622 0 32.334-23.21 55.364-23.21 23.031-55.434 23.031Z"/></svg>
</label>
<div class="toc-content">
{{ macros_toc::toc(page=page, header=false) }}
</div>
</div>
{% endif %}
{# Button to go to the comment section #}
{% if comment_system %}
<a href="#comments" id="comments-button" title="Go to comment section">
<svg viewBox="0 0 20 20" fill="currentColor"><path d="M18 10c0 3.866-3.582 7-8 7a8.841 8.841 0 01-4.083-.98L2 17l1.338-3.123C2.493 12.767 2 11.434 2 10c0-3.866 3.582-7 8-7s8 3.134 8 7zM7 9H5v2h2V9zm8 0h-2v2h2V9zM9 9h2v2H9V9z" clip-rule="evenodd" fill-rule="evenodd"/></svg>
</a>
{% endif %}
{# Button to go to the top of the page #}
<a href="#" id="top-button" title="Go to top of page">
<svg viewBox="0 0 20 20" fill="currentColor"><path d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z"/></svg>
</a>
</div>
{% endif %}
{# Add KaTeX functionality #}
{%- if macros_settings::evaluate_setting_priority(setting="katex", page=page, default_global_value=false) == "true" -%}
<link rel="stylesheet" href="{{ get_url(path='katex.min.css', trailing_slash=false) | safe }}">
<script defer src="{{ get_url(path='js/katex.min.js', trailing_slash=false) | safe }}"></script>
{%- endif -%}
{# Add copy button to code blocks #}
{%- if macros_settings::evaluate_setting_priority(setting="copy_button", page=page, default_global_value=true) == "true" -%}
<script defer src="{{ get_url(path='js/copyCodeToClipboard.min.js', trailing_slash=false) | safe }}"/></script>
{%- endif -%}
{# Add backlinks to footnotes #}
{%- if macros_settings::evaluate_setting_priority(setting="footnote_backlinks", page=page, default_global_value=false) == "true" -%}
<script defer src="{{ get_url(path='js/footnoteBacklinks.min.js', trailing_slash=false | safe )}}"/></script>
{%- endif -%}
{% endmacro content %}

View File

@@ -1,7 +1,7 @@
{% macro format_date(date, short) %}
{% macro format_date(date, short, language_strings="") %}
{# Set locale #}
{% set date_locale = macros_translate::translate(key="date_locale", default="en_GB") %}
{% set date_locale = macros_translate::translate(key="date_locale", default="en_GB", language_strings=language_strings) %}
{% if config.extra.short_date_format and short %}
{{ date | date(format=config.extra.short_date_format, locale=date_locale) }}

View File

@@ -1,4 +1,4 @@
{% macro list_posts(posts, max) %}
{% macro list_posts(posts, max, language_strings="") %}
<div class="bloglist-container">
{% for post in posts %}
@@ -13,7 +13,7 @@
{% if post.date %}
<div class="date">
{{ macros_format_date::format_date(date=post.date, short=false) }}
{{ macros_format_date::format_date(date=post.date, short=false, language_strings=language_strings) }}
</div>
{% endif %}
@@ -37,14 +37,14 @@
<p>{{ post.summary | striptags | safe | trim_end_matches(pat=".") }}…</p>
{% endif %}
</div>
<a class="readmore" href={{ post.permalink }}>{{ macros_translate::translate(key="read_more", default="Read more") }}&nbsp;</a>
<a class="readmore" href={{ post.permalink }}>{{ macros_translate::translate(key="read_more", default="Read more", language_strings=language_strings) }}&nbsp;</a>
</div>
</section>
{% endif %}
{% if not loop.last %}
{% if loop.index == max %}
<div class="all-posts">
<a href="{{ get_url(path="blog", lang=lang) }}/">{{ macros_translate::translate(key="all_posts", default="All posts") }}&nbsp;</a>
<a href="{{ get_url(path="blog", lang=lang) }}/">{{ macros_translate::translate(key="all_posts", default="All posts", language_strings=language_strings) }}&nbsp;</a>
</div>
{% endif %}
{% endif %}

View File

@@ -1,28 +1,28 @@
{% macro paginate() %}
{% macro paginate(language_strings="") %}
{% if paginator %}
<ul class="pagination">
{% if paginator.previous %}
<li class="page-item page-prev">
<a href="{{ paginator.previous }}" class="page-link" aria-label="{{ macros_translate::translate(key="prev", default="Prev") }}">← {{ macros_translate::translate(key="prev", default="Prev") }}</a>
<a href="{{ paginator.previous }}" class="page-link" aria-label="{{ macros_translate::translate(key="prev", default="Prev", language_strings=language_strings) }}">← {{ macros_translate::translate(key="prev", default="Prev", language_strings=language_strings) }}</a>
</li>
{% else %}
<li class="page-item page-prev">
<span class="page-link disabled" aria-disabled="true" aria-label="{{ macros_translate::translate(key="prev", default="Prev") }} (disabled)">← {{ macros_translate::translate(key="prev", default="Prev") }}</span>
<span class="page-link disabled" aria-disabled="true" aria-label="{{ macros_translate::translate(key="prev", default="Prev", language_strings=language_strings) }} (disabled)">← {{ macros_translate::translate(key="prev", default="Prev", language_strings=language_strings) }}</span>
</li>
{% endif %}
<li class="page-item page-numbers">
{{ paginator.current_index }} {{ macros_translate::translate(key="of", default="of") }} {{ paginator.number_pagers }}
{{ paginator.current_index }} {{ macros_translate::translate(key="of", default="of", language_strings=language_strings) }} {{ paginator.number_pagers }}
</li>
{% if paginator.next %}
<li class="page-item page-next">
<a href="{{ paginator.next }}" class="page-link" aria-label="{{ macros_translate::translate(key="next", default="Next") }}">{{ macros_translate::translate(key="next", default="Next") }} →</a>
<a href="{{ paginator.next }}" class="page-link" aria-label="{{ macros_translate::translate(key="next", default="Next", language_strings=language_strings) }}">{{ macros_translate::translate(key="next", default="Next", language_strings=language_strings) }} →</a>
</li>
{% else %}
<li class="page-item page-next">
<span class="page-link disabled" aria-disabled="true" aria-label="{{ macros_translate::translate(key="next", default="Next") }} (disabled)">{{ macros_translate::translate(key="next", default="Next") }} →</span>
<span class="page-link disabled" aria-disabled="true" aria-label="{{ macros_translate::translate(key="next", default="Next", language_strings=language_strings) }} (disabled)">{{ macros_translate::translate(key="next", default="Next", language_strings=language_strings) }} →</span>
</li>
{% endif %}
</ul>

View File

@@ -1,4 +1,4 @@
{%- macro set_title() -%}
{%- macro set_title(language_strings="") -%}
{# Setup. #}
{%- set prefix = config.title | safe -%}
@@ -26,7 +26,7 @@
{%- set suffix = term.name -%}
{% elif taxonomy.name %}
{# List of tags. #}
{%- set suffix = macros_translate::translate(key=taxonomy.name) | capitalize -%}
{%- set suffix = macros_translate::translate(key=taxonomy.name, language_strings=language_strings) | capitalize -%}
{% else %}
{%- set suffix = "404" %}
{%- endif -%}

View File

@@ -1,4 +1,4 @@
{% macro toc(page, header) %}
{% macro toc(page, header, language_strings="") %}
{%- set toc_levels = page.extra.toc_levels | default(value=3) -%}
@@ -8,7 +8,7 @@
<div class="toc-container">
{% if header %}
<h3>{{ macros_translate::translate(key="table_of_contents", default="Table of Contents") }}</h3>
<h3>{{ macros_translate::translate(key="table_of_contents", default="Table of Contents", language_strings=language_strings) }}</h3>
{% endif %}
<ul>

View File

@@ -1,14 +1,24 @@
{% macro translate(key, default="", force_lang="") %}
{#
Macro: translate
Purpose: Translate text strings based on the current language setting.
Parameters:
- key: The key used to look up the translation in the loaded language data.
- language_strings: The loaded language data (from a .toml file).
- default: The default text to use if a translation is not found.
{%- if config.default_language != "en" -%}
{#- The entire site should be translated -#}
{{- trans(key=key | safe, lang=lang) -}}
{%- elif lang != config.default_language -%}
{{- trans(key=key | safe, lang=lang) -}}
{%- elif force_lang -%}
{{- trans(key=key | safe, lang=force_lang) -}}
{%- else -%}
{{- default -}}
{%- endif -%}
Usage:
Use this macro to translate text in templates. The macro looks for the
translation based on the given 'key' in 'language_strings'. If not found,
it falls back to using the 'default' text.
Note:
The 'language_strings' are loaded in base.html based on the current language
from files in the 'i18n' folder.
Example:
{{ macros_translate::translate(key="site_source", language_strings=language_strings, default="Site source", language_strings=language_strings) }}
#}
{% macro translate(key, language_strings="", default="") %}
{{- language_strings[key] | default(value=default) | safe -}}
{% endmacro %}

View File

@@ -1,7 +1,198 @@
{% extends "base.html" %}
{% block main_content %}
{%- set separator = config.extra.separator | default(value="•") -%}
{{ macros_content::content(page=page)}}
{%- set rel_attributes = macros_rel_attributes::rel_attributes() | trim -%}
{%- if config.markdown.external_links_target_blank -%}
{%- set blank_target = "target=_blank" -%}
{%- else -%}
{%- set blank_target = "" -%}
{%- endif -%}
{# Debugging #}
{# {% set last_ancestor = page.ancestors | slice(start=-1) %}
{% set current_section = get_section(path=last_ancestor.0) %}
{% set settings_to_test = [
"footnote_backlinks",
"katex",
"quick_navigation_buttons",
"show_reading_time",
"show_remote_changes",
"toc",
] %}
<table>
<thead>
<tr>
<th>setting</th>
<th>page</th>
<th>section</th>
<th>config</th>
<th>macro output</th>
</tr>
</thead>
<tbody>
{% for setting in settings_to_test %}
<tr>
<td><code>{{ setting }}</code></td>
<td>{{ page.extra[setting] | default(value="⬛") }}</td>
<td>{{ current_section.extra[setting] | default(value="⬛") }}</td>
<td>{{ config.extra[setting] | default(value="⬛") }}</td>
<td>{{ macros_settings::evaluate_setting_priority(setting=setting, page=page) }}</td>
</tr>
{% endfor %}
</tbody>
</table> #}
{# End debugging #}
<main>
<article>
<h1 class="article-title">
{{ page.title }}
</h1>
<ul class="meta">
{% if page.draft %}
<li class="draft-label">{{ macros_translate::translate(key="draft", default="DRAFT", language_strings=language_strings) }}</li>
{% endif %}
{% if page.date %}
<li>{{ macros_format_date::format_date(date=page.date, short=true, language_strings=language_strings) }}</li>
{% endif %}
{# page settings override config settings #}
{% if macros_settings::evaluate_setting_priority(setting="show_reading_time", page=page, default_global_value=true) == "true" %}
{{ separator }} <li title="{{ page.word_count }} {{ macros_translate::translate(key="words", default="words", language_strings=language_strings) }}">{{ page.reading_time }}&nbsp;{{ macros_translate::translate(key="min_read", default="min read", language_strings=language_strings) }}</li>
{% endif %}
{%- if page.taxonomies and page.taxonomies.tags -%}
{{ separator }}&nbsp;<li>{{- macros_translate::translate(key="tags", default="tags", language_strings=language_strings) | capitalize -}}:&nbsp;</li>
{%- for tag in page.taxonomies.tags -%}
<li><a href={{ get_taxonomy_url(kind='tags', name=tag, lang=lang) | safe }}>{{ tag }}</a>
{%- if not loop.last -%}
,&nbsp;
{%- endif -%}
</li>
{%- endfor -%}
{%- endif -%}
{% if page.updated %}
</ul><ul class="meta last-updated"><li>{{ macros_translate::translate(key="last_updated_on", default="Last updated on", language_strings=language_strings) }} {{ macros_format_date::format_date(date=page.updated, short=true, language_strings=language_strings) }}</li>
{# Show link to remote changes if enabled #}
{% if config.extra.remote_repository_url and macros_settings::evaluate_setting_priority(setting="show_remote_changes", page=page, default_global_value=true) == "true" %}
{{ separator }}
<li><a href="{{ macros_create_history_url::create_history_url(relative_path=page.relative_path) }}" {{ blank_target }} rel="{{ rel_attributes }}">{{ macros_translate::translate(key="see_changes", default="See changes", language_strings=language_strings) }}<small></small></a></li>
{% endif %}
{% endif %}
</ul>
{% if page.extra.tldr %}
<div class="tldr">
<h3>TL;DR:</h3>
<p>{{ page.extra.tldr }}</p>
</div>
{% endif %}
{# Optional table of contents below the header #}
{% if page.toc and macros_settings::evaluate_setting_priority(setting="toc", page=page, default_global_value=false) == "true" %}
{{ macros_toc::toc(page=page, header=true, language_strings=language_strings) }}
{% endif %}
<section class="body">
{# The replace pattern is used to enable arbitrary locations for the Table of Contents #}
{# This is Philipp Oppermann's workaround: https://github.com/getzola/zola/issues/584#issuecomment-474329637 #}
{{ page.content | replace(from="<!-- toc -->", to=macros_toc::toc(page=page, header=false, language_strings=language_strings)) | safe }}
</section>
{# Check if comments are enabled #}
{% set giscus_enabled = config.extra.giscus.enabled_for_all_posts or page.extra.giscus %}
{% set utterances_enabled = config.extra.utterances.enabled_for_all_posts or page.extra.utterances %}
{% set hyvortalk_enabled = config.extra.hyvortalk.enabled_for_all_posts or page.extra.hyvortalk %}
{% set isso_enabled = config.extra.isso.enabled_for_all_posts or page.extra.isso %}
{# Ensure only one comment system is enabled #}
{# Counter for enabled comment systems #}
{% set enabled_systems = 0 %}
{# Check and count the enabled comment systems #}
{% if giscus_enabled %}
{% set comment_system = "giscus" %}
{% set enabled_systems = enabled_systems + 1 %}
{% endif %}
{% if utterances_enabled %}
{% set comment_system = "utterances" %}
{% set enabled_systems = enabled_systems + 1 %}
{% endif %}
{% if hyvortalk_enabled %}
{% set comment_system = "hyvortalk" %}
{% set enabled_systems = enabled_systems + 1 %}
{% endif %}
{% if isso_enabled %}
{% set comment_system = "isso" %}
{% set enabled_systems = enabled_systems + 1 %}
{% endif %}
{# Ensure only one comment system is enabled #}
{% if enabled_systems > 1 %}
{{ throw(message="ERROR: Multiple comment systems have been enabled for the same page. Check your config.toml and individual page settings to ensure only one comment system is activated at a time.") }}
{% endif %}
{% if comment_system %}
{% set automatic_loading = config.extra[comment_system].automatic_loading %}
{{ macros_add_comments::add_comments(comment_system=comment_system, automatic_loading=automatic_loading) }}
{% endif %}
</article>
</main>
{# Quick navigation buttons #}
{% if macros_settings::evaluate_setting_priority(setting="quick_navigation_buttons", page=page, default_global_value=false) == "true" %}
<div id="button-container">
{# Button to go show a floating Table of Contents #}
{% if page.toc %}
<div id="toc-floating-container">
<input type="checkbox" id="toc-toggle" class="toggle"/>
<label for="toc-toggle" class="overlay"></label>
<label for="toc-toggle" id="toc-button" class="button" title="Toggle Table of Contents" aria-label="toggle Table of Contents">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M414.82-193.094q-18.044 0-30.497-12.32-12.453-12.319-12.453-30.036t12.453-30.086q12.453-12.37 30.497-12.37h392.767q17.237 0 29.927 12.487 12.69 12.486 12.69 30.203 0 17.716-12.69 29.919t-29.927 12.203H414.82Zm0-244.833q-18.044 0-30.497-12.487Q371.87-462.9 371.87-480.45t12.453-29.92q12.453-12.369 30.497-12.369h392.767q17.237 0 29.927 12.511 12.69 12.512 12.69 29.845 0 17.716-12.69 30.086-12.69 12.37-29.927 12.37H414.82Zm0-245.167q-18.044 0-30.497-12.32t-12.453-30.037q0-17.716 12.453-30.086 12.453-12.369 30.497-12.369h392.767q17.237 0 29.927 12.486 12.69 12.487 12.69 30.203 0 17.717-12.69 29.92-12.69 12.203-29.927 12.203H414.82ZM189.379-156.681q-32.652 0-55.878-22.829t-23.226-55.731q0-32.549 23.15-55.647 23.151-23.097 55.95-23.097 32.799 0 55.313 23.484 22.515 23.484 22.515 56.246 0 32.212-22.861 54.893-22.861 22.681-54.963 22.681Zm0-245.167q-32.652 0-55.878-23.134-23.226-23.135-23.226-55.623 0-32.487 23.467-55.517t56.12-23.03q32.102 0 54.721 23.288 22.62 23.288 22.62 55.775 0 32.488-22.861 55.364-22.861 22.877-54.963 22.877Zm-.82-244.833q-32.224 0-55.254-23.288-23.03-23.289-23.03-55.623 0-32.333 23.271-55.364 23.272-23.03 55.495-23.03 32.224 0 55.193 23.288 22.969 23.289 22.969 55.622 0 32.334-23.21 55.364-23.21 23.031-55.434 23.031Z"/></svg>
</label>
<div class="toc-content">
{{ macros_toc::toc(page=page, header=false, language_strings=language_strings) }}
</div>
</div>
{% endif %}
{# Button to go to the comment section #}
{% if comment_system %}
<a href="#comments" id="comments-button" title="Go to comment section">
<svg viewBox="0 0 20 20" fill="currentColor"><path d="M18 10c0 3.866-3.582 7-8 7a8.841 8.841 0 01-4.083-.98L2 17l1.338-3.123C2.493 12.767 2 11.434 2 10c0-3.866 3.582-7 8-7s8 3.134 8 7zM7 9H5v2h2V9zm8 0h-2v2h2V9zM9 9h2v2H9V9z" clip-rule="evenodd" fill-rule="evenodd"/></svg>
</a>
{% endif %}
{# Button to go to the top of the page #}
<a href="#" id="top-button" title="Go to top of page">
<svg viewBox="0 0 20 20" fill="currentColor"><path d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z"/></svg>
</a>
</div>
{% endif %}
{# Add KaTeX functionality #}
{%- if macros_settings::evaluate_setting_priority(setting="katex", page=page, default_global_value=false) == "true" -%}
<link rel="stylesheet" href="{{ get_url(path='katex.min.css', trailing_slash=false) | safe }}">
<script defer src="{{ get_url(path='js/katex.min.js', trailing_slash=false) | safe }}"></script>
{%- endif -%}
{# Add copy button to code blocks #}
{%- if macros_settings::evaluate_setting_priority(setting="copy_button", page=page, default_global_value=true) == "true" -%}
<script defer src="{{ get_url(path='js/copyCodeToClipboard.min.js', trailing_slash=false) | safe }}"/></script>
{%- endif -%}
{# Add backlinks to footnotes #}
{%- if macros_settings::evaluate_setting_priority(setting="footnote_backlinks", page=page, default_global_value=false) == "true" -%}
<script defer src="{{ get_url(path='js/footnoteBacklinks.min.js', trailing_slash=false | safe )}}"/></script>
{%- endif -%}
{% endblock main_content %}

View File

@@ -65,23 +65,23 @@
{% set current_year = now() | date(format="%Y") %}
{# Translate the copyright if set in the config #}
{%- if config.extra.translate_copyright -%}
<p>{{ macros_translate::translate(key="copyright", default=config.extra.copyright) | replace(from="$CURRENT_YEAR", to=current_year) | replace(from="$SEPARATOR", to=separator) | markdown | safe }}</p>
<p>{{ macros_translate::translate(key="copyright", default=config.extra.copyright, language_strings=language_strings) | replace(from="$CURRENT_YEAR", to=current_year) | replace(from="$SEPARATOR", to=separator) | markdown | safe }}</p>
{%- else -%}
<p>{{ config.extra.copyright | replace(from="$CURRENT_YEAR", to=current_year) | replace(from="$SEPARATOR", to=separator) | markdown | safe }}</p>
{%- endif -%}
{%- endif -%}
{# Shows "Powered by Zola & tabi" notice #}
{{ macros_translate::translate(key="powered_by", default="Powered by") }}
{{ macros_translate::translate(key="powered_by", default="Powered by", language_strings=language_strings) }}
<a rel="{{ rel_attributes }}" {{ blank_target }} href="https://www.getzola.org">Zola</a>
{{ macros_translate::translate(key="and", default="&") }}
{{ macros_translate::translate(key="and", default="&", language_strings=language_strings) }}
<a rel="{{ rel_attributes }}" {{ blank_target }} href="https://github.com/welpo/tabi">tabi</a>
{# Shows link to remote repository #}
{%- if config.extra.remote_repository_url and config.extra.show_remote_source | default(value=true) -%}
{{ separator }}
<a rel="{{ rel_attributes }}" {{ blank_target }} href="{{ config.extra.remote_repository_url }}">
{{ macros_translate::translate(key="site_source", default="Site source") }}
{{ macros_translate::translate(key="site_source", default="Site source", language_strings=language_strings) }}
</a>
{%- endif -%}
</small>

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{# Site title #}
<title>{{ macros_set_title::set_title() }}</title>
<title>{{ macros_set_title::set_title(language_strings=language_strings) }}</title>
{# Favicon #}
{% if config.extra.favicon %}

View File

@@ -0,0 +1,43 @@
<li class="language-switcher">
<div class="dropdown" type="Button">
<div class="language-switcher-icon"></div>
<div class="dropdown-content">
{#- Display the current language first in the dropdown -#}
{{ macros_translate::translate(key="language_name", default=lang, language_strings=language_strings) }}
{#- Loop through all the available languages in the config -#}
{%- for lcode, ldetails in config.languages -%}
{#- Skip the current language to avoid linking to the current page -#}
{%- if lang == lcode -%}
{%- continue -%}
{%- endif -%}
{#- Dynamically load the language strings for each language -#}
{%- set other_language_strings = load_data(path="i18n/" ~ lcode ~ ".toml", required=false) -%}
{%- if not other_language_strings -%}
{%- set other_language_strings = load_data(path="themes/tabi/i18n/" ~ lcode ~ ".toml", required=false) -%}
{%- endif -%}
{#- Use the loaded language strings to get the language name -#}
{% set language_name = macros_translate::translate(key="language_name", default=lcode,
language_strings=other_language_strings) %}
{#- Check if the language code matches the default language -#}
{%- if lcode == config.default_language -%}
{#- If it does, link to the root path (no language code in URL) -#}
<a type="Button" href="{{ current_url | replace(from='/' ~ lang ~ '/', to = '/') }}">{{ language_name }}</a>
{%- elif lang == config.default_language -%}
{#- Check if the current language is the default language -#}
{#- If it is, append the language code to the base URL -#}
<a type="Button" href="{{ config.base_url }}/{{ lcode }}{{ current_path | default(value=" /") | safe }}">{{
language_name }}</a>
{%- else -%}
{#- If it's not, replace the current language code in the URL with the new one -#}
<a type="Button" href="{{ current_url | replace(from='/' ~ lang ~ '/', to='/' ~ lcode ~ '/') }}">{{
language_name }}</a>
{%- endif -%}
{%- endfor -%}
</div>
</div>
</li>

View File

@@ -12,7 +12,7 @@
<li>
{% set trailing_slash = menu.trailing_slash | default(value=true) %}
<a class="nav-links no-hover-padding" href="{{ get_url(path=menu.url, lang=lang, trailing_slash=trailing_slash) }}"/>
{{ macros_translate::translate(key=menu.name, default=menu.name) }}
{{ macros_translate::translate(key=menu.name, default=menu.name, language_strings=language_strings) }}
</a>
</li>
{% endfor %}
@@ -21,37 +21,7 @@
{# Language switcher #}
{# Display the language switcher only if more than one language is available #}
{%- if config.languages | length > 0 %}
<li class="language-switcher">
<div class="dropdown" type="Button">
<div class="language-switcher-icon"></div>
{# Display the current language first in the dropdown #}
<div class="dropdown-content">
{{ macros_translate::translate(key="language_name", default=config.extra.language_name.en) }}
{# Loop through all the available languages in the config #}
{%- for lcode, language_name in config.extra.language_name -%}
{# Skip the current language to avoid linking to the current page #}
{%- if lang != lcode -%}
{# Check if the language code matches the default language #}
{%- if lcode == config.default_language -%}
{# If it does, link to the root path (no language code in URL) #}
<a type="Button" href="{{ current_url | replace(from='/' ~ lang ~ '/', to = '/') }}">{{ language_name }}</a>
{%- else -%}
{# Check if the current language is the default language #}
{%- if lang == config.default_language -%}
{# If it is, append the language code to the base URL #}
<a type="Button" href="{{ config.base_url }}/{{ lcode }}{{ current_path | default(value="/") | safe }}">{{ language_name }}</a>
{%- else -%}
{# If it's not, replace the current language code in the URL with the new one #}
<a type="Button" href="{{ current_url | replace(from='/' ~ lang ~ '/', to='/' ~ lcode ~ '/') }}"/>{{ language_name }}</a>
{%- endif -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
</div>
</div>
</li>
{% include "partials/language_switcher.html" %}
{%- endif %}
{# Theme switcher #}

View File

@@ -23,12 +23,12 @@
{% endif -%}
{% set max = section.extra.max_posts | default(value=999999) %}
{{ macros_list_posts::list_posts(posts=pages, max=max) }}
{{ macros_list_posts::list_posts(posts=pages, max=max, language_strings=language_strings) }}
</div>
</main>
{% if paginator %}
{{ macros_paginate::paginate() }}
{{ macros_paginate::paginate(language_strings=language_strings) }}
{% endif %}
{% endblock main_content %}

View File

@@ -1,7 +1,12 @@
{%- import "macros/translate.html" as macros_translate -%}
{# Load internationalisation data #}
{%- set language_strings = load_data(path="i18n/" ~ lang ~ '.toml', required=false) -%}
{%- if not language_strings -%}
{%- set language_strings = load_data(path="themes/tabi/i18n/" ~ lang ~ ".toml", required=false) -%}
{%- endif -%}
{%- set open_quote = macros_translate::translate(key="open_quotation_mark", default="“") -%}
{%- set close_quote = macros_translate::translate(key="close_quotation_mark", default="”") -%}
{%- set open_quote = macros_translate::translate(key="open_quotation_mark", default="“", language_strings=language_strings) -%}
{%- set close_quote = macros_translate::translate(key="close_quotation_mark", default="”", language_strings=language_strings) -%}
{#- The `random_id` ensures that each instance of the shortcode has a "unique" id -#}
{#- allowing individual interactive elements (like toggles) to function correctly. -#}
@@ -15,16 +20,16 @@
<div class="translated">
<blockquote>
<p>{{ open_quote ~ translated ~ close_quote }}</p>
{% if author %}<p> — {{ author }}{% endif %}<label for="toggle-{{ random_id }}" class="quote-label quote-label-original">
({{- macros_translate::translate(key="show_original_quote", default="Show original quote") -}})
</label></p>
{% if author %}<p> — {{ author }}{% endif %} <label for="toggle-{{ random_id }}" class="quote-label quote-label-original">
({{- macros_translate::translate(key="show_original_quote", default="Show original quote", language_strings=language_strings) -}})
</label></p>
</blockquote>
</div>
<div class="original">
<blockquote>
<p>{{ open_quote ~ original ~ close_quote }}</p>
{% if author %}<p> — {{ author }}{% endif %} <label for="toggle-{{ random_id }}" class="quote-label quote-label-translate">
({{- macros_translate::translate(key="show_translation", default="Show translation") -}})
({{- macros_translate::translate(key="show_translation", default="Show translation", language_strings=language_strings) -}})
</label></p>
</blockquote>
</div>

View File

@@ -2,7 +2,7 @@
{% block main_content %}
{%- set title = macros_translate::translate(key="tags_title", default="All tags") -%}
{%- set title = macros_translate::translate(key="all_tags", default="All tags", language_strings=language_strings) -%}
{{ macros_page_header::page_header(title=title)}}
@@ -13,10 +13,10 @@
{{ term.name }}</a>
{{ term.pages | length }}{%- if term.pages | length == 1 %}
{# Only one post. Singular. #}
{{- macros_translate::translate(key="post", default="post") -}}
{{- macros_translate::translate(key="post", default="post", language_strings=language_strings) -}}
{% elif term.pages | length > 1 %}
{# More than one post per tag. Plural. #}
{{- macros_translate::translate(key="posts", default="posts") -}}
{{- macros_translate::translate(key="posts", default="posts", language_strings=language_strings) -}}
{%- endif -%}
</li>
{%- endfor -%}

View File

@@ -9,7 +9,7 @@
<ul class="pagination">
<li class="page-item">
<a class="all-tags" href="{{ get_url(path="tags", lang=lang) }}/">&nbsp;{{- macros_translate::translate(key="all_tags", default="All tags") -}}</a>
<a class="all-tags" href="{{ get_url(path="tags", lang=lang) }}/">&nbsp;{{- macros_translate::translate(key="all_tags", default="All tags", language_strings=language_strings) -}}</a>
</li>
</ul>