Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: add translations #65

Merged
merged 5 commits into from
Nov 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .app/.eleventy.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import { tagsModule } from "./lib/modules/tags/index.js";
import { tocModule } from "./lib/modules/toc/index.js";
import { wikilinksModule } from "./lib/modules/wikilinks/index.js";
import { assetsModule } from "./lib/modules/assets/index.js";
import { translationModule } from "./lib/modules/translation/index.js";
import { core } from "./lib/core/index.js";

export const config = core.configObj;

export default function (eleventyConfig) {
export default async function (eleventyConfig) {
sharedModule.setup(eleventyConfig);
customPropsModule.setup(eleventyConfig);
dynamicContentModule.setup(eleventyConfig);
Expand All @@ -23,6 +24,7 @@ export default function (eleventyConfig) {
tocModule.setup(eleventyConfig);
wikilinksModule.setup(eleventyConfig);
assetsModule.setup(eleventyConfig);
await translationModule.setup(eleventyConfig);

core.setup(eleventyConfig);
}
36 changes: 36 additions & 0 deletions .app/app-translations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// @ts-check
import en from "./i18n/en.js";

/**
* @typedef {typeof en} BaseTranslations;
* @typedef {{ [TKey in keyof BaseTranslations]: BaseTranslations[TKey] | (string & {})}} Translations
* @typedef {{ lang: string; partial: true; translations: Partial<Translations>}
* | { lang: string; partial?: false; translations: Translations}} TranslationConfig
*/

/**
* @param {TranslationConfig} config
* @returns {TranslationConfig} The config
*/
export function defineTranslations(config) {
validateKeys(config);
return config;
}

/**
* @param {TranslationConfig} config
*/
function validateKeys(config) {
const base = Object.keys(en);
const overrides = Object.keys(config.translations);

overrides
.filter((key) => !base.includes(key))
.forEach((key) => console.error(`[Translation] Invalid key '${key}'`));

if (config.partial !== true) {
base
.filter((key) => !overrides.includes(key))
.forEach((key) => console.error(`[Translation] Missing key '${key}'`));
}
}
35 changes: 35 additions & 0 deletions .app/i18n/en.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export default /** @type {const} */ ({
"nav_toggle.label": "Menu",
"search.action": "Search",
"search.noscript": "Please enable JavaScript to use the search.",
"search.shortcut_info": 'Press "/" to activate search',
"search.input.placeholder": "Search...",
"search.popover.placeholder": "Search by title, content and #tags",
"search.popover.searching": "Searching...",
"search.popover.no_results": "Nothing found, try a different search term.",
"theme_switcher.label": "Appearance",
"theme_switcher.options.light": "Light",
"theme_switcher.options.dark": "Dark",
"theme_switcher.options.system": "System",
"skip_link.label": "Skip to content",
"sidebar.label": "Navigation",
"sidebar.sections.main.label": "Main",
"sidebar.sections.main.home_link": "Home",
"sidebar.sections.main.tags_link": "Tags",
"sidebar.sections.main.search_link": "Search",
"sidebar.sections.links.label": "Links",
"sidebar.sections.bookmarks.label": "Bookmarks",
"sidebar.sections.tags.label": "Tags",
"sidebar.navItemToggle.label": "Toggle {{ title }}",
"panel.table_of_contents.label": "On this page",
"panel.custom_properties.label": "Properties",
"panel.tags.label": "Tags",
"panel.incoming_links.label": "Incoming",
"panel.outgoing_links.label": "Outgoing",
"panel.external_links.label": "External",
"bookmark_toggle.label": "Bookmark",
"edit_this_note.label": "Edit",
"page.tags.title": "Tags",
"page.tag.title": "Tag:",
"page.search.title": "Search",
});
4 changes: 2 additions & 2 deletions .app/lib/core/nav-toggle.partial.njk
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
:aria-expanded="$store.nav.open"
@click="$store.nav.toggle()"
x-cloak
lang="en"
lang="{% uiLang %}"
>
<span x-show="!$store.nav.open">{{ 'menu' | feather | safe }}</span>
<span x-show="$store.nav.open">{{ 'x' | feather | safe }}</span>
<span class="visually-hidden">Menu</span>
<span class="visually-hidden">{{ 'nav_toggle.label' | t }}</span>
</button>
</div>
4 changes: 2 additions & 2 deletions .app/lib/modules/bookmarks/bookmark-toggle.macro.njk
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
class="toggle-btn"
:aria-pressed="$store.bookmarks.is('{{ id }}')"
@click="$store.bookmarks.toggle('{{ id }}')"
lang="en"
lang="{% uiLang %}"
>
{{ 'bookmark' | feather | safe }}
<span>Bookmark</span>
<span>{{ 'bookmark_toggle.label' | t }}</span>
</button>
{% endmacro %}
4 changes: 2 additions & 2 deletions .app/lib/modules/notes/edit-this-note.macro.njk
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
href="{{ page | editThisNoteLink(config) }}"
{{ 'target="_blank" rel="noopener"' if config.openInNewTab == true else '' }}
class="toggle-btn"
lang="en"
lang="{% uiLang %}"
>
{{ 'edit-2' | feather({ width: '1em', height: '1em' }) | safe }}
<span>Edit</span>
<span>{{ 'edit_this_note.label' | t }}</span>
</a>
{% endif %}
{% endmacro %}
24 changes: 18 additions & 6 deletions .app/lib/modules/notes/notes-panel.partial.njk
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
x-on:scroll.window.throttle.30ms="onScroll()"
:style="indicatorStyles"
>
<h2 class="toc__label" id="app-panel-toc" lang="en">On this page</h2>
<h2 class="toc__label" id="app-panel-toc" lang="{% uiLang %}">
{{ 'panel.table_of_contents.label' | t }}
</h2>
<ul class="toc__list" role="list" aria-labelledby="app-panel-toc">
{% for item in toc %}
<li style="--level: {{ item.level }}" class="toc__item">
Expand All @@ -27,7 +29,9 @@
{% set props = app.customProperties.properties | resolveCustomProps %}
{% if props | length and app.panel.customProperties %}
<div class="custom-props">
<h2 class="custom-props__label" id="app-panel-props" data-pagefind-ignore lang="en">Properties</h2>
<h2 class="custom-props__label" id="app-panel-props" data-pagefind-ignore lang="{% uiLang %}">
{{ 'panel.custom_properties.label' | t }}
</h2>
<dl class="custom-props__list stack" data-stack="4" role="list" aria-labelledby="app-panel-props">
{% for prop in props %}
<div>
Expand Down Expand Up @@ -57,7 +61,9 @@

{% if tags | length and app.panel.tags %}
<div class="grouped-links">
<h2 class="grouped-links__label" id="app-panel-tags" lang="en">Tags</h2>
<h2 class="grouped-links__label" id="app-panel-tags" lang="{% uiLang %}">
{{ 'panel.tags.label' | t }}
</h2>
<ul class="grouped-links__list" role="list" aria-labelledby="app-panel-tags">
{% for tag in tags %}
{% set tagConfig = collections.tags.byId[tag] %}
Expand All @@ -77,7 +83,9 @@
{% set links = collections._links[page.url]() %}
{% if links.incoming | length and app.panel.incomingLinks %}
<div class="grouped-links" data-pagefind-ignore>
<h2 class="grouped-links__label" id="app-panel-incoming" lang="en">Incoming</h2>
<h2 class="grouped-links__label" id="app-panel-incoming" lang="{% uiLang %}">
{{ 'panel.incoming_links.label' | t }}
</h2>
<ul class="grouped-links__list" role="list" aria-labelledby="app-panel-incoming">
{% for incoming in links.incoming %}
<li>
Expand All @@ -93,7 +101,9 @@

{% if links.outgoing | length and app.panel.outgoingLinks %}
<div class="grouped-links" data-pagefind-ignore>
<h2 class="grouped-links__label" id="app-panel-outgoing" lang="en">Outgoing</h2>
<h2 class="grouped-links__label" id="app-panel-outgoing" lang="{% uiLang %}">
{{ 'panel.outgoing_links.label' | t }}
</h2>
<ul class="grouped-links__list" role="list" aria-labelledby="app-panel-outgoing">
{% for outgoing in links.outgoing %}
<li>
Expand All @@ -109,7 +119,9 @@

{% if links.external | length and app.panel.externalLinks %}
<div class="grouped-links" data-pagefind-ignore>
<h2 class="grouped-links__label" id="app-panel-external" lang="en">External</h2>
<h2 class="grouped-links__label" id="app-panel-external" lang="{% uiLang %}">
{{ 'panel.external_links.label' | t }}
</h2>
<ul class="grouped-links__list" role="list" aria-labelledby="app-panel-external">
{% for external in links.external %}
<li>
Expand Down
4 changes: 2 additions & 2 deletions .app/lib/modules/search/search-button.partial.njk
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<a href="/search/" class="icon-btn" lang="en">
<a href="/search/" class="icon-btn" lang="{% uiLang %}">
{{ 'search' | feather | safe }}
<span class="visually-hidden">Search</span>
<span class="visually-hidden">{{ 'search.action' | t }}</span>
</a>
24 changes: 13 additions & 11 deletions .app/lib/modules/search/search.macro.njk
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
{% set id = uniqueId() %}

{% if mode === 'inline' %}
<noscript lang="en">
Please enable JavaScript to use the search.
<noscript lang="{% uiLang %}">
{{ 'search.noscript' | t }}
</noscript>
{% endif %}

Expand All @@ -23,7 +23,7 @@
aria-expanded="false"
:aria-activedescendant="open && selectedId ? `{{ id('result', '') }}${selectedId}` : ''"
:aria-expanded="open"
placeholder="Search..."
placeholder="{{ 'search.input.placeholder' | t }}"
class="search__input"
autocorrect="off"
autocapitalize="off"
Expand All @@ -34,24 +34,26 @@
@focus="open = true"
@keydown="onKeyDown"
@pageshow.window="term = $el.value"
lang="en"
lang="{% uiLang %}"
{{ 'autofocus' if mode === 'inline' else '' }}
>
{% if mode === 'overlay' %}
<kbd class="search__kbd" aria-hidden="true">/</kbd>
<span class="visually-hidden" lang="en">Press <kbd>/<kbd> to activate search</span>
<span class="visually-hidden" lang="{% uiLang %}">
{{ 'search.shortcut_info' | t }}
</span>
{% endif %}
</div>
<template x-if="open{{ '|| true' if mode === 'inline' else '' }}">
<div class="search__popover" id="{{ id('popup') }}">
<div x-show="!term" class="search__info" lang="en">
Search by title, content and #tags
<div x-show="!term" class="search__info" lang="{% uiLang %}">
{{ 'search.popover.placeholder' | t }}
</div>
<div x-show="!!term && results && !results.length" class="search__info" lang="en">
Nothing found, try a different search term.
<div x-show="!!term && results && !results.length" class="search__info" lang="{% uiLang %}">
{{ 'search.popover.no_results' | t }}
</div>
<div x-show="!!term && !results" class="search__info" lang="en">
Searching...
<div x-show="!!term && !results" class="search__info" lang="{% uiLang %}">
{{ 'search.popover.searching' | t }}
</div>

<ul x-show="term && results && results.length" class="search__results" role="listbox" aria-label="Search Results">
Expand Down
2 changes: 1 addition & 1 deletion .app/lib/modules/sidebar/sidebar-tree.macro.njk
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
>
{{ 'chevron-right' | feather | safe }}
<span class="visually-hidden">
Toggle {{ item.title }}
{{ 'sidebar.navItemToggle.label' | t({ title: item.title }) }}
</span>
</button>
{% endif %}
Expand Down
26 changes: 16 additions & 10 deletions .app/lib/modules/sidebar/sidebar.partial.njk
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{% from 'modules/sidebar/sidebar-tree.macro.njk' import sidebarTree %}

<aside>
<h2 class="visually-hidden" lang="en">Navigation</h2>
<h2 class="visually-hidden" lang="{% uiLang %}">{{ 'sidebar.title' | t }}</h2>

<nav aria-labelledby="app:sidebar:nav" class="nav-section">
<h3 id="app:sidebar:nav" class="visually-hidden" lang="en">Main</h3>
<h3 id="app:sidebar:nav" class="visually-hidden" lang="{% uiLang %}">{{ 'sidebar.sections.main.label' | t }}</h3>
<ul class="nav-list" role="list" aria-labelledby="app:sidebar:nav">
<li>
<a
Expand All @@ -15,7 +15,7 @@
{{ 'home' | feather | safe }}
{% set indexQuery = { filter: [['url', 'isEqual', '/' ]] } %}
{% set indexNote = (collections._notes | query(indexQuery)) | first %}
<span>{{ indexNote.data.title if indexNote else 'Home' }}</span>
<span>{{ indexNote.data.title if indexNote else ('sidebar.sections.main.home_link' | t) }}</span>
</a>
</li>
{% if collections.tags.length %}
Expand All @@ -24,10 +24,10 @@
href="/tags/"
{{ '/tags/' | ariaCurrent }}
class="nav-list__link"
lang="en"
lang="{% uiLang %}"
>
{{ 'hash' | featherRef | safe }}
<span>Tags</span>
<span>{{ 'sidebar.sections.main.tags_link' | t }}</span>
</a>
</li>
{% endif %}
Expand All @@ -36,18 +36,20 @@
href="/search/"
{{ '/search/' | ariaCurrent }}
class="nav-list__link"
lang="en"
lang="{% uiLang %}"
>
{{ 'search' | feather | safe }}
<span>Search</span>
<span>{{ 'sidebar.sections.main.search_link' | t }}</span>
</a>
</li>
</ul>
</nav>

{% if app.sidebar.links.length %}
<nav aria-labelledby="app:sidebar:section:links" class="nav-section nav-section--continued">
<h3 id="app:sidebar:section:links" class="visually-hidden" lang="en">Links</h3>
<h3 id="app:sidebar:section:links" class="visually-hidden" lang="{% uiLang %}">
{{ 'sidebar.sections.links.label' | t }}
</h3>
<ul class="nav-list" role="list" aria-labelledby="app:sidebar:section:links">
{% for item in app.sidebar.links %}
<li>
Expand All @@ -67,7 +69,9 @@

<template x-if="$store.bookmarks.items.length">
<nav aria-labelledby="app:sidebar:section:bookmarks" class="nav-section" x-cloak>
<h3 id="app:sidebar:section:bookmarks" class="nav-section__title" lang="en">Bookmarks</h3>
<h3 id="app:sidebar:section:bookmarks" class="nav-section__title" lang="{% uiLang %}">
{{ 'sidebar.sections.bookmarks.label' | t }}
</h3>
<ul class="nav-list" role="list" aria-labelledby="app:sidebar:section:bookmarks">
<template x-for="bkm in $store.bookmarks.items">
<li>
Expand Down Expand Up @@ -122,7 +126,9 @@

{% if collections.tags.length %}
<nav aria-labelledby="app:sidebar:section:tags" class="nav-section">
<h3 id="app:sidebar:section:tags" class="nav-section__title" lang="en">Tags</h3>
<h3 id="app:sidebar:section:tags" class="nav-section__title" lang="{% uiLang %}">
{{ 'sidebar.sections.tags.label' | t }}
</h3>
<ul class="nav-list" role="list" aria-labelledby="app:sidebar:section:tags">
{% for tag in collections.tags %}
<li>
Expand Down
8 changes: 4 additions & 4 deletions .app/lib/modules/themes/theme-switcher.partial.njk
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% set id = uniqueId() %}

<div role="radiogroup" aria-label="Appearance" class="theme-switcher" x-data x-cloak lang="en">
<div role="radiogroup" aria-label="{{ 'theme_switcher.label' | t }}" class="theme-switcher" x-data x-cloak lang="{% uiLang %}">
<input
id="{{ id('light') }}"
type="radio"
Expand All @@ -9,7 +9,7 @@
value="light"
class="visually-hidden"
>
<label for="{{ id('light') }}" class="theme-switcher__label" aria-label="Light">
<label for="{{ id('light') }}" class="theme-switcher__label" aria-label="{{ 'theme_switcher.options.light' | t}}">
{{ 'sun' | feather | safe }}
</label>

Expand All @@ -21,7 +21,7 @@
value="dark"
class="visually-hidden"
>
<label for="{{ id('dark') }}" class="theme-switcher__label" aria-label="Dark">
<label for="{{ id('dark') }}" class="theme-switcher__label" aria-label="{{ 'theme_switcher.options.dark' | t }}">
{{ 'moon' | feather | safe }}
</label>

Expand All @@ -33,7 +33,7 @@
value="system"
class="visually-hidden"
>
<label for="{{ id('system') }}" class="theme-switcher__label" aria-label="System">
<label for="{{ id('system') }}" class="theme-switcher__label" aria-label="{{ 'theme_switcher.options.system' | t }}">
{{ 'monitor' | feather | safe }}
</label>
</div>
13 changes: 13 additions & 0 deletions .app/lib/modules/translation/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { translateFilter } from "./translate.filter.js";
import { uiLangShortcode } from "./ui-lang.shortcode.js";

export const translationModule = {
/**
* Sets up the module
* @param {import("@11ty/eleventy").UserConfig} config
*/
async setup(config) {
config.addFilter("t", await translateFilter(config));
config.addShortcode("uiLang", await uiLangShortcode(config));
},
};
Loading