Skip to content

Commit

Permalink
Add localization and 'de' locale to website
Browse files Browse the repository at this point in the history
  • Loading branch information
salmenf committed Nov 21, 2024
1 parent 25a6f61 commit 797dbea
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 47 deletions.
10 changes: 10 additions & 0 deletions @webwriter/website/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,15 @@ export default defineConfig({
},
}]
]
},
i18n: {
defaultLocale: "en",
locales: ["en", "de"],
fallback: {
"de": "en"
},
routing: {
fallbackType: "rewrite"
}
}
});
34 changes: 34 additions & 0 deletions @webwriter/website/src/components/LanguagePicker.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
import { getRelativeLocaleUrl } from "astro:i18n"
import {languages, getLocalizedURL} from "../localize"
---
<ul class="lang-options">
{Object.entries(languages).map(([l, labels]) => (
<li id={l} class="lang-option">
<a href={getLocalizedURL(Astro.url, l)}>
<span class="lang-icon">{labels._}</span>
<span>{labels[l as keyof typeof languages]} ({labels["en"]})</span>
</a>
</li>
))}
</ul>

<style scoped>
.lang-options {
list-style: none;
}

.lang-option a {
display: flex;
flex-direction: row;
align-items: center;
gap: 1ch;
}

.lang-icon {
font-size: 2rem;
-webkit-text-stroke: 4px white;
}

</style>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ title: Explorables
# Explorables
WebWriter is built to edit explorables, which are...
- **open**, conformant with OER principles allowing easy reuse
- **multimedia**, combining many media types such as text, audio, video, etc.
- **multimodal**, combining many media types such as text, audio, video, etc.
- **interactive**, allowing users to interact and receive feedback
- **web based**, built with client-side web standards and deployable on the web
- **file oriented**, available as all-in-one, standalone, offline-capable files
Expand Down
52 changes: 38 additions & 14 deletions @webwriter/website/src/layouts/Layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,23 @@ import "@shoelace-style/shoelace/dist/themes/light.css"
import {EmailAddress} from "@components/EmailAddress"
import iconUrl from "@assets/app-icon.svg?url"
import {msgResolver} from "../localize"
const lang = Astro.currentLocale!
const translations = {
"docs": {"de": "Dokumentation"},
"widgets": {"de": "Widgets"},
"about": {"de": "Über uns"},
"get_started": {"de": "Ausprobieren"},
"Get Started": {"de": "Ausprobieren"},
"Features": {"de": "Funktionen"},
"© Frederic Salmen. All rights reserved.": {"de": "© Frederic Salmen. Alle Rechte vorbehalten."},
"Legal Notice": {"de": "Impressum"},
"Pages": {"de": "Seiten"}
}
const msg = msgResolver(translations, lang)
export interface Props {
title?: string;
}
Expand All @@ -16,13 +33,15 @@ function capitalize(str: string) {
}
const pageFiles = (await Astro.glob("../pages/**/*.astro")).filter(page => !page.url?.endsWith("]") && !page.url?.endsWith("404") && !page?.url?.endsWith("legal_notice") && !page?.url?.endsWith("dashboard") && page.url)
const pageUrls = pageFiles.map(page => page.url ?? "")
const pageUrls = pageFiles
.map(page => page.url ?? "")
.map(url => getRelativeLocaleUrl(Astro.currentLocale!, url))
const pageOrder = ["", "docs", "widgets", "about", "get_started"]
const pages = pageUrls
.map(relUrl => {
let id = relUrl?.split("/").at(-1) as string
let id = relUrl?.split("/").at(-2) as string
const rootLocation = relUrl?.split("/").at(1) as string
let name = id? id.split("_").map(capitalize).join(" "): "Features"
let name = id? msg(id).split("_").map(capitalize).join(" "): msg("Features")
let active = rootLocation === pathname.split("/")?.at(1)
let urlObj = new URL(Astro.request.url); urlObj.pathname = relUrl
let url = urlObj.href
Expand All @@ -31,13 +50,15 @@ const pages = pageUrls
.sort((a, b) => {
return pageOrder.indexOf(a.id) - pageOrder.indexOf(b.id)
})
const rootActive = pathname === "/"
const getStartedActive = pathname === "/get_started"
import "bootstrap-icons/font/bootstrap-icons.css"
import { SEO } from "astro-seo";
import LanguagePicker from "@components/LanguagePicker.astro";
import { getRelativeLocaleUrl } from "astro:i18n";
---

<!DOCTYPE html>
Expand All @@ -58,9 +79,9 @@ import { SEO } from "astro-seo";
</head>
<body>
<header id="layout-header">
<a data-swup-preload href="/" class="logo">
<a data-swup-preload href={getRelativeLocaleUrl(Astro.currentLocale!)} class="logo">
<img src={iconUrl} alt="WebWriter Icon" width={40} height={40} />
<span class="logo-text">WebWriter <sup>Alpha</sup></span>
<span class="logo-text">{msg("WebWriter")} <sup>{msg("Alpha")}</sup></span>
</a>
<nav>
<label class="menu-toggle bi-list" for="menu-toggle"></label>
Expand All @@ -70,10 +91,10 @@ import { SEO } from "astro-seo";
{page.name}
</a></li>))}
</ol>
<a class="dashboard" href="/dashboard">
<a class="dashboard" href={getRelativeLocaleUrl(Astro.currentLocale!, "/dashboard")}>
<div class="icon bi-person-circle"></div>
</a>
<a data-swup-preload class="get_started" draggable="false" data-active={getStartedActive} href="https://run.webwriter.app">Get Started</a>
<a data-swup-preload class="get_started" draggable="false" data-active={getStartedActive} href="https://run.webwriter.app">{msg("Get Started")}</a>
</nav>
</header>
<main id="swup">
Expand All @@ -84,28 +105,31 @@ import { SEO } from "astro-seo";
<div class="logo">
<img id="icon-footer" src={iconUrl} alt="WebWriter Icon" width={40} height={40} />
<span class="logo-text">
<span>WebWriter</span>
<span>{msg("WebWriter")}</span>
<EmailAddress class="contact" client:only="lit" />
</span>
</div>
<div>© 2023 Frederic Salmen. All rights reserved.</div>
<a data-swup-preload class="legal_notice" draggable="false" href="/legal_notice">Legal Notice</a>
<div>{msg("© Frederic Salmen. All rights reserved.")}</div>
<a data-swup-preload class="legal_notice" draggable="false" href={getRelativeLocaleUrl(Astro.currentLocale!, "/legal_notice")}>{msg("Legal Notice")}</a>
</div>
<div class="sitemap-card">
<h2>Pages</h2>
<h2>{msg("Pages")}</h2>
<nav><ol>
{pages.map(page => (<li><a data-swup-preload draggable="false" href={page.url}>
{page.name}
</a></li>))}
</ol></nav>
</div>
<div class="sitemap-card">
<h2>Community</h2>
<h2>{msg("Community")}</h2>
<nav><ol>
<li><a href="https://github.com/webwriter-app">GitHub</a></li>
<li><a href="https://learntech.rwth-aachen.de">LearnTech RWTH Aachen</a></li>
<li><a href="https://explorabl.es">Explorables</a></li>
</ol></nav>
</div>
<div class="settings-card">
<LanguagePicker></LanguagePicker>
</div>
</footer>
</body>
Expand All @@ -124,7 +148,7 @@ import { SEO } from "astro-seo";
document.body.setAttribute("data-status", "signedin")
const dashHeader = document.querySelector("#dash h1")
const user = pocketbase.authStore.model as {email: string}
dashHeader && (dashHeader.textContent = `Welcome, ${user.email}`)
dashHeader && (dashHeader.textContent = `${user.email}`)
}
}
pocketbase.authStore.onChange((token, model) => updateUserUI())
Expand Down
17 changes: 17 additions & 0 deletions @webwriter/website/src/localize/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const languages = {
"en": {"_": "🇬🇧", "en": "English", "de": "Englisch"},
"de": {"_": "🇩🇪", "en": "German", "de": "Deutsch" }
}
export type Lang = keyof typeof languages

export function getLocalizedURL(url: URL | string, lang: string) {
const _url = new URL(url)
const pathParts = _url.pathname.split("/")
const pathWithoutLang = pathParts.slice(pathParts[1] in languages? 2: 1).join("/")
return new URL(lang === "en"
? `${_url.protocol}//${_url.host}/${pathWithoutLang}`:
`${_url.protocol}//${_url.host}/${lang}/${pathWithoutLang}`
)
}

export const msgResolver = (translations: Record<string, Record<string, string>>, lang: string) => (str: string) => (translations[str]?.[lang] ?? str)
23 changes: 18 additions & 5 deletions @webwriter/website/src/pages/about.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,30 @@
import Layout from '../layouts/Layout.astro';
import {Content as AboutContent} from '../content/about.md';
import LinkButton from '@components/LinkButton.astro';
import {msgResolver} from "../localize"
const lang = Astro.currentLocale!
const translations = {
"About": {"de": "Über uns"},
"WebWriter is a tool for creating explorables. Explorables mix text with multimedia and interactive content and can also be thought of as digital worksheets.": {"de": "WebWriter ist ein Werkzeug zum Erstellen von Explorables. Explorables mischen multimodale und interaktive Inhalte - man kann sie sich als interaktive Arbeitsblätter vorstellen."},
"Learn more...": {"de": "Erfahre mehr..."},
"Dissertation Project": {"de": "Dissertationsprojekt"},
"WebWriter is the product of a dissertation project at RWTH Aachen University's Learning Technologies Research Group.": {"de": "WebWriter ist das Produkt einer Dissertation im Lehr- und Forschungsgebiet Learning Technologies der RWTH Aachen."},
}
const msg = msgResolver(translations, lang)
---

<Layout title="About">
<div id="about-base">
<h1><span>About</span></h1>
<h1><span>{msg("About")}</span></h1>

<h2>Explorables</h2>
<p>WebWriter is a tool for creating explorables. Explorables mix text with multimedia and interactive content and can also be thought of as digital worksheets. <a class="learn-more" href="https://explorabl.es/">Learn more...</a></p>
<h2>{msg("Explorables")}</h2>
<p>{msg("WebWriter is a tool for creating explorables. Explorables mix text with multimedia and interactive content and can also be thought of as digital worksheets.")} <a class="learn-more" href="https://explorabl.es/">{msg("Learn more...")}</a></p>

<h2>Dissertation Project</h2>
<p>WebWriter is the product of a dissertation project at RWTH Aachen University's Learning Technologies Research Group. <a class="learn-more" href="https://learntech.rwth-aachen.de/cms/LearnTech/Forschung/Laufende-Dissertationen/~ybguz/Frederic-Salmen/?lidx=1">Learn more...</a></p>
<h2>{msg("Dissertation Project")}</h2>
<p>{msg("WebWriter is the product of a dissertation project at RWTH Aachen University's Learning Technologies Research Group.")} <a class="learn-more" href="https://learntech.rwth-aachen.de/cms/LearnTech/Forschung/Laufende-Dissertationen/~ybguz/Frederic-Salmen/?lidx=1">{msg("Learn more...")}</a></p>
</div>
</Layout>

Expand Down
36 changes: 26 additions & 10 deletions @webwriter/website/src/pages/dashboard.astro
Original file line number Diff line number Diff line change
@@ -1,30 +1,46 @@
---
import Layout from "../layouts/Layout.astro";
import {msgResolver} from "../localize"
const lang = Astro.currentLocale!
const translations = {
"Set your password": {"de": "Passwort setzen"},
"Password": {"de": "Passwort"},
"Confirm password": {"de": "Passwort bestätigen"},
"Welcome": {"de": "Willkommen"},
"Sign out": {"de": "Abmelden"},
"Sign in": {"de": "Anmelden"},
"Registration is not available as webwriter.app accounts are currently invitation only.": {"de": "Registrierung ist nicht verfügbar, da webwriter.app-Accounts aktuell nur per Einladung erhältlich sind."},
"Login": {"de": "Anmelden"}
}
const msg = msgResolver(translations, lang)
---

<Layout title="Dashboard">
<div id="dashboard-base" data-status="loading">
<div id="setpassword" class="auth-card">
<h1>Set your password</h1>
<h1>{msg("Set your password")}</h1>
<form id="setpassword-form" action="/" method="post">
<label for="password">Password</label>
<label for="password">{msg("Password")}</label>
<input type="password" name="password" id="password" required />
<button type="submit">Confirm password</button>
<button type="submit">{msg("Confirm password")}</button>
</form>
</div>
<div id="dash" class="auth-card">
<h1>Welcome</h1>
<button id="signout-button">Sign out</button>
<h1>{msg("Welcome")}</h1>
<button id="signout-button">{msg("Sign out")}</button>
</div>
<div id="signin" class="auth-card">
<h1>Sign in</h1>
<div class="banner">Registration is not available as webwriter.app accounts are currently invitation only.</div>
<h1>{msg("Sign in")}</h1>
<div class="banner">{msg("Registration is not available as webwriter.app accounts are currently invitation only.")}</div>
<form id="signin-form">
<label for="email">Email</label>
<label for="email">{msg("Email")}</label>
<input type="email" name="email" id="email" required />
<label for="password">Password</label>
<label for="password">{msg("Password")}</label>
<input type="password" name="password" id="password" required />
<button type="submit">Login</button>
<button type="submit">{msg("Login")}</button>
</form>
</div>
</div>
Expand Down
Loading

0 comments on commit 797dbea

Please sign in to comment.