mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 14:27:49 +00:00
Various 3rd edition template improvements
- merge improvements from second edition (e.g. improved light switch, prefered theme in session storage, translation support) - giscus instead of utterances - add an alpha warning - fix error caused by missing posts etc
This commit is contained in:
@@ -1,3 +1,21 @@
|
||||
+++
|
||||
template = "edition-3/index.html"
|
||||
+++
|
||||
|
||||
<h1>Writing an OS in Rust</h1>
|
||||
|
||||
<h1 style="visibility: hidden; height: 0px; margin: 0px; padding: 0px;">Writing an OS in Rust</h1>
|
||||
|
||||
<p>A blog by Philipp Oppermann <em class="gray">— Third Edition (Alpha Release)</em></p>
|
||||
|
||||
<div class="front-page-introduction">
|
||||
|
||||
This blog series creates a small operating system in the [Rust programming language](https://www.rust-lang.org/). Each post is a small tutorial and includes all needed code, so you can follow along if you like. The source code is also available in the corresponding [Github repository](https://github.com/phil-opp/blog_os).
|
||||
|
||||
<!-- alpha-warning -->
|
||||
|
||||
We explain how to create an operating system for the **`x86_64`** architecture step by step. Starting from scratch, we create a bootable OS kernel, implement basic input/output support, show how to test and debug our kernel, explain virtual memory management, and add support for multitasking and userspace programs.
|
||||
|
||||
Latest post: <!-- latest-post -->
|
||||
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
{% block after_main %}
|
||||
<hr>
|
||||
<section>
|
||||
<h2>Comments</h2>
|
||||
{{ snippets::utterances() }}
|
||||
<h2 id="comments">Comments</h2>
|
||||
{{ snippets::giscus(search_term=page.title ~ " (Extra Post)") }}
|
||||
</section>
|
||||
{% endblock after_main %}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="description" content="{% block description %}{{ config.description }}{% endblock description %}">
|
||||
<meta name="author" content="{{ config.extra.author.name }}">
|
||||
|
||||
@@ -16,6 +17,13 @@
|
||||
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS feed for os.phil-opp.com" href="{{ config.base_url | safe }}/rss.xml" />
|
||||
|
||||
<script>
|
||||
let theme = localStorage.getItem("theme");
|
||||
if (theme != null) {
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script async src="/js/edition-3/main.js"></script>
|
||||
|
||||
<title>{% block title %}{% endblock title %} (Third Edition - Alpha)</title>
|
||||
@@ -34,30 +42,19 @@
|
||||
|
||||
<footer class="footer">
|
||||
<hr>
|
||||
<div class="theme-switch">
|
||||
<div class="light-switch" onclick="toggle_lights()" title="Switch between light and dark theme"></div>
|
||||
<div class="light-switch-reset" onclick="clear_theme_override()" title="Clear the theme override and go back to the system theme"></div>
|
||||
</div>
|
||||
<small>
|
||||
© <time datetime="2020">2020</time>. All rights reserved.
|
||||
<a href="{{ get_url(path="@/pages/contact.md") | safe }}">Contact</a>
|
||||
© <time datetime="2022">2022</time>. All rights reserved.
|
||||
<a class="spaced" href="https://github.com/phil-opp/blog_os#license">License</a>
|
||||
<a class="spaced" href="{{ get_url(path="@/pages/contact.md") | safe }}">Contact</a>
|
||||
</small>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="light-switch" onclick="toggle_lights()"></div>
|
||||
|
||||
<!-- Fathom - simple website analytics - https://github.com/usefathom/fathom -->
|
||||
<script>
|
||||
(function(f, a, t, h, o, m){
|
||||
a[h]=a[h]||function(){
|
||||
(a[h].q=a[h].q||[]).push(arguments)
|
||||
};
|
||||
o=f.createElement('script'),
|
||||
m=f.getElementsByTagName('script')[0];
|
||||
o.async=1; o.src=t; o.id='fathom-script';
|
||||
m.parentNode.insertBefore(o,m)
|
||||
})(document, window, '//fathom.phil-opp.com/tracker.js', 'fathom');
|
||||
fathom('set', 'siteId', 'MUXWM');
|
||||
fathom('trackPageview');
|
||||
</script>
|
||||
<!-- / Fathom -->
|
||||
<script data-goatcounter="https://phil-opp.goatcounter.com/count" async src="//gc.zgo.at/count.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -6,34 +6,20 @@
|
||||
{% block title %}{{ config.title }}{% endblock title %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
{% set posts_section = get_section(path = "edition-3/posts/_index.md") %}
|
||||
{% set posts = posts_section.pages %}
|
||||
|
||||
<h1>Writing an OS in Rust</h1>
|
||||
<p>{{ config.extra.subtitle | replace(from=" ", to=" ") | safe }} <em class="gray">— Third Edition (Alpha Release)</em></p>
|
||||
|
||||
<div class="front-page-introduction">
|
||||
<p>
|
||||
This blog series creates a small operating system in the
|
||||
<a href="https://www.rust-lang.org/">Rust programming language</a>. Each post is a small tutorial and includes all
|
||||
needed code, so you can follow along if you like. The source code is also available in the corresponding
|
||||
<a href="https://github.com/phil-opp/blog_os">Github repository</a>.
|
||||
</p>
|
||||
|
||||
<p>Latest post:
|
||||
{% set latest_post = posts|last %}
|
||||
<strong><a href="{{ latest_post.path | safe }}">{{ latest_post.title }}</a></strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<p>In the following posts, we explain how to create an operating system for the <code>x86_64</code> architecture step for step. Starting from scratch, we create a bootable OS kernel, implement basic input/output support, show how to test and debug our kernel, explain virtual memory management, and add support for multitasking and userspace programs.</p>
|
||||
-->
|
||||
{{ section.content
|
||||
| replace(from="<!-- latest-post -->", to=macros::latest_post(posts=posts))
|
||||
| replace(from="<!-- alpha-warning -->", to=macros::alpha_warning())
|
||||
| safe
|
||||
}}
|
||||
|
||||
{%- set chapter = "none" -%}
|
||||
{%- for post in posts -%}
|
||||
{%- if post.extra["chapter"] != chapter -%}
|
||||
{%- if chapter != "none" -%}
|
||||
{%- if not loop.first -%}
|
||||
</ul></div>
|
||||
{%- endif -%}
|
||||
|
||||
@@ -52,8 +38,11 @@
|
||||
{%- endif -%}
|
||||
|
||||
<li>{{ macros::post_link(page=post) }}</li>
|
||||
|
||||
{% if loop.last %}
|
||||
</ul></div>
|
||||
{% endif %}
|
||||
{%- endfor -%}
|
||||
</ul></div>
|
||||
|
||||
<hr>
|
||||
|
||||
@@ -83,10 +72,10 @@
|
||||
<p>You are currently viewing the third edition of “Writing an OS in Rust”. In case you are interested in the older editions, you can still find them here:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong><a href="{{ get_url(path="@/edition-2/_index.md")}}">Second Edition:</a></strong> The second edition is based on older version of the <code>bootloader</code> crate, which uses the hardware-provided VGA text buffer instead of a pixel-based framebuffer for screen output. Instead of the APIC, the legacy PIC is used for implementing hardware interrupts. The second edition only works on BIOS-based systems, not on the newer UEFI standard. <a class="read-more" href="{{ get_url(path = "edition-2") | safe }}"><em>read the second edition »</em></a></p>
|
||||
<p><strong><a href="{{ get_url(path="@/edition-2/_index.md")}}">Second Edition:</a></strong> The second edition is based on older version of the <code>bootloader</code> crate, which uses the hardware-provided VGA text buffer instead of a pixel-based framebuffer for screen output. Instead of the APIC, the legacy PIC is used for implementing hardware interrupts. The second edition only works on BIOS-based systems, not on the newer UEFI standard. <a class="read-more" href="{{ get_url(path = "edition-2") | safe }}"><em>read the second edition »</em></a></p>
|
||||
</li>
|
||||
<li>
|
||||
<strong><a href="{{ get_url(path="@/edition-1/_index.md")}}">First Edition:</a></strong> The first edition was already started in 2015. It is very different in many aspects, for example it builds upon the GRUB bootloader instead of using the `bootloader` crate. This means that it requires you to manually write some assembly code and do an elaborate remap of the kernel's virtual pages in order to improve safety.<a class="read-more" href="{{ get_url(path = "edition-1") | safe }}"><em>read the first edition »</em></a></p>
|
||||
<p><strong><a href="{{ get_url(path="@/edition-1/_index.md")}}">First Edition:</a></strong> The first edition was already started in 2015. It is very different in many aspects, for example it builds upon the GRUB bootloader instead of using the `bootloader` crate. This means that it requires you to manually write some assembly code and do an elaborate remap of the kernel's virtual pages in order to improve safety.<a class="read-more" href="{{ get_url(path = "edition-1") | safe }}"><em>read the first edition »</em></a></p>
|
||||
</li>
|
||||
</ul>
|
||||
<p><em>Note that the older editions are no longer updated and might no longer work or contain outdated information.</em></p>
|
||||
@@ -100,22 +89,20 @@
|
||||
|
||||
{% block after_main %}
|
||||
<aside class="page-aside-right">
|
||||
{% if section.translations | length > 1 %}
|
||||
<div class="block" id="language-selector">
|
||||
{% if section.translations -%}
|
||||
<div class="block" id="language-selector">
|
||||
<h2>Other Languages</h2>
|
||||
<ul>{%- for translation in section.translations | sort(attribute="lang") %}
|
||||
<h2>Other Languages</h2>
|
||||
{% set translations = section.translations | group_by(attribute="lang") %}
|
||||
<ul>{%- for lang_code in config.extra.languages -%}
|
||||
{%- if translations[lang_code] and lang_code != lang -%}
|
||||
{%- set translation = translations[lang_code].0 -%}
|
||||
<li data-lang-switch-to="{{ translation.lang }}" class=""><a href="{{ translation.permalink | safe }}">
|
||||
{%- if translation.lang == "en" -%}
|
||||
English (original)
|
||||
{%- else -%}
|
||||
{{ trans(key="lang_name", lang = translation.lang) }}
|
||||
{%- endif -%}
|
||||
{{ trans(key="lang_name", lang = translation.lang) }}
|
||||
</a></li>
|
||||
{% endfor %}</ul>
|
||||
</div>
|
||||
{%- endif %}
|
||||
{%- endif -%}
|
||||
{% endfor %}</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="block">
|
||||
<h2>Recent Updates</h2>
|
||||
{% include "auto/recent-updates.html" %}
|
||||
|
||||
@@ -1,71 +1,89 @@
|
||||
{% macro latest_post(posts) %}
|
||||
{% set post = posts|last %}
|
||||
<strong><a href="{{ post.path | safe }}">{{ post.title }}</a></strong>
|
||||
{% set post = posts|last %}
|
||||
{% if post %}
|
||||
<strong><a href="{{ post.path | safe }}">{{ post.title }}</a></strong>
|
||||
{% else %}
|
||||
<em>none yet, stay tuned!</em>
|
||||
{% endif %}
|
||||
{% endmacro latest_post %}
|
||||
|
||||
{% macro post_link(page) %}
|
||||
<div>
|
||||
{% set translations = page.translations | filter(attribute="lang", value=lang) -%}
|
||||
{%- if translations -%}
|
||||
{%- set post = get_page(path = translations.0.path) -%}
|
||||
{%- else -%}
|
||||
{%- set post = page -%}
|
||||
{%- set not_translated = true -%}
|
||||
<div>
|
||||
{% set translations = page.translations | filter(attribute="lang", value=lang) -%}
|
||||
{%- if translations -%}
|
||||
{%- set post = get_page(path = translations.0.path) -%}
|
||||
{%- else -%}
|
||||
{%- set post = page -%}
|
||||
{%- set not_translated = true -%}
|
||||
{%- endif -%}
|
||||
<h3 class="post-list-title"><a href="{{ post.path | safe }}">{{ post.title }}</a></h3>
|
||||
<span class="post-list-icon">
|
||||
{%- if post.extra.icon -%}{{post.extra.icon | safe}}{%- endif -%}
|
||||
</span>
|
||||
<div class="post-summary">
|
||||
{{ post.summary | safe }}
|
||||
<a class="read-more" href="{{ post.path | safe }}"><em>read more »</em></a>
|
||||
|
||||
{% if page.extra.extra_content %}
|
||||
<aside class="post-extra-content">
|
||||
<h4>Extra Content:</h4>
|
||||
{% for name in page.extra.extra_content %}
|
||||
{% set path = page.relative_path | split(pat="/") | slice(end=-1) | concat(with=name) | join(sep="/") %}
|
||||
|
||||
{% set extra_page = get_page(path = path) %}
|
||||
|
||||
<span class="post-list-extra-post-icon">
|
||||
{%- if extra_page.extra.icon -%}{{extra_page.extra.icon | safe}}{%- endif -%}
|
||||
</span>
|
||||
<a href="{{ extra_page.path | safe }}">{{ extra_page.title }}</a>{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
</aside>
|
||||
{% endif %}
|
||||
|
||||
{%- if lang and not_translated and lang != config.default_language -%}
|
||||
<aside class="no-translation">
|
||||
(This post is not translated yet.)
|
||||
</aside>
|
||||
{%- endif -%}
|
||||
<h3 class="post-list-title"><a href="{{ post.path | safe }}">{{ post.title }}</a></h3>
|
||||
<span class="post-list-icon">
|
||||
{%- if post.extra.icon -%}{{post.extra.icon | safe}}{%- endif -%}
|
||||
</span>
|
||||
<div class="post-summary">
|
||||
{{ post.summary | safe }}
|
||||
<a class="read-more" href="{{ post.path | safe }}"><em>read more »</em></a>
|
||||
|
||||
{% if page.extra.extra_content %}
|
||||
<aside class="post-extra-content">
|
||||
<h4>Extra Content:</h4>
|
||||
{% for name in page.extra.extra_content %}
|
||||
{% set path = page.relative_path | split(pat="/") | slice(end=-1) | concat(with=name) | join(sep="/") %}
|
||||
|
||||
{% set extra_page = get_page(path = path) %}
|
||||
|
||||
<a href = "{{ extra_page.path | safe }}">{{ extra_page.title }}</a>{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
</aside>
|
||||
{% endif %}
|
||||
|
||||
{%- if lang and not_translated and lang != config.default_language -%}
|
||||
<aside class="no-translation">
|
||||
(This post is not translated yet.)
|
||||
</aside>
|
||||
{%- endif -%}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro post_link %}
|
||||
|
||||
{% macro toc(toc) %}
|
||||
<details id = "toc-inline">
|
||||
<summary><b>Table of Contents</b></summary>
|
||||
<ul>
|
||||
{% for h2 in toc %}<li>
|
||||
<a href="#{{h2.id | safe}}">{{ h2.title | safe }}</a>
|
||||
{% if h2.children %}<ul>
|
||||
{% for h3 in h2.children %}<li>
|
||||
<a href="#{{h3.id | safe}}">{{ h3.title | safe }}</a>
|
||||
</li>{% endfor %}
|
||||
</ul>{% endif %}
|
||||
</li>{% endfor %}
|
||||
<li class="toc-comments-link"><a href="#comments">Comments</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details id="toc-inline">
|
||||
<summary><b>Table of Contents</b></summary>
|
||||
<ul>
|
||||
{% for h2 in toc %}<li>
|
||||
<a href="#{{h2.id | safe}}">{{ h2.title | safe }}</a>
|
||||
{% if h2.children %}<ul>
|
||||
{% for h3 in h2.children %}<li>
|
||||
<a href="#{{h3.id | safe}}">{{ h3.title | safe }}</a>
|
||||
</li>{% endfor %}
|
||||
</ul>{% endif %}
|
||||
</li>{% endfor %}
|
||||
<li class="toc-comments-link"><a href="#comments">Comments</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<div class="theme-switch-inline">
|
||||
Switch between light and dark mode:
|
||||
<span class="switches">
|
||||
<span class="light-switch" onclick="toggle_lights()" title="Switch between light and dark theme"></span>
|
||||
<span class="light-switch-reset" onclick="clear_theme_override()"
|
||||
title="Clear the theme override and go back to the system theme"></span>
|
||||
</span>
|
||||
</div>
|
||||
{% endmacro toc %}
|
||||
|
||||
{% macro utterances() %}
|
||||
<script src="https://utteranc.es/client.js"
|
||||
data-repo="phil-opp/blog_os"
|
||||
data-issue-term="url"
|
||||
data-label="comments"
|
||||
crossorigin="anonymous"
|
||||
theme="preferred-color-scheme"
|
||||
async>
|
||||
</script>
|
||||
{% endmacro utterances %}
|
||||
{% macro alpha_warning() %}
|
||||
<div class="warning">
|
||||
<p>
|
||||
This is an <strong>early preview</strong> of the upcoming <em>third edition</em> of this guide. The edition is
|
||||
still in alpha state, so things might be still in progress, not work, or change without warning!
|
||||
</p>
|
||||
<p>
|
||||
For a more stable experience, check out the current <a href="{{ get_url(path = " @/edition-2/_index.md") | safe
|
||||
}}"><strong>Second Edition</strong></a>.
|
||||
</p>
|
||||
</div>
|
||||
{% endmacro alpha_warning %}
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
{% block title %}{{ page.title }} | {{ config.title }}{% endblock title %}
|
||||
{% block header %}
|
||||
{% if lang != "en" -%}
|
||||
<aside id="all-posts-link"><a href="{{ get_url(path="@/edition-3/_index.md") }}/{{ lang }}" title="All Posts">« All Posts</a></aside>
|
||||
<aside id="all-posts-link"><a href="{{ get_url(path="@/edition-3/_index.md") }}/{{ lang }}" title="All Posts">{{ trans(key="all_posts", lang=lang) }}</a></aside>
|
||||
{%- else -%}
|
||||
<aside id="all-posts-link"><a href="{{ get_url(path="@/edition-3/_index.md") }}" title="All Posts">« All Posts</a></aside>
|
||||
<aside id="all-posts-link"><a href="{{ get_url(path="@/edition-3/_index.md") }}" title="All Posts">{{ trans(key="all_posts", lang=lang) }}</a></aside>
|
||||
{%- endif %}
|
||||
{% endblock header %}
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
{%- endblock description %}
|
||||
|
||||
{% block toc_aside %}
|
||||
<aside id="toc-aside">
|
||||
<h2>Table of Contents</h2>
|
||||
<aside id="toc-aside" class="{% if page.extra.rtl %}right-to-left{% endif %}">
|
||||
<h2>{{ trans(key="toc", lang=lang) }}</h2>
|
||||
<ol>
|
||||
{% for h2 in page.toc %}<li>
|
||||
<a href="#{{h2.id | safe}}">{{ h2.title | safe }}</a>
|
||||
@@ -28,7 +28,7 @@
|
||||
</li>{% endfor %}
|
||||
</ol>{% endif %}
|
||||
</li>{% endfor %}
|
||||
<li class="toc-comments-link"><a href="#comments">Comments</a></li>
|
||||
<li class="toc-comments-link"><a href="#comments">{{ trans(key="comments", lang=lang) }}</a></li>
|
||||
</ol>
|
||||
</aside>
|
||||
{% endblock toc_aside %}
|
||||
@@ -47,6 +47,8 @@
|
||||
</time>
|
||||
</div>
|
||||
|
||||
{{ macros::alpha_warning() }}
|
||||
|
||||
{% if page.extra.warning %}
|
||||
<div class="warning">
|
||||
{% if page.extra.warning_short %} <b>{{ page.extra.warning_short }}</b> {% endif %}
|
||||
@@ -55,18 +57,20 @@
|
||||
{% endif %}
|
||||
|
||||
{%- if page.lang != "en" %}
|
||||
<div class="warning">
|
||||
<div class="warning{% if page.extra.rtl %} right-to-left{% endif %}">
|
||||
{% set translations = page.translations | filter(attribute="lang", value="en") %}
|
||||
{% set original = translations.0 %}
|
||||
<p>
|
||||
<b>Translated Content:</b>
|
||||
This is a community translation of the <strong><a href="{{ original.permalink }}">{{ original.title }}</a></strong> post. It might be incomplete, outdated or contain errors. Please report any issues!
|
||||
<b>{{ trans(key="translated_content", lang=lang) }}</b>
|
||||
{{ trans(key="translated_content_notice", lang=lang) |
|
||||
replace(from="_original.permalink_", to=original.permalink) |
|
||||
replace(from="_original.title_", to=original.title) | safe }}
|
||||
</p>
|
||||
{%- if page.extra.translators %}
|
||||
<p>
|
||||
Translation by {% for user in page.extra.translators -%}
|
||||
{{ trans(key="translated_by", lang=lang) }} {% for user in page.extra.translators -%}
|
||||
{%- if not loop.first -%}
|
||||
{%- if loop.last %}, and {% else %}, {% endif -%}
|
||||
{%- if loop.last %} {{ trans(key="word_separator", lang=lang) }} {% else %}, {% endif -%}
|
||||
{%- endif -%}
|
||||
<a href="https://github.com/{{user}}">@{{user}}</a>
|
||||
{%- endfor %}.
|
||||
@@ -75,11 +79,11 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="{% if page.extra.rtl %}right-to-left{% endif %}">
|
||||
<div class="{% if page.extra.rtl %}right-to-left{% endif %}">
|
||||
{{ page.content | replace(from="<!-- toc -->", to=macros::toc(toc=page.toc)) | safe }}
|
||||
</div>
|
||||
|
||||
<div class="post-footer-support">
|
||||
<div class="post-footer-support{% if page.extra.rtl %} right-to-left{% endif %}">
|
||||
<h2>Support Me</h2>
|
||||
{{ snippets::support() }}
|
||||
</div>
|
||||
@@ -98,41 +102,41 @@
|
||||
|
||||
<hr>
|
||||
<section>
|
||||
<h2 id="comments">Comments</h2>
|
||||
<h2 id="comments" class="{% if page.extra.rtl %}right-to-left{% endif %}">{{ trans(key="comments", lang=lang) }}</h2>
|
||||
|
||||
{% if page.extra.comments_search_term %}
|
||||
{% set search_term=page.extra.comments_search_term %}
|
||||
{% elif page.lang != "en" %}
|
||||
{% set translations = page.translations | filter(attribute="lang", value="en") %}
|
||||
{% set original = translations.0 %}
|
||||
{% set search_term=original.title ~ " (" ~ page.lang ~ ")" %}
|
||||
{% else %}
|
||||
{% set search_term=page.title %}
|
||||
{% endif %}
|
||||
{{ snippets::giscus(search_term=search_term) }}
|
||||
|
||||
{%- if page.lang != "en" %}
|
||||
<p>
|
||||
Please leave your comments in English if possible.
|
||||
<p class="{% if page.extra.rtl %}right-to-left{% endif %}">
|
||||
{{ trans(key="comments_notice", lang=lang) }}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{{ macros::utterances() }}
|
||||
</section>
|
||||
|
||||
<aside class="page-aside-right">
|
||||
{% if page.translations -%}
|
||||
{% if page.translations | length > 1-%}
|
||||
<div class="block" id="language-selector">
|
||||
<h2>Other Languages</h2>
|
||||
<ul>{%- for translation in page.translations | sort(attribute="lang") %}
|
||||
<li data-lang-switch-to="{{ translation.lang }}" class=""><a href="{{ translation.permalink | safe }}">
|
||||
{%- if translation.lang == "en" -%}
|
||||
English (original)
|
||||
{%- else -%}
|
||||
{% set translations = page.translations | group_by(attribute="lang") %}
|
||||
<ul>{%- for lang_code in config.extra.languages -%}{%- if translations[lang_code] -%}
|
||||
{%- set translation = translations[lang_code] | first -%}
|
||||
{%- if translation and lang_code != lang -%}
|
||||
<li data-lang-switch-to="{{ translation.lang }}" class=""><a href="{{ translation.permalink | safe }}">
|
||||
{{ trans(key="lang_name", lang = translation.lang) }}
|
||||
{%- endif -%}
|
||||
</a></li>
|
||||
{% endfor %}</ul>
|
||||
</a></li>
|
||||
{%- endif -%}
|
||||
{%- endif -%}{% endfor %}</ul>
|
||||
</div>
|
||||
{%- endif %}
|
||||
|
||||
<div class="block">
|
||||
<h2>About Me</h2>
|
||||
<p>
|
||||
I'm a Rust freelancer with a master's degree in computer science. I love systems programming, open source software, and new challenges.
|
||||
</p><p>
|
||||
If you want to work with me, reach out on <a href = "https://www.linkedin.com/in/phil-opp/">LinkedIn</a> or write me at <a href="mailto:job@phil-opp.com">job@phil-opp.com</a>.
|
||||
</p>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{% endblock main %}
|
||||
|
||||
Reference in New Issue
Block a user