Merge commit '8be91ee3d9' as 'themes/tabi-lean'

This commit is contained in:
Fabian Montero 2025-09-14 16:00:14 -06:00
commit 2c6602e3b2
336 changed files with 25227 additions and 0 deletions

View file

@ -0,0 +1,17 @@
{#- Feed utility macros -#}
{#- Zola 0.19.0 uses `generate_feeds`. Prior versions use `generate_feed` -#}
{%- macro get_generate_feed() -%}
{{- config.generate_feeds | default(value=config.generate_feed) -}}
{%- endmacro get_generate_feed -%}
{%- macro get_feed_url() -%}
{{- config.feed_filenames[0] | default(value=(config.feed_filename)) -}}
{%- endmacro get_feed_url -%}
{#- Check footer feed icon conditions -#}
{%- macro should_show_footer_feed_icon() -%}
{%- set generate_feed = feed_utils::get_generate_feed() == "true" -%}
{%- set feed_url = feed_utils::get_feed_url() -%}
{{- generate_feed and config.extra.feed_icon and feed_url -}}
{%- endmacro should_show_footer_feed_icon -%}

View file

@ -0,0 +1,59 @@
{%- macro format_date(date, short, language_strings="") -%}
{#- Set locale -#}
{%- set date_locale = macros_translate::translate(key="date_locale", default="en_GB", language_strings=language_strings) -%}
{#- Check for language-specific date formats -#}
{%- set language_format = "" -%}
{%- if config.extra.date_formats -%}
{%- for format_config in config.extra.date_formats -%}
{%- if format_config.lang == lang -%}
{%- if short and format_config.short -%}
{%- set_global language_format = format_config.short -%}
{%- elif not short and format_config.long -%}
{%- set_global language_format = format_config.long -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
{%- if language_format -%}
{{ date | date(format=language_format, locale=date_locale) }}
{%- elif config.extra.short_date_format and short -%}
{{ date | date(format=config.extra.short_date_format, locale=date_locale) }}
{%- elif config.extra.long_date_format and not short -%}
{{ date | date(format=config.extra.long_date_format, locale=date_locale) }}
{%- elif not config.extra.short_date_format and date_locale == "en_GB" -%}
{%- set day = date | date(format='%-d') | int -%}
{%- if day in [11, 12, 13] -%}
{%- set suffix = "th" -%}
{%- else -%}
{%- set last_digit = day % 10 -%}
{%- if last_digit == 1 -%}
{%- set suffix = "st" -%}
{%- elif last_digit == 2 -%}
{%- set suffix = "nd" -%}
{%- elif last_digit == 3 -%}
{%- set suffix = "rd" -%}
{%- else -%}
{%- set suffix = "th" -%}
{%- endif -%}
{%- endif -%}
{#- Return the date. -#}
{{ date | date(format="%-d") }}{{ suffix }}
{%- if short == true -%}
{{ date | date(format=" %b %Y") }}
{%- else -%}
{{ date | date(format=" %B %Y") }}
{%- endif -%}
{%- else -%}
{%- if short -%}
{{ date | date(format="%-d %b %Y", locale=date_locale) }}
{%- else -%}
{{ date | date(format="%d %b %Y", locale=date_locale) }}
{%- endif -%}
{%- endif -%}
{%- endmacro -%}

View file

@ -0,0 +1,163 @@
{# `metadata` can be "dates", "indexes" or both (e.g. "dates indexes" or "indexes dates"). #}
{# If both, the order doesn't matter and indexes will always be displayed before dates. #}
{# It would also work with arrays (e.g. ["dates"] or ["indexes"] or even ["indexes","dates"]). #}
{# Nevertheless, arrays cannot be used as a default value for a macro parameter in Tera (see https://github.com/Keats/tera/issues/710). #}
{# `paginator` is only used to compute indexes metadata and can be let empty otherwise. #}
{% macro list_posts(posts, all_posts="", max=999999, metadata="dates", language_strings="", section_path="", paginator="", pinned_first=false, current_page=1) %}
{%- set separator = config.extra.separator | default(value="•") -%}
{# Separate pinned and regular posts from all_posts if available, otherwise from posts #}
{% if pinned_first %}
{% set source_posts = all_posts | default(value=posts) %}
{% set pinned_posts = [] %}
{% set regular_posts = [] %}
{% for post in source_posts %}
{% if post.extra.pinned %}
{% set_global pinned_posts = pinned_posts | concat(with=post) %}
{% else %}
{% set_global regular_posts = regular_posts | concat(with=post) %}
{% endif %}
{% endfor %}
{# On page 1 or when no pagination, show pinned then regular #}
{% if current_page == 1 %}
{% if paginator %}
{# With pagination: pinned + current page's posts #}
{% set display_posts = pinned_posts | concat(with=posts) %}
{% else %}
{# Without pagination: pinned + regular (no duplicates) #}
{% set display_posts = pinned_posts | concat(with=regular_posts) %}
{% endif %}
{% else %}
{% set display_posts = posts %}
{% endif %}
{% else %}
{% set display_posts = posts %}
{% endif %}
<div class="bloglist-container">
{# Display posts #}
{% for post in display_posts %}
{% if loop.index <= max %}
{% if loop.index == max or loop.last %}
{% set bottom_divider = false %}
{% else %}
{% set bottom_divider = true %}
{% endif %}
<section class="bloglist-meta {% if bottom_divider -%}bottom-divider{%- endif -%}">
<ul>
{%- if "indexes" in metadata -%}
{%- set post_index = loop.index -%}
{%- set number_of_posts = posts | length -%}
{# in case we have a pager, the index has been computed for the current page. #}
{%- if paginator -%}
{%- set number_of_posts = paginator.total_pages -%}
{%- set number_of_other_pages = paginator.current_index - 1 -%}
{%- set posts_per_page = paginator.paginate_by -%}
{%- set posts_in_other_pages = number_of_other_pages * posts_per_page -%}
{%- set post_index = posts_in_other_pages + post_index -%}
{%- endif -%}
{%- if macros_settings::evaluate_setting_priority(setting="post_listing_index_reversed", page=section, default_global_value=false) == "true" -%}
{# index starts at 1 instead of 0 #}
{%- set post_index = number_of_posts + 1 - post_index -%}
{%- endif -%}
<li class="index-label">{{ post_index }}</li>
{%- endif -%}
{%- if "dates" in metadata -%}
{%- set allowed_post_listing_dates = ["date", "updated", "both"] -%}
{#- Calling the hierarchy macro here causes an error due to the "get parents" part of the macro. -#}
{#- This seems cleaner. -#}
{%- set post_listing_date = section.extra.post_listing_date | default(value=config.extra.post_listing_date) | default(value="date") -%}
{%- if post_listing_date not in allowed_post_listing_dates -%}
{{ throw(message="ERROR: Invalid value for config.extra.post_listing_date. Allowed values are 'date', 'updated', or 'both'.") }}
{%- endif -%}
{%- set show_date = post.date and post_listing_date == "date" or post.date and post_listing_date == "both" or post.date and post_listing_date == "updated" and not post.updated -%}
{%- set show_updated = post.updated and post_listing_date == "updated" or post.updated and post_listing_date == "both" -%}
{%- if show_date or show_updated -%}
{%- if show_date -%}
<li class="date">{{- macros_format_date::format_date(date=post.date, short=false, language_strings=language_strings) -}}</li>
{%- endif -%}
{%- if show_date and show_updated -%}
<li class="mobile-only separator">{{- separator -}}</li>
{%- endif -%}
{%- if show_updated -%}
{%- set last_updated_str = macros_translate::translate(key="last_updated_on", default="Updated on $DATE", language_strings=language_strings) -%}
{%- set formatted_date = macros_format_date::format_date(date=post.updated, short=true, language_strings=language_strings) -%}
{%- set updated_str = last_updated_str | replace(from="$DATE", to=formatted_date) -%}
<li class="date">{{ updated_str }}</li>
{%- endif -%}
{%- endif -%}
{%- endif -%}
{% if post.extra.local_image or post.extra.remote_image %}
<li class="post-thumbnail">
<a href="{{ post.permalink }}">
{% if post.extra.local_image %}
{% set meta = get_image_metadata(path=post.extra.local_image, allow_missing=true) %}
<img class="thumbnail-image"
alt="{{ post.extra.local_image }}"
src="{{ get_url(path=post.extra.local_image) }}"
{% if meta.width %}width="{{ meta.width }}"{% endif %}
{% if meta.height %}height="{{ meta.height }}"{% endif %}>
{% elif post.extra.remote_image %}
<img class="thumbnail-image"
alt="{{ post.extra.remote_image }}"
src="{{ post.extra.remote_image }}">
{% endif %}
</a>
</li>
{% endif %}
{% if post.draft %}
<li class="draft-label">{{ macros_translate::translate(key="draft", default="DRAFT", language_strings=language_strings) }}</li>
{% endif %}
</ul>
</section>
<section class="bloglist-content {% if bottom_divider -%}bottom-divider{%- endif -%}">
<div>
{% if pinned_first and post.extra.pinned %}
<div class="pinned-label">
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="currentColor" fill-rule="evenodd" d="M10.5 2.255v-.01c.003-.03.013-.157-.361-.35C9.703 1.668 8.967 1.5 8 1.5s-1.703.169-2.138.394c-.375.194-.365.32-.362.351v.01c-.003.03-.013.157.362.35C6.297 2.832 7.033 3 8 3s1.703-.169 2.139-.394c.374-.194.364-.32.361-.351M12 2.25c0 .738-.433 1.294-1.136 1.669l.825 2.31c1.553.48 2.561 1.32 2.561 2.52c0 1.854-2.402 2.848-5.5 2.985V15a.75.75 0 0 1-1.5 0v-3.266c-3.098-.136-5.5-1.131-5.5-2.984c0-1.2 1.008-2.04 2.561-2.52l.825-2.311C4.433 3.544 4 2.988 4 2.25C4 .75 5.79 0 8 0s4 .75 4 2.25" clip-rule="evenodd"/></svg>
<span>{{ macros_translate::translate(key="pinned", default="Pinned", language_strings=language_strings) }}</span>
</div>
{% endif %}
<h2 class="bloglist-title">
<a href="{{ post.permalink }}">{{ post.title }}</a>
</h2>
{% if post.taxonomies.tags %}
<div class="bloglist-tags">
{% for tag in post.taxonomies.tags %}
<a class="tag" href="{{ get_taxonomy_url(kind='tags', name=tag, lang=lang) | safe }}">{{ tag }}</a>
{% endfor %}
</div>
{% endif %}
<div class="description">
{% if post.description %}
<p>{{ post.description | markdown(inline=true) | safe }}</p>
{% elif post.summary %}
<p>{{ post.summary | markdown(inline=true) | trim_end_matches(pat=".") | safe }}…</p>
{% endif %}
</div>
<a class="readmore" href="{{ post.permalink }}">{{ macros_translate::translate(key="read_more", default="Read more", language_strings=language_strings) }}&nbsp;<span class="arrow"></span></a>
</div>
</section>
{% endif %}
{% if not loop.last %}
{% if loop.index == max %}
<div class="all-posts">
<a href="{{ get_url(path=section_path, lang=lang) }}/">{{ macros_translate::translate(key="all_posts", default="All posts", language_strings=language_strings) }}&nbsp;<span class="arrow"></span></a>
</div>
{% endif %}
{% endif %}
{% endfor %}
</div>
{% endmacro %}

View file

@ -0,0 +1,18 @@
{% macro page_header(title, show_feed_icon=false) %}
{% set rel_attributes = macros_rel_attributes::rel_attributes() | trim %}
{%- set blank_target = macros_target_attribute::target_attribute(new_tab=config.markdown.external_links_target_blank) -%}
<h1 class="title-container section-title bottom-divider">
{{ title -}}
{% if show_feed_icon %}
{%- set feed_url = feed_utils::get_feed_url() -%}
<a class="no-hover-padding social" rel="{{ rel_attributes }}" {{ blank_target }} href="{{ get_url(path=term.path ~ feed_url, lang=lang, trailing_slash=false) | safe }}">
<img loading="lazy" alt="feed" title="feed" src="{{ get_url(path='/social_icons/rss.svg') }}">
</a>
{% endif %}
</h1>
{% endmacro page_header %}

View file

@ -0,0 +1,19 @@
{% macro rel_attributes() %}
{%- set rel_attributes = [] -%}
{%- if config.markdown.external_links_target_blank -%}
{%- set rel_attributes = rel_attributes | concat(with="noopener") -%}
{%- endif -%}
{# https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel#nofollow #}
{# This is ignored, as it doesn't make sense to set `nofollow` on projects or social links. #}
{# {%- if config.markdown.external_links_no_follow -%}
{%- set rel_attributes = rel_attributes | concat(with="nofollow") -%}
{%- endif -%} #}
{%- if config.markdown.external_links_no_referrer -%}
{%- set rel_attributes = rel_attributes | concat(with="noreferrer") -%}
{%- endif -%}
{# Return the array of rel attributes joined by a space #}
{{- rel_attributes | join(sep=" ") -}}
{% endmacro rel_attributes %}

View file

@ -0,0 +1,162 @@
{#
Those macros deal with introduction and navigation for series pages.
Using macros have been prefered over partial inclusion or inline code to make sure series_ordered_pages is forced to be used.
A section's pages natural order is invalid in case of reversed pagination which would lead to invalid series' pages order.
To prevent this, pages are ordered correctly in a separate variable which must be used instead of the series section pages.
#}
{#
Computes the introduction of a series's page.
Parameters:
- `page`: The page object being part of the series.
- `series_section`: The series' section the page belongs to.
- `series_ordered_pages`: The series' pages properly ordered (see at the top of this file for an explanation).
- `language_strings`: A dictionary containing the translation strings.
#}
{% macro process_series_template(template_type, page, series_section, series_ordered_pages, language_strings) %}
{%- if "series" in series_section.extra and series_section.extra.series -%}
{# Prepare variables for substitution #}
{%- set series_title = series_section.title -%}
{%- set series_permalink = series_section.permalink -%}
{%- set series_html_link = '<a href="' ~ series_section.permalink ~ '" aria_label="' ~ series_section.title ~ '">' ~ series_section.title ~ '</a>' -%}
{# Build series pages list #}
{%- set series_pages_list = [] -%}
{%- for series_page in series_ordered_pages -%}
{%- if series_page.relative_path == page.relative_path -%}
{%- set series_pages_list_item = '<li>' ~ series_page.title ~ '</li>' -%}
{%- else -%}
{%- set series_pages_list_item = '<li><a href="' ~ series_page.permalink ~ '" aria_label="' ~ series_page.title ~ '">' ~ series_page.title ~ '</a></li>' -%}
{%- endif -%}
{%- set_global series_pages_list = series_pages_list | concat(with=series_pages_list_item) -%}
{%- endfor -%}
{%- set series_pages_list = series_pages_list | join(sep="") -%}
{%- if macros_settings::evaluate_setting_priority(setting="post_listing_index_reversed", page=series_section, default_global_value=false) == "true" -%}
{%- set series_pages_ordered_list = '<ol reversed>' ~ series_pages_list ~ '</ol>' -%}
{%- else -%}
{%- set series_pages_ordered_list = '<ol>' ~ series_pages_list ~ '</ol>' -%}
{%- endif -%}
{%- set series_pages_unordered_list = '<ul>' ~ series_pages_list ~ '</ul>' -%}
{# Get page position and navigation info #}
{%- set series_pages_number = 0 -%}
{%- set series_page_index = 0 -%}
{%- set first_page = series_ordered_pages | first -%}
{%- set is_found = false -%}
{%- for series_page in series_ordered_pages -%}
{%- set_global series_pages_number = series_pages_number + 1 -%}
{%- if series_page.relative_path == page.relative_path -%}
{%- set_global series_page_index = series_pages_number -%}
{%- set_global is_found = true -%}
{%- else -%}
{%- if not is_found -%}
{%- set_global prev_page = series_page -%}
{%- elif not next_page is defined -%}
{%- set_global next_page = series_page -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{# Determine template to use based on available navigation #}
{%- set position = "middle" -%}
{%- if prev_page is defined and not next_page is defined -%}
{%- set_global position = "prev_only" -%}
{%- elif next_page is defined and not prev_page is defined -%}
{%- set_global position = "next_only" -%}
{%- endif -%}
{# Get template from config #}
{%- set templates_key = "series_" ~ template_type ~ "_templates" -%}
{%- set template = "" -%}
{%- if series_section.extra[templates_key] is defined -%}
{%- if series_section.extra[templates_key][position] is defined -%}
{%- set_global template = series_section.extra[templates_key][position] -%}
{%- elif series_section.extra[templates_key].default is defined -%}
{%- set_global template = series_section.extra[templates_key].default -%}
{%- endif -%}
{%- endif -%}
{# Prepare navigation variables #}
{%- if prev_page is defined -%}
{%- set prev_title = prev_page.title -%}
{%- set prev_permalink = prev_page.permalink -%}
{%- set prev_html_link = '<a href="' ~ prev_page.permalink ~ '" aria_label="' ~ prev_page.title ~ '">' ~ prev_page.title ~ '</a>' -%}
{%- set prev_description = prev_page.description | default(value="") -%}
{%- endif -%}
{%- if next_page is defined -%}
{%- set next_title = next_page.title -%}
{%- set next_permalink = next_page.permalink -%}
{%- set next_html_link = '<a href="' ~ next_page.permalink ~ '" aria_label="' ~ next_page.title ~ '">' ~ next_page.title ~ '</a>' -%}
{%- set next_description = next_page.description | default(value="") -%}
{%- endif -%}
{# Replace standard variables #}
{%- set template = template
| replace(from="$SERIES_TITLE", to=series_title)
| replace(from="$SERIES_PERMALINK", to=series_permalink)
| replace(from="$SERIES_HTML_LINK", to=series_html_link)
| replace(from="$FIRST_TITLE", to=first_page.title)
| replace(from="$FIRST_HTML_LINK", to='<a href="' ~ first_page.permalink ~ '">' ~ first_page.title ~ '</a>')
| replace(from="$SERIES_PAGES_NUMBER", to=series_pages_number | as_str)
| replace(from="$SERIES_PAGE_INDEX", to=series_page_index | as_str)
| replace(from="$SERIES_PAGES_OLIST", to=series_pages_ordered_list)
| replace(from="$SERIES_PAGES_ULIST", to=series_pages_unordered_list)
-%}
{# Replace navigation variables if they exist #}
{%- if prev_page is defined -%}
{%- set template = template
| replace(from="$PREV_TITLE", to=prev_title)
| replace(from="$PREV_PERMALINK", to=prev_permalink)
| replace(from="$PREV_HTML_LINK", to=prev_html_link)
| replace(from="$PREV_DESCRIPTION", to=prev_description)
-%}
{%- endif -%}
{%- if next_page is defined -%}
{%- set template = template
| replace(from="$NEXT_TITLE", to=next_title)
| replace(from="$NEXT_PERMALINK", to=next_permalink)
| replace(from="$NEXT_HTML_LINK", to=next_html_link)
| replace(from="$NEXT_DESCRIPTION", to=next_description)
-%}
{%- endif -%}
{# Custom placeholders #}
{%- if series_section.extra.series_template_placeholders is defined -%}
{%- set missing_vars = [] -%}
{%- for placeholder in series_section.extra.series_template_placeholders -%}
{%- if placeholder in template -%}
{%- set var_name = placeholder | replace(from="$", to="") | lower -%}
{%- if page.extra.series_template_variables is defined and page.extra.series_template_variables[var_name] is defined -%}
{%- set_global template = template | replace(from=placeholder, to=page.extra.series_template_variables[var_name]) -%}
{%- else -%}
{%- set_global missing_vars = missing_vars | concat(with=var_name) -%}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- if missing_vars | length > 0 -%}
{%- set missing_vars_str = missing_vars | join(sep=", ") -%}
{{ throw(message="ERROR: The following variables are included in this page's series templates (`series_template_placeholders`) but have not been set in the `series_template_variables` of this page: " ~ missing_vars_str) }}
{%- endif -%}
{%- endif -%}
{# Output the processed template if not empty #}
{%- if template | length > 0 -%}
<section class="series-page-{{ template_type }}">
{{ template | markdown | safe }}
</section>
{%- endif -%}
{%- endif -%}
{% endmacro %}
{# Macro for series introduction #}
{% macro get_introduction(page, series_section, series_ordered_pages, language_strings) %}
{{ macros_series_page::process_series_template(template_type="intro", page=page, series_section=series_section, series_ordered_pages=series_ordered_pages, language_strings=language_strings) }}
{% endmacro %}
{# Macro for series outro #}
{% macro get_outro(page, series_section, series_ordered_pages, language_strings) %}
{{ macros_series_page::process_series_template(template_type="outro", page=page, series_section=series_section, series_ordered_pages=series_ordered_pages, language_strings=language_strings) }}
{% endmacro %}

View file

@ -0,0 +1,68 @@
{#
Evaluates the priority of a particular setting across different scopes.
The priority is as follows: page > section > config.
Parameters:
- setting: The name of the setting to evaluate.
- page: The page object containing settings.
- default_global_value: The setting's default value.
#}
{% macro evaluate_setting_priority(setting, page, section="", default_global_value="") %}
{%- if section -%}
{%- set current_section = section -%}
{%- elif page -%}
{%- set current_section = "" -%}
{#- Retrieve last ancestor to determine current section, if applicable -#}
{%- if page.ancestors | length > 0 -%}
{%- set last_ancestor = page.ancestors | slice(start=-1) -%}
{%- set_global current_section = get_section(path=last_ancestor.0, metadata_only=true) -%}
{%- else -%}
{#- We're likely in a nested page. Try to find the parent page or nearest section. -#}
{%- set components = page.components -%}
{%- for i in range(start=1, end=components | length) -%}
{%- if lang == config.default_language -%}
{%- set potential_path = components | slice(end=components | length - i) | join(sep="/") -%}
{%- set potential_page = potential_path ~ "/index.md" -%}
{%- set potential_section = potential_path ~ "/_index.md" -%}
{%- else -%}
{%- set potential_path = components | slice(start=1, end=components | length - i) | join(sep="/") -%}
{%- set potential_page = potential_path ~ "/index." ~ lang ~ ".md" -%}
{%- set potential_section = potential_path ~ "/_index." ~ lang ~ ".md" -%}
{%- endif -%}
{#- Check for parent page first. -#}
{%- set page_data = load_data(path=potential_page, required=false) -%}
{%- if page_data -%}
{%- set_global current_section = get_page(path=potential_page) -%}
{%- break -%}
{%- endif -%}
{#- No parent page, check for section. -#}
{%- set section_data = load_data(path=potential_section, required=false) -%}
{%- if section_data -%}
{%- set_global current_section = get_section(path=potential_section, metadata_only=true) -%}
{%- break -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
{%- endif -%}
{%- set priority_order = [
page.extra[setting] | default(value=""),
current_section.extra[setting] | default(value=""),
config.extra[setting] | default(value="")
] -%}
{%- set output = default_global_value -%}
{%- for value in priority_order -%}
{%- if value != "" -%}
{%- set_global output = value -%}
{%- break -%}
{%- endif -%}
{%- endfor -%}
{{- output -}}
{% endmacro %}

View file

@ -0,0 +1,54 @@
{% macro toc(page, header, language_strings="") %}
{%- set toc_levels = page.extra.toc_levels | default(value=3) -%}
{% if page.extra.toc_ignore_pattern %}
{%- set toc_ignore_pattern = page.extra.toc_ignore_pattern -%}
{% endif %}
<div class="toc-container">
{% if header %}
<h3>{{ macros_translate::translate(key="table_of_contents", default="Table of Contents", language_strings=language_strings) }}</h3>
{% endif %}
<ul>
{% for h1 in page.toc %}
{# Only render headers if there's no ignore pattern, or if the header text doesn't match the pattern. #}
{% if not toc_ignore_pattern or not (h1.title is matching(toc_ignore_pattern)) %}
<li><a href="{{ h1.permalink | safe }}">{{ h1.title }}</a>
{% if h1.children and toc_levels > 1 %}
<ul>
{% for h2 in h1.children %}
{% if not toc_ignore_pattern or not (h2.title is matching(toc_ignore_pattern)) %}
<li><a href="{{ h2.permalink | safe }}">{{ h2.title }}</a>
{% if h2.children and toc_levels > 2 %}
<ul>
{% for h3 in h2.children %}
{% if not toc_ignore_pattern or not (h3.title is matching(toc_ignore_pattern)) %}
<li><a href="{{ h3.permalink | safe }}">{{ h3.title }}</a>
{% if h3.children and toc_levels > 3 %}
<ul>
{% for h4 in h3.children %}
{% if not toc_ignore_pattern or not (h4.title is matching(toc_ignore_pattern)) %}
<li><a href="{{ h4.permalink | safe }}">{{ h4.title }}</a></li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endmacro toc %}

View file

@ -0,0 +1,11 @@
{% macro target_attribute(new_tab) %}
{%- set blank_target = "" -%}
{%- if new_tab -%}
{%- set blank_target = "target=_blank" -%}
{%- endif -%}
{{ blank_target }}
{% endmacro target_attribute %}

View file

@ -0,0 +1,72 @@
{#- Dynamically selects the appropriate translation key based on the provided `number` and `lang` context.
If a `number` is provided, the macro will attempt to pluralize the translation key based on the language's rules.
Parameters:
- `key`: The base key for the translation string, matching the i18n files. Example: `results` to get `zero_results`, `one_results`, `many_results`, etc.
- `number`: Optional. The numerical value associated with the key.
- `language_strings`: A dictionary containing the translation strings.
- `default`: A default string to use if no translation is found for the key.
- `replace`: Optional. If `true`, the macro will replace the `$NUMBER` placeholder in the translation string with the provided `number`.
The macro supports special pluralization rules for:
- Arabic (`ar`): Has unique forms for zero, one, two, few, and many.
- Slavic languages: Pluralization depends on the last digit of the number, with exceptions for numbers ending in 11-14.
NOTE: If the logic for pluralization is modified, it needs to be replicated on the JavaScript search.
Files: static/js/searchElasticlunr.js and its minified version at static/js/searchElasticlunr.min.js
Function name: getPluralizationKey -#}
{% macro translate(key, number=-1, language_strings="", default="", replace=true) %}
{%- set slavic_plural_languages = ["uk", "be", "bs", "hr", "ru", "sr"] -%}
{%- set key_prefix = "" -%}
{#- `zero_` and `one_` are common cases. We handle "many" (plural) later, after language-specific pluralization -#}
{%- if number == 0 -%}
{%- set key_prefix = "zero_" -%}
{%- elif number == 1 -%}
{%- set key_prefix = "one_" -%}
{%- endif -%}
{#- Pluralization -#}
{%- if number != -1 and key_prefix == "" -%}
{#- Arabic -#}
{%- if lang == "ar" -%}
{%- set modulo = number % 100 -%}
{%- if number == 2 -%}
{%- set key_prefix = "two_" -%}
{%- elif modulo >= 3 and modulo <= 10 -%}
{%- set key_prefix = "few_" -%}
{%- else -%}
{%- set key_prefix = "many_" -%}
{%- endif -%}
{#- Slavic languages like Russian or Ukrainian -#}
{%- elif lang in slavic_plural_languages -%}
{%- set modulo10 = number % 10 -%}
{%- set modulo100 = number % 100 -%}
{%- if modulo10 == 1 and modulo100 != 11 -%}
{%- set key_prefix = "one_" -%}
{%- elif modulo10 >= 2 and modulo10 <= 4 -%}
{%- if modulo100 >= 12 and modulo100 <= 14 -%}
{%- set key_prefix = "many_" -%}
{%- else -%}
{%- set key_prefix = "few_" -%}
{%- endif -%}
{%- else -%}
{%- set key_prefix = "many_" -%}
{%- endif -%}
{%- else -%}
{#- Default pluralization -#}
{#- Zero and one are already handled -#}
{%- set key_prefix = "many_" -%}
{%- endif -%}
{%- endif -%}
{#- Translated string -#}
{%- set final_key = key_prefix ~ key -%}
{%- set translated_text = language_strings[final_key] | default(value=default) | safe -%}
{#- Replace $NUMBER with the number -#}
{%- if replace -%}
{%- set translated_text = translated_text | replace(from="$NUMBER", to=number | as_str) -%}
{%- endif -%}
{{- translated_text -}}
{% endmacro %}